diff --git a/projects/calculator/index.html b/projects/calculator/index.html
index ada16de..d6626a4 100644
--- a/projects/calculator/index.html
+++ b/projects/calculator/index.html
@@ -1,18 +1,48 @@
+
-
-
- Calculator | Vanilla Verse
-
+
+
+ Calculator
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/calculator/main.js b/projects/calculator/main.js
index 7c489e5..1347028 100644
--- a/projects/calculator/main.js
+++ b/projects/calculator/main.js
@@ -1,15 +1,224 @@
-/**
- * TODO: Calculator logic
- * - Render/display current input and result
- * - Support operations: +, -, *, /, %, ±, decimal
- * - Keyboard support (numbers, operators, Enter for =, Backspace)
- * - Prevent invalid inputs (multiple decimals, divide by zero)
- * Optional:
- * - Expression history
- * - Theme toggle
- */
-
-document.addEventListener('DOMContentLoaded', () => {
- console.log('Calculator app ready');
- // TODO: Build UI or select existing DOM nodes and attach event listeners
-});
+
+const currentOperandElement = document.getElementById('currentOperand');
+const previousOperandElement = document.getElementById('previousOperand');
+const buttons = document.querySelectorAll('button');
+
+let currentOperand = '0';
+let previousOperand = '';
+let operation = null;
+let shouldResetScreen = false;
+
+function init() {
+ buttons.forEach(button => {
+ button.addEventListener('click', () => {
+ button.classList.add('press-animation');
+ setTimeout(() => button.classList.remove('press-animation'), 200);
+
+ handleButtonClick(button);
+ });
+ });
+
+ document.addEventListener('keydown', handleKeyboardInput);
+
+ updateDisplay();
+}
+
+function handleButtonClick(button) {
+ if (button.dataset.number !== undefined) {
+ appendNumber(button.dataset.number);
+ } else if (button.dataset.action === 'operator') {
+ chooseOperation(button.dataset.operator);
+ } else if (button.dataset.action === 'decimal') {
+ appendDecimal();
+ } else if (button.dataset.action === 'equals') {
+ compute();
+ } else if (button.dataset.action === 'clear') {
+ clear();
+ } else if (button.dataset.action === 'backspace') {
+ backspace();
+ } else if (button.dataset.action === 'toggle-sign') {
+ toggleSign();
+ } else if (button.dataset.action === 'percentage') {
+ percentage();
+ }
+}
+
+function handleKeyboardInput(e) {
+ if (e.key === 'Enter' || e.key === 'Escape' || e.key === 'Backspace') {
+ e.preventDefault();
+ }
+
+ if (e.key >= '0' && e.key <= '9') {
+ appendNumber(e.key);
+ }
+ else if (e.key === '.') {
+ appendDecimal();
+ }
+ else if (e.key === '+' || e.key === '-' || e.key === '*' || e.key === '/') {
+ const operatorMap = {
+ '+': '+',
+ '-': '−',
+ '*': '×',
+ '/': '÷'
+ };
+ chooseOperation(operatorMap[e.key]);
+ }
+ else if (e.key === 'Enter' || e.key === '=') {
+ compute();
+ }
+ else if (e.key === 'Escape') {
+ clear();
+ }
+ else if (e.key === 'Backspace') {
+ backspace();
+ }
+}
+
+function appendNumber(number) {
+ if (shouldResetScreen) {
+ currentOperand = '';
+ shouldResetScreen = false;
+ }
+
+ if (currentOperand === '0') {
+ currentOperand = number;
+ } else if (currentOperand.length < 12) {
+ currentOperand += number;
+ }
+
+ updateDisplay();
+}
+
+function appendDecimal() {
+ if (shouldResetScreen) {
+ currentOperand = '0';
+ shouldResetScreen = false;
+ }
+
+ if (!currentOperand.includes('.')) {
+ currentOperand += '.';
+ }
+
+ updateDisplay();
+}
+
+function chooseOperation(op) {
+ if (currentOperand === '') return;
+
+ if (previousOperand !== '') {
+ compute();
+ }
+
+ operation = op;
+ previousOperand = currentOperand;
+ shouldResetScreen = true;
+ updateDisplay();
+}
+
+function compute() {
+ if (operation === null || previousOperand === '') return;
+
+ let computation;
+ const prev = parseFloat(previousOperand);
+ const current = parseFloat(currentOperand);
+
+ if (isNaN(prev) || isNaN(current)) {
+ clear();
+ return;
+ }
+
+ switch (operation) {
+ case '+': computation = prev + current; break;
+ case '−': computation = prev - current; break;
+ case '×': computation = prev * current; break;
+ case '÷':
+ if (current === 0) {
+ currentOperand = 'Error';
+ previousOperand = '';
+ operation = null;
+ shouldResetScreen = true;
+ updateDisplay();
+ return;
+ }
+ computation = prev / current;
+ break;
+ default: return;
+ }
+
+ currentOperand = roundResult(computation);
+ operation = null;
+ previousOperand = '';
+ shouldResetScreen = true;
+ updateDisplay();
+}
+
+function clear() {
+ currentOperand = '0';
+ previousOperand = '';
+ operation = null;
+ shouldResetScreen = false;
+ updateDisplay();
+}
+
+function backspace() {
+ if (currentOperand.length > 1) {
+ currentOperand = currentOperand.slice(0, -1);
+ } else {
+ currentOperand = '0';
+ }
+ updateDisplay();
+}
+
+function toggleSign() {
+ if (currentOperand !== '0') {
+ currentOperand = currentOperand.startsWith('-')
+ ? currentOperand.slice(1)
+ : '-' + currentOperand;
+ }
+ updateDisplay();
+}
+
+function percentage() {
+ currentOperand = (parseFloat(currentOperand) / 100).toString();
+ shouldResetScreen = true;
+ updateDisplay();
+}
+
+function roundResult(number) {
+ const strNumber = number.toExponential(12);
+ const roundedNumber = parseFloat(strNumber);
+
+ return Number.isInteger(roundedNumber)
+ ? roundedNumber.toString()
+ : roundedNumber.toString();
+}
+
+function formatNumber(number) {
+ if (number === 'Error') return number;
+
+ const stringNumber = number.toString();
+
+ if (stringNumber.length > 12) {
+ return parseFloat(number).toExponential(6);
+ }
+
+ return stringNumber;
+}
+
+function updateDisplay() {
+ currentOperandElement.textContent = formatNumber(currentOperand);
+
+ if (operation != null) {
+ previousOperandElement.textContent = `${previousOperand} ${operation}`;
+ } else {
+ previousOperandElement.textContent = '';
+ }
+
+ if (currentOperand === 'Error') {
+ currentOperandElement.classList.add('error');
+ } else {
+ currentOperandElement.classList.remove('error');
+ }
+}
+
+init();
diff --git a/projects/calculator/styles.css b/projects/calculator/styles.css
index 93a483b..8e71aa5 100644
--- a/projects/calculator/styles.css
+++ b/projects/calculator/styles.css
@@ -1,18 +1,112 @@
-/* TODO: Style the Calculator UI (grid of buttons, responsive layout, accessible focus states) */
-:root {
- --bg: #0f172a;
- --fg: #e2e8f0;
- --accent: #22d3ee;
- --panel: #111827;
-}
-* { box-sizing: border-box; }
-body {
- margin: 0;
- font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
- background: var(--bg);
- color: var(--fg);
- min-height: 100vh;
- display: grid;
- place-items: center;
-}
-#app { width: min(92vw, 420px); }
+* { margin: 0; padding: 0; box-sizing: border-box; }
+
+ body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background: #1a1a1a;
+ color: #f0f0f0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
+ padding: 20px;
+ }
+
+ /* ===== CALCULATOR CONTAINER ===== */
+ .calculator {
+ background: #2d2d2d;
+ border-radius: 20px;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
+ width: 100%;
+ max-width: 360px;
+ padding: 25px;
+ }
+
+ /* ===== DISPLAY ===== */
+ .display {
+ background: #1a1a1a;
+ border-radius: 10px;
+ padding: 20px;
+ margin-bottom: 20px;
+ text-align: right;
+ min-height: 80px;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ }
+
+ .previous-operand {
+ color: #888;
+ font-size: 1.2rem;
+ margin-bottom: 5px;
+ height: 1.5rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .current-operand {
+ color: #f0f0f0;
+ font-size: 2.5rem;
+ font-weight: 300;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ /* ===== BUTTON GRID ===== */
+ .buttons {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 12px;
+ }
+
+ /* ===== BUTTON STYLES ===== */
+ button {
+ background: #333;
+ border: none;
+ border-radius: 50%;
+ color: #f0f0f0;
+ cursor: pointer;
+ font-size: 1.5rem;
+ height: 70px;
+ width: 70px;
+ transition: all 0.2s ease;
+ outline: none;
+ }
+
+ button:focus {
+ box-shadow: 0 0 0 3px rgba(255, 165, 0, 0.5);
+ }
+
+ button:hover { background: #444; }
+ button:active { transform: scale(0.95); }
+
+ /* ===== SPECIAL BUTTONS ===== */
+ .operator { background: #ff9500; color: white; }
+ .operator:hover { background: #ffad33; }
+
+ .equals { background: #4caf50; color: white; }
+ .equals:hover { background: #66bb6a; }
+
+ .function { background: #a5a5a5; color: #333; }
+ .function:hover { background: #bfbfbf; }
+
+ /* ===== ANIMATIONS ===== */
+ @keyframes press {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(0.95); }
+ 100% { transform: scale(1); }
+ }
+
+ .press-animation { animation: press 0.2s ease; }
+
+ /* ===== RESPONSIVE ===== */
+ @media (max-width: 400px) {
+ .calculator { padding: 15px; }
+ button { height: 60px; width: 60px; font-size: 1.3rem; }
+ .current-operand { font-size: 2rem; }
+ .previous-operand { font-size: 1rem; }
+ }
+
+ /* ===== ERROR STATE ===== */
+ .error { color: #ff5252; }
\ No newline at end of file