Calculators are indispensable tools in our daily lives, simplifying complex mathematical tasks with ease. From basic arithmetic to advanced scientific computations, they serve a wide range of purposes.
In this article, we will explore ten diverse calculator examples that showcase their versatility and utility. Whether you're a student, professional, or hobbyist, these examples will highlight the various ways calculators can assist you.
CODE1
Here's the code:
CODETEXT1
CODE2
Here's the code:
CODETEXT2
CODE3
Here's the code:
CODETEXT3
CODE4
Here's the code:
CODETEXT4
CODE5
Here's the code:
CODETEXT5
Designers and developers, elevate your calculator projects with Subframe's drag-and-drop interface. Its intuitive, responsive canvas ensures pixel-perfect UI every time, making it a favorite among professionals.
Ready to create stunning designs effortlessly? Start for free today!
CODE6
Here's the code:
CODETEXT6
CODE7
Here's the code:
CODETEXT7
CODE8
Here's the code:
CODETEXT8
CODE9
Here's the code:
CODETEXT9
CODE10
Here's the code:
CODETEXT10
Unlock the power of Subframe to design stunning UIs, including calculators, with unmatched efficiency. Its drag-and-drop interface ensures pixel-perfect results instantly.
Ready to elevate your design game? Start for free and begin creating right away!
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Minimal Calculator</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; user-select: none; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: #f7f7f7; overflow: hidden; } .calculator { width: 320px; background-color: #ffffff; border-radius: 20px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); padding: 24px; transition: transform 0.3s ease, box-shadow 0.3s ease; overflow: hidden; position: relative; } .calculator:hover { transform: translateY(-5px); box-shadow: 0 15px 35px rgba(0, 0, 0, 0.12); } .calculator::before { content: ''; position: absolute; width: 100%; height: 5px; background: linear-gradient(90deg, #6c5ce7, #a29bfe, #74b9ff, #0984e3); top: 0; left: 0; border-radius: 5px 5px 0 0; } .display { background-color: #f8f9fa; border-radius: 12px; padding: 20px; margin-bottom: 24px; position: relative; overflow: hidden; min-height: 100px; box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.05); } .display-background { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0.03; background-image: radial-gradient(circle at 30% 30%, #000 1px, transparent 1px); background-size: 20px 20px; pointer-events: none; } .previous-operand { color: #6c757d; font-size: 16px; text-align: right; min-height: 24px; opacity: 0.7; transition: all 0.2s ease; } .current-operand { color: #212529; font-size: 36px; font-weight: 500; text-align: right; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; margin-top: 8px; } .current-operand::-webkit-scrollbar { display: none; } .buttons { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; } button { border: none; padding: 16px 0; border-radius: 12px; font-size: 18px; cursor: pointer; background-color: #ffffff; color: #212529; transition: all 0.15s ease; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); position: relative; overflow: hidden; } button::after { content: ''; position: absolute; top: 50%; left: 50%; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.04); border-radius: 50%; transform: translate(-50%, -50%) scale(0); transition: transform 0.5s ease-out; } button:active::after { transform: translate(-50%, -50%) scale(2); opacity: 0; } button:active { transform: scale(0.95); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } .operation { background-color: #f1f3f5; color: #495057; font-weight: 500; } .equals { background-color: #4263eb; color: white; font-weight: 500; } .clear, .delete { background-color: #ffe3e3; color: #e03131; font-weight: 500; } .mode-switch { display: flex; justify-content: flex-end; margin-bottom: 16px; } .mode-switch button { background: none; border: none; font-size: 14px; cursor: pointer; color: #6c757d; box-shadow: none; padding: 5px 10px; border-radius: 8px; } .mode-switch button:hover { background-color: #f1f3f5; } /* Tooltip */ .tooltip { position: fixed; background-color: #212529; color: white; padding: 8px 12px; border-radius: 6px; font-size: 14px; pointer-events: none; opacity: 0; transition: opacity 0.2s ease; z-index: 1000; white-space: nowrap; } .tooltip.show { opacity: 1; } /* Dark mode styles */ .dark-mode { background-color: #212529; } .dark-mode .calculator { background-color: #343a40; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); } .dark-mode .display { background-color: #212529; } .dark-mode .display-background { opacity: 0.05; background-image: radial-gradient(circle at 30% 30%, #fff 1px, transparent 1px); } .dark-mode .previous-operand { color: #adb5bd; } .dark-mode .current-operand { color: #f8f9fa; } .dark-mode button { background-color: #495057; color: #f8f9fa; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); } .dark-mode .operation { background-color: #343a40; color: #ced4da; } .dark-mode .equals { background-color: #5c7cfa; } .dark-mode .clear, .dark-mode .delete { background-color: #c92a2a; color: #ffe3e3; } .dark-mode .mode-switch button { color: #adb5bd; } .dark-mode .mode-switch button:hover { background-color: #495057; } .history-panel { position: absolute; top: 0; right: -220px; width: 200px; height: 100%; background-color: #f8f9fa; padding: 20px; transition: transform 0.3s ease; border-radius: 0 20px 20px 0; box-shadow: -5px 0 15px rgba(0, 0, 0, 0.05); overflow-y: auto; z-index: 10; } .history-panel.open { transform: translateX(-220px); } .dark-mode .history-panel { background-color: #343a40; box-shadow: -5px 0 15px rgba(0, 0, 0, 0.2); } .history-title { font-size: 16px; font-weight: 500; margin-bottom: 15px; color: #212529; } .dark-mode .history-title { color: #f8f9fa; } .history-list { list-style-type: none; } .history-item { padding: 8px 0; border-bottom: 1px solid #dee2e6; font-size: 14px; color: #495057; cursor: pointer; transition: background-color 0.2s ease; } .history-item:hover { background-color: #e9ecef; } .dark-mode .history-item { border-bottom: 1px solid #495057; color: #ced4da; } .dark-mode .history-item:hover { background-color: #495057; } .history-empty { color: #adb5bd; font-style: italic; text-align: center; margin-top: 20px; } .history-toggle { position: absolute; top: 15px; right: 15px; background: none; border: none; color: #6c757d; cursor: pointer; font-size: 18px; padding: 5px; border-radius: 50%; display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; z-index: 11; box-shadow: none; transition: background-color 0.2s ease; } .history-toggle:hover { background-color: rgba(0, 0, 0, 0.05); } .dark-mode .history-toggle { color: #adb5bd; } .dark-mode .history-toggle:hover { background-color: rgba(255, 255, 255, 0.1); } @media (max-width: 360px) { .calculator { width: 290px; padding: 16px; } .display { padding: 15px; margin-bottom: 16px; min-height: 80px; } .current-operand { font-size: 30px; } .buttons { gap: 8px; } button { padding: 12px 0; font-size: 16px; } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } .special-animation { animation: pulse 0.6s ease; } </style> </head> <body> <div class="tooltip"></div> <div class="calculator"> <button class="history-toggle" id="historyToggle"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z"/> <path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z"/> </svg> </button> <div class="mode-switch"> <button id="modeToggle"> <svg id="lightIcon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/> </svg> <svg id="darkIcon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="display: none;"> <path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/> </svg> </button> </div> <div class="display"> <div class="display-background"></div> <div class="previous-operand" id="previousOperand"></div> <div class="current-operand" id="currentOperand">0</div> </div> <div class="buttons"> <button class="clear" data-tooltip="Clear all">AC</button> <button class="delete" data-tooltip="Delete last digit">DEL</button> <button class="operation" data-tooltip="Percentage">%</button> <button class="operation" data-tooltip="Division">÷</button> <button class="number" data-tooltip="Number 7">7</button> <button class="number" data-tooltip="Number 8">8</button> <button class="number" data-tooltip="Number 9">9</button> <button class="operation" data-tooltip="Multiplication">×</button> <button class="number" data-tooltip="Number 4">4</button> <button class="number" data-tooltip="Number 5">5</button> <button class="number" data-tooltip="Number 6">6</button> <button class="operation" data-tooltip="Subtraction">−</button> <button class="number" data-tooltip="Number 1">1</button> <button class="number" data-tooltip="Number 2">2</button> <button class="number" data-tooltip="Number 3">3</button> <button class="operation" data-tooltip="Addition">+</button> <button class="operation" data-tooltip="Plus/Minus">±</button> <button class="number" data-tooltip="Number 0">0</button> <button class="number" data-tooltip="Decimal point">.</button> <button class="equals" data-tooltip="Calculate result">=</button> </div> <div class="history-panel"> <h3 class="history-title">Calculation History</h3> <ul class="history-list" id="historyList"> <li class="history-empty">No calculations yet</li> </ul> </div> </div> <script> class Calculator { constructor(previousOperandElement, currentOperandElement) { this.previousOperandElement = previousOperandElement; this.currentOperandElement = currentOperandElement; this.history = []; this.clear(); } clear() { this.currentOperand = '0'; this.previousOperand = ''; this.operation = undefined; this.updateDisplay(); } delete() { if (this.currentOperand === '0') return; if (this.currentOperand.length === 1) { this.currentOperand = '0'; } else { this.currentOperand = this.currentOperand.slice(0, -1); } this.updateDisplay(); } appendNumber(number) { if (number === '.' && this.currentOperand.includes('.')) return; if (this.currentOperand === '0' && number !== '.') { this.currentOperand = number; } else { this.currentOperand += number; } this.updateDisplay(); } chooseOperation(operation) { if (this.currentOperand === '') return; if (this.previousOperand !== '') { this.compute(); } this.operation = operation; this.previousOperand = this.currentOperand; this.currentOperand = ''; this.updateDisplay(); } compute() { let computation; const prev = parseFloat(this.previousOperand); const current = parseFloat(this.currentOperand); if (isNaN(prev) || isNaN(current)) return; switch (this.operation) { case '+': computation = prev + current; break; case '−': computation = prev - current; break; case '×': computation = prev * current; break; case '÷': computation = prev / current; break; case '%': computation = prev * (current / 100); break; default: return; } // Add to history const historyEntry = `${this.getDisplayNumber(prev)} ${this.operation} ${this.getDisplayNumber(current)} = ${this.getDisplayNumber(computation)}`; this.addToHistory(historyEntry); this.currentOperand = computation.toString(); this.operation = undefined; this.previousOperand = ''; // Add animation class to current operand this.currentOperandElement.classList.add('special-animation'); setTimeout(() => { this.currentOperandElement.classList.remove('special-animation'); }, 600); } addToHistory(entry) { this.history.unshift(entry); // Add to beginning of array if (this.history.length > 10) { this.history.pop(); // Remove oldest entry if more than 10 } this.updateHistoryDisplay(); } updateHistoryDisplay() { const historyList = document.getElementById('historyList'); historyList.innerHTML = ''; if (this.history.length === 0) { const emptyItem = document.createElement('li'); emptyItem.className = 'history-empty'; emptyItem.textContent = 'No calculations yet'; historyList.appendChild(emptyItem); return; } this.history.forEach(item => { const listItem = document.createElement('li'); listItem.className = 'history-item'; listItem.textContent = item; listItem.addEventListener('click', () => { const result = item.split(' = ')[1]; this.currentOperand = result; this.previousOperand = ''; this.operation = undefined; this.updateDisplay(); }); historyList.appendChild(listItem); }); } toggleSign() { if (this.currentOperand === '0') return; this.currentOperand = (parseFloat(this.currentOperand) * -1).toString(); this.updateDisplay(); } getDisplayNumber(number) { const stringNumber = number.toString(); const integerDigits = parseFloat(stringNumber.split('.')[0]); const decimalDigits = stringNumber.split('.')[1]; let integerDisplay; if (isNaN(integerDigits)) { integerDisplay = ''; } else { integerDisplay = integerDigits.toLocaleString('en', { maximumFractionDigits: 0 }); } if (decimalDigits != null) { return `${integerDisplay}.${decimalDigits}`; } else { return integerDisplay; } } updateDisplay() { this.currentOperandElement.textContent = this.getDisplayNumber(this.currentOperand); if (this.operation != null) { this.previousOperandElement.textContent = `${this.getDisplayNumber(this.previousOperand)} ${this.operation}`; } else { this.previousOperandElement.textContent = ''; } } } document.addEventListener('DOMContentLoaded', function() { const previousOperandElement = document.getElementById('previousOperand'); const currentOperandElement = document.getElementById('currentOperand'); const numberButtons = document.querySelectorAll('.number'); const operationButtons = document.querySelectorAll('.operation'); const equalsButton = document.querySelector('.equals'); const deleteButton = document.querySelector('.delete'); const clearButton = document.querySelector('.clear'); const modeToggle = document.getElementById('modeToggle'); const lightIcon = document.getElementById('lightIcon'); const darkIcon = document.getElementById('darkIcon'); const historyToggle = document.getElementById('historyToggle'); const historyPanel = document.querySelector('.history-panel'); const tooltip = document.querySelector('.tooltip'); const calculator = new Calculator(previousOperandElement, currentOperandElement); // Number buttons numberButtons.forEach(button => { button.addEventListener('click', () => { calculator.appendNumber(button.innerText); }); }); // Operation buttons operationButtons.forEach(button => { button.addEventListener('click', () => { if (button.innerText === '±') { calculator.toggleSign(); } else if (button.innerText === '%') { calculator.chooseOperation('%'); } else { calculator.chooseOperation(button.innerText); } }); }); // Equals button equalsButton.addEventListener('click', () => { calculator.compute(); }); // Clear button clearButton.addEventListener('click', () => { calculator.clear(); }); // Delete button deleteButton.addEventListener('click', () => { calculator.delete(); }); // Mode toggle modeToggle.addEventListener('click', () => { document.body.classList.toggle('dark-mode'); if (document.body.classList.contains('dark-mode')) { lightIcon.style.display = 'none'; darkIcon.style.display = 'block'; } else { lightIcon.style.display = 'block'; darkIcon.style.display = 'none'; } }); // History toggle historyToggle.addEventListener('click', () => { historyPanel.classList.toggle('open'); }); // Keyboard support document.addEventListener('keydown', function(event) { if (event.key >= '0' && event.key <= '9' || event.key === '.') { calculator.appendNumber(event.key); } else if (event.key === '+') { calculator.chooseOperation('+'); } else if (event.key === '-') { calculator.chooseOperation('−'); } else if (event.key === '*') { calculator.chooseOperation('×'); } else if (event.key === '/') { event.preventDefault(); // Prevent browser's find functionality calculator.chooseOperation('÷'); } else if (event.key === '%') { calculator.chooseOperation('%'); } else if (event.key === 'Enter' || event.key === '=') { calculator.compute(); } else if (event.key === 'Backspace') { calculator.delete(); } else if (event.key === 'Escape') { calculator.clear(); } }); // Tooltips const buttons = document.querySelectorAll('button[data-tooltip]'); buttons.forEach(button => { button.addEventListener('mouseenter', (e) => { const tooltipText = e.target.getAttribute('data-tooltip'); tooltip.textContent = tooltipText; tooltip.classList.add('show'); const rect = e.target.getBoundingClientRect(); tooltip.style.top = `${rect.top - 40}px`; tooltip.style.left = `${rect.left + rect.width / 2 - tooltip.offsetWidth / 2}px`; }); button.addEventListener('mouseleave', () => { tooltip.classList.remove('show'); }); }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>FinCalc Pro</title> <style> :root { --primary: #0c2d48; --primary-light: #145480; --secondary: #2e8bc0; --accent: #b1d4e0; --gray-dark: #2c3e50; --gray-light: #ecf0f1; --text-primary: #333; --text-secondary: #7f8c8d; --success: #2ecc71; --warning: #f39c12; --danger: #e74c3c; --white: #ffffff; --shadow: 0 4px 12px rgba(0, 0, 0, 0.1); --shadow-hover: 0 8px 24px rgba(0, 0, 0, 0.15); --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', sans-serif; } body { background-color: var(--gray-light); color: var(--text-primary); height: 700px; width: 700px; overflow: hidden; display: flex; flex-direction: column; } .container { width: 100%; height: 100%; padding: 20px; display: flex; flex-direction: column; overflow: hidden; } .calculator-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .logo { display: flex; align-items: center; gap: 8px; } .logo h1 { font-size: 24px; font-weight: 700; color: var(--primary); margin: 0; } .logo-icon { width: 32px; height: 32px; background-color: var(--secondary); border-radius: 6px; display: flex; align-items: center; justify-content: center; color: var(--white); font-weight: bold; } .calculator-tabs { display: flex; border-bottom: 1px solid var(--accent); margin-bottom: 20px; } .tab { padding: 12px 20px; font-size: 14px; font-weight: 600; cursor: pointer; transition: var(--transition); position: relative; color: var(--text-secondary); } .tab.active { color: var(--primary); } .tab.active::after { content: ''; position: absolute; bottom: -1px; left: 0; width: 100%; height: 3px; background-color: var(--secondary); border-radius: 2px 2px 0 0; } .tab:hover:not(.active) { color: var(--primary-light); } .calculator-content { display: flex; flex: 1; overflow: hidden; background-color: var(--white); border-radius: 12px; box-shadow: var(--shadow); } .calculator-form { flex: 1; padding: 20px; overflow-y: auto; border-right: 1px solid var(--accent); } .calculator-results { width: 220px; padding: 20px; background-color: var(--primary); color: var(--white); border-radius: 0 12px 12px 0; position: relative; overflow: hidden; } .field-group { margin-bottom: 20px; } .field-group h3 { font-size: 14px; font-weight: 600; margin-bottom: 12px; color: var(--primary); } .field { margin-bottom: 16px; } .field label { display: block; font-size: 13px; margin-bottom: 6px; color: var(--text-secondary); font-weight: 500; display: flex; align-items: center; } .tooltip { position: relative; display: inline-block; margin-left: 5px; cursor: help; } .tooltip-icon { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; background-color: var(--accent); border-radius: 50%; color: var(--primary); font-size: 10px; font-weight: bold; } .tooltip-content { position: absolute; top: 100%; left: 50%; transform: translateX(-50%); background-color: var(--gray-dark); color: var(--white); padding: 8px 12px; border-radius: 4px; font-size: 12px; width: 200px; z-index: 10; opacity: 0; visibility: hidden; transition: var(--transition); pointer-events: none; box-shadow: var(--shadow); } .tooltip:hover .tooltip-content { opacity: 1; visibility: visible; top: calc(100% + 5px); } .input-wrapper { position: relative; } input, select { width: 100%; padding: 10px 12px; border: 1px solid var(--accent); border-radius: 6px; font-size: 14px; transition: var(--transition); background: var(--white); } input:focus, select:focus { outline: none; border-color: var(--secondary); box-shadow: 0 0 0 3px rgba(46, 139, 192, 0.2); } .currency-input { padding-left: 24px; } .currency-symbol { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--text-secondary); font-size: 14px; } .percentage-input { padding-right: 24px; } .percentage-symbol { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); color: var(--text-secondary); font-size: 14px; } .calculate-btn { display: block; width: 100%; padding: 12px; background-color: var(--secondary); color: var(--white); border: none; border-radius: 6px; font-size: 14px; font-weight: 600; cursor: pointer; transition: var(--transition); margin-top: 20px; } .calculate-btn:hover { background-color: var(--primary-light); box-shadow: var(--shadow-hover); transform: translateY(-2px); } .results-header { margin-bottom: 20px; } .results-header h3 { font-size: 16px; font-weight: 600; } .results-header p { font-size: 12px; opacity: 0.8; margin-top: 4px; } .result-item { margin-bottom: 16px; position: relative; z-index: 1; } .result-label { font-size: 12px; opacity: 0.8; margin-bottom: 4px; } .result-value { font-size: 18px; font-weight: 600; } .bubble { position: absolute; width: 150px; height: 150px; border-radius: 50%; background-color: rgba(255, 255, 255, 0.05); z-index: 0; } .bubble-1 { top: -50px; right: -50px; } .bubble-2 { bottom: -30px; left: -60px; width: 120px; height: 120px; } .field-row { display: flex; gap: 12px; } .field-row .field { flex: 1; } .amortization-table { margin-top: 20px; width: 100%; border-collapse: collapse; border-radius: 6px; overflow: hidden; font-size: 12px; } .amortization-table th { background-color: var(--primary); color: var(--white); text-align: left; padding: 10px; } .amortization-table td { padding: 8px 10px; border-bottom: 1px solid var(--accent); } .amortization-table tbody tr:nth-child(even) { background-color: var(--gray-light); } .amortization-container { display: none; margin-top: 20px; overflow: auto; max-height: 200px; } .show-amortization { display: inline-block; margin-top: 10px; padding: 8px 12px; background-color: var(--primary-light); color: var(--white); border-radius: 4px; font-size: 12px; cursor: pointer; transition: var(--transition); } .show-amortization:hover { background-color: var(--primary); } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .field, .result-item { animation: fadeIn 0.3s ease-out forwards; } .field:nth-child(2) { animation-delay: 0.05s; } .field:nth-child(3) { animation-delay: 0.1s; } .result-item:nth-child(2) { animation-delay: 0.05s; } .result-item:nth-child(3) { animation-delay: 0.1s; } .result-item:nth-child(4) { animation-delay: 0.15s; } /* Responsive styles */ @media (max-width: 600px) { .calculator-content { flex-direction: column; } .calculator-results { width: 100%; border-radius: 0 0 12px 12px; } .calculator-form { border-right: none; border-bottom: 1px solid var(--accent); } .tab { padding: 10px 15px; font-size: 13px; } .field-row { flex-direction: column; gap: 0; } } /* Tab content styling */ .tab-content { display: none; } .tab-content.active { display: block; } /* Animated calculation indicator */ .calculation-indicator { position: absolute; bottom: 10px; right: 10px; width: 24px; height: 24px; display: none; } .calculation-indicator.active { display: block; } .calculation-indicator span { position: absolute; width: 10px; height: 10px; background-color: var(--accent); border-radius: 50%; animation: pulse 1.5s infinite; } .calculation-indicator span:nth-child(1) { left: 0; animation-delay: 0s; } .calculation-indicator span:nth-child(2) { left: 7px; animation-delay: 0.5s; } .calculation-indicator span:nth-child(3) { left: 14px; animation-delay: 1s; } @keyframes pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.5); opacity: 0.5; } 100% { transform: scale(1); opacity: 1; } } </style> </head> <body> <div class="container"> <div class="calculator-header"> <div class="logo"> <div class="logo-icon">Ƒ</div> <h1>FinCalc Pro</h1> </div> </div> <div class="calculator-tabs"> <div class="tab active" data-tab="mortgage">Mortgage</div> <div class="tab" data-tab="investment">Investment</div> <div class="tab" data-tab="loan">Loan</div> <div class="tab" data-tab="retirement">Retirement</div> </div> <div class="calculator-content"> <div class="calculator-form"> <!-- Mortgage Calculator Tab --> <div id="mortgage" class="tab-content active"> <div class="field-group"> <h3>Mortgage Details</h3> <div class="field"> <label> Home Price <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">The total purchase price of the property including land and structure.</div> </div> </label> <div class="input-wrapper"> <span class="currency-symbol">$</span> <input type="number" id="homePrice" class="currency-input" placeholder="300,000" value="300000"> </div> </div> <div class="field"> <label> Down Payment <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">The initial upfront payment that reduces the amount borrowed.</div> </div> </label> <div class="field-row"> <div class="field"> <div class="input-wrapper"> <span class="currency-symbol">$</span> <input type="number" id="downPaymentAmount" class="currency-input" placeholder="60,000" value="60000"> </div> </div> <div class="field"> <div class="input-wrapper"> <input type="number" id="downPaymentPercent" class="percentage-input" placeholder="20" value="20"> <span class="percentage-symbol">%</span> </div> </div> </div> </div> <div class="field"> <label> Loan Term <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">The length of time over which you'll repay the mortgage.</div> </div> </label> <select id="loanTerm"> <option value="30">30 years</option> <option value="25">25 years</option> <option value="20">20 years</option> <option value="15">15 years</option> <option value="10">10 years</option> </select> </div> <div class="field"> <label> Interest Rate <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">Annual interest rate for your mortgage. The APR includes the interest rate plus other loan costs.</div> </div> </label> <div class="input-wrapper"> <input type="number" id="interestRate" class="percentage-input" placeholder="5.25" value="5.25" step="0.01"> <span class="percentage-symbol">%</span> </div> </div> <div class="field"> <label> Property Tax (Annual) <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">Annual property taxes assessed by local government, typically 0.5% to 2.5% of the property value.</div> </div> </label> <div class="input-wrapper"> <span class="currency-symbol">$</span> <input type="number" id="propertyTax" class="currency-input" placeholder="3,600" value="3600"> </div> </div> <div class="field"> <label> Home Insurance (Annual) <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">Annual premium for homeowner's insurance to protect your property.</div> </div> </label> <div class="input-wrapper"> <span class="currency-symbol">$</span> <input type="number" id="homeInsurance" class="currency-input" placeholder="1,200" value="1200"> </div> </div> <div class="field"> <label> PMI (If down payment < 20%) <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">Private Mortgage Insurance, required for down payments less than 20%. Typically 0.5% to 1% of loan amount annually.</div> </div> </label> <div class="input-wrapper"> <input type="number" id="pmiRate" class="percentage-input" placeholder="0.5" value="0.5" step="0.01"> <span class="percentage-symbol">%</span> </div> </div> </div> <button id="calculateMortgage" class="calculate-btn">Calculate Payment</button> <div class="amortization-container" id="amortizationContainer"> <table class="amortization-table" id="amortizationTable"> <thead> <tr> <th>Year</th> <th>Principal Paid</th> <th>Interest Paid</th> <th>Remaining Balance</th> </tr> </thead> <tbody id="amortizationBody"> </tbody> </table> </div> </div> <!-- Investment Calculator Tab --> <div id="investment" class="tab-content"> <div class="field-group"> <h3>Investment Calculator</h3> <div class="field"> <label> Initial Investment <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">The initial amount you're starting with.</div> </div> </label> <div class="input-wrapper"> <span class="currency-symbol">$</span> <input type="number" id="initialInvestment" class="currency-input" placeholder="10,000" value="10000"> </div> </div> <div class="field"> <label> Monthly Contribution <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">How much you plan to add each month.</div> </div> </label> <div class="input-wrapper"> <span class="currency-symbol">$</span> <input type="number" id="monthlyContribution" class="currency-input" placeholder="500" value="500"> </div> </div> <div class="field"> <label> Annual Return Rate <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">Expected annual rate of return on your investment. The S&P 500 has historically returned about 10% annually before inflation.</div> </div> </label> <div class="input-wrapper"> <input type="number" id="annualReturn" class="percentage-input" placeholder="8" value="8" step="0.01"> <span class="percentage-symbol">%</span> </div> </div> <div class="field"> <label> Time Period <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">How long you plan to keep this investment.</div> </div> </label> <div class="field-row"> <div class="field"> <input type="number" id="investmentYears" placeholder="20" value="20"> </div> <div class="field"> <select id="investmentPeriodType"> <option value="years">Years</option> <option value="months">Months</option> </select> </div> </div> </div> <div class="field"> <label> Inflation Rate (Optional) <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">Average annual inflation rate. Historically around 2-3% in the US.</div> </div> </label> <div class="input-wrapper"> <input type="number" id="inflationRate" class="percentage-input" placeholder="2.5" value="2.5" step="0.1"> <span class="percentage-symbol">%</span> </div> </div> </div> <button id="calculateInvestment" class="calculate-btn">Calculate Returns</button> </div> <!-- Loan Calculator Tab --> <div id="loan" class="tab-content"> <div class="field-group"> <h3>Loan Calculator</h3> <div class="field"> <label> Loan Amount <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">The total amount you want to borrow.</div> </div> </label> <div class="input-wrapper"> <span class="currency-symbol">$</span> <input type="number" id="loanAmount" class="currency-input" placeholder="25,000" value="25000"> </div> </div> <div class="field"> <label> Loan Term <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">The period over which you'll repay the loan.</div> </div> </label> <div class="field-row"> <div class="field"> <input type="number" id="loanYears" placeholder="5" value="5"> </div> <div class="field"> <select id="loanPeriodType"> <option value="years">Years</option> <option value="months">Months</option> </select> </div> </div> </div> <div class="field"> <label> Interest Rate <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">Annual interest rate for your loan. For personal loans, rates typically range from 6% to 36% depending on credit score.</div> </div> </label> <div class="input-wrapper"> <input type="number" id="loanInterestRate" class="percentage-input" placeholder="7.5" value="7.5" step="0.01"> <span class="percentage-symbol">%</span> </div> </div> <div class="field"> <label> Payment Frequency <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">How often you'll make payments on this loan.</div> </div> </label> <select id="paymentFrequency"> <option value="monthly">Monthly</option> <option value="biweekly">Bi-weekly</option> <option value="weekly">Weekly</option> </select> </div> </div> <button id="calculateLoan" class="calculate-btn">Calculate Payment</button> </div> <!-- Retirement Calculator Tab --> <div id="retirement" class="tab-content"> <div class="field-group"> <h3>Retirement Calculator</h3> <div class="field"> <label> Current Age <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">Your current age.</div> </div> </label> <input type="number" id="currentAge" placeholder="35" value="35"> </div> <div class="field"> <label> Retirement Age <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">The age at which you plan to retire.</div> </div> </label> <input type="number" id="retirementAge" placeholder="65" value="65"> </div> <div class="field"> <label> Current Savings <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">How much you've already saved for retirement.</div> </div> </label> <div class="input-wrapper"> <span class="currency-symbol">$</span> <input type="number" id="currentSavings" class="currency-input" placeholder="50,000" value="50000"> </div> </div> <div class="field"> <label> Monthly Contribution <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">How much you're adding to your retirement each month.</div> </div> </label> <div class="input-wrapper"> <span class="currency-symbol">$</span> <input type="number" id="retirementContribution" class="currency-input" placeholder="500" value="500"> </div> </div> <div class="field"> <label> Expected Annual Return <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">Expected annual rate of return on your retirement investments before retirement. Conservative estimates range from 5-7%.</div> </div> </label> <div class="input-wrapper"> <input type="number" id="retirementReturn" class="percentage-input" placeholder="7" value="7" step="0.1"> <span class="percentage-symbol">%</span> </div> </div> <div class="field"> <label> Income Needed in Retirement <div class="tooltip"> <div class="tooltip-icon">?</div> <div class="tooltip-content">Percentage of your current income you'll need in retirement. Financial advisors often suggest 70-80%.</div> </div> </label> <div class="input-wrapper"> <input type="number" id="incomeNeeded" class="percentage-input" placeholder="80" value="80" step="1"> <span class="percentage-symbol">%</span> </div> </div> </div> <button id="calculateRetirement" class="calculate-btn">Calculate Retirement Plan</button> </div> </div> <div class="calculator-results"> <div class="bubble bubble-1"></div> <div class="bubble bubble-2"></div> <div class="results-header"> <h3>Results</h3> <p>Based on your inputs</p> </div> <div id="mortgageResults" class="results-content active"> <div class="result-item"> <div class="result-label">Monthly Principal & Interest</div> <div class="result-value" id="monthlyPayment">$1,324.99</div> </div> <div class="result-item"> <div class="result-label">Monthly Property Tax</div> <div class="result-value" id="monthlyTax">$300.00</div> </div> <div class="result-item"> <div class="result-label">Monthly Insurance</div> <div class="result-value" id="monthlyInsurance">$100.00</div> </div> <div class="result-item"> <div class="result-label">Monthly PMI</div> <div class="result-value" id="monthlyPMI">$0.00</div> </div> <div class="result-item"> <div class="result-label">Total Monthly Payment</div> <div class="result-value" id="totalMonthlyPayment">$1,724.99</div> </div> <div class="result-item"> <div class="result-label">Total Interest Paid</div> <div class="result-value" id="totalInterest">$237,196.40</div> </div> <div class="show-amortization" id="showAmortization">View Amortization Schedule</div> </div> <div id="investmentResults" class="results-content"> <div class="result-item"> <div class="result-label">Future Investment Value</div> <div class="result-value" id="futureValue">$0.00</div> </div> <div class="result-item"> <div class="result-label">Total Contributions</div> <div class="result-value" id="totalContributions">$0.00</div> </div> <div class="result-item"> <div class="result-label">Total Interest Earned</div> <div class="result-value" id="totalInterestEarned">$0.00</div> </div> <div class="result-item"> <div class="result-label">Inflation-Adjusted Value</div> <div class="result-value" id="inflationAdjustedValue">$0.00</div> </div> </div> <div id="loanResults" class="results-content"> <div class="result-item"> <div class="result-label">Regular Payment</div> <div class="result-value" id="loanPayment">$0.00</div> </div> <div class="result-item"> <div class="result-label">Total Payments</div> <div class="result-value" id="totalPayments">$0.00</div> </div> <div class="result-item"> <div class="result-label">Total Interest</div> <div class="result-value" id="loanTotalInterest">$0.00</div> </div> </div> <div id="retirementResults" class="results-content"> <div class="result-item"> <div class="result-label">Retirement Savings</div> <div class="result-value" id="retirementSavings">$0.00</div> </div> <div class="result-item"> <div class="result-label">Est. Monthly Income</div> <div class="result-value" id="retirementMonthly">$0.00</div> </div> <div class="result-item"> <div class="result-label">Years of Coverage</div> <div class="result-value" id="retirementYears">0 years</div> </div> <div class="result-item"> <div class="result-label">Savings Gap</div> <div class="result-value" id="savingsGap">$0.00</div> </div> </div> <div class="calculation-indicator" id="calculationIndicator"> <span></span> <span></span> <span></span> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Tab Switching const tabs = document.querySelectorAll('.tab'); const tabContents = document.querySelectorAll('.tab-content'); const resultsContents = document.querySelectorAll('.results-content'); tabs.forEach(tab => { tab.addEventListener('click', () => { const tabId = tab.getAttribute('data-tab'); // Update active tab tabs.forEach(t => t.classList.remove('active')); tab.classList.add('active'); // Update active content tabContents.forEach(content => { content.classList.remove('active'); if (content.id === tabId) { content.classList.add('active'); } }); // Update active results resultsContents.forEach(content => { content.classList.remove('active'); if (content.id === `${tabId}Results`) { content.classList.add('active'); } }); }); }); // Format Currency Function function formatCurrency(number) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(number); } // Show Calculation Animation function showCalculating() { document.getElementById('calculationIndicator').classList.add('active'); setTimeout(() => { document.getElementById('calculationIndicator').classList.remove('active'); }, 800); } // Mortgage Calculator const downPaymentAmount = document.getElementById('downPaymentAmount'); const downPaymentPercent = document.getElementById('downPaymentPercent'); const homePrice = document.getElementById('homePrice'); // Sync down payment amount and percentage downPaymentAmount.addEventListener('input', function() { if (homePrice.value > 0) { downPaymentPercent.value = ((this.value / homePrice.value) * 100).toFixed(2); } }); downPaymentPercent.addEventListener('input', function() { if (homePrice.value > 0) { downPaymentAmount.value = (homePrice.value * (this.value / 100)).toFixed(0); } }); homePrice.addEventListener('input', function() { if (downPaymentPercent.value > 0) { downPaymentAmount.value = (this.value * (downPaymentPercent.value / 100)).toFixed(0); } }); // Initial Mortgage Calculation calculateMortgage(); // Calculate Mortgage document.getElementById('calculateMortgage').addEventListener('click', function() { showCalculating(); setTimeout(calculateMortgage, 300); }); function calculateMortgage() { const price = parseFloat(homePrice.value) || 0; const downPayment = parseFloat(downPaymentAmount.value) || 0;
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Scientific Calculator</title> <style> :root { --bg-color: #1f2937; --display-bg: #111827; --primary-button: #374151; --primary-button-hover: #4b5563; --accent-button: #0369a1; --accent-button-hover: #0284c7; --function-button: #4b5563; --function-button-hover: #6b7280; --display-text: #f9fafb; --button-text: #e5e7eb; --border-radius: 8px; --button-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); --transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } body { display: flex; justify-content: center; align-items: center; background-color: var(--bg-color); height: 100vh; width: 100%; overflow: hidden; color: var(--button-text); } .calculator { width: 100%; max-width: 380px; background-color: var(--bg-color); border-radius: var(--border-radius); box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); overflow: hidden; padding: 1.5rem; position: relative; } .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; } .header h1 { font-size: 1.25rem; font-weight: 600; color: var(--display-text); } .mode-toggle { border: none; background: var(--function-button); color: var(--button-text); padding: 0.35rem 0.75rem; border-radius: var(--border-radius); font-size: 0.75rem; font-weight: 500; cursor: pointer; transition: var(--transition); } .mode-toggle:hover { background: var(--function-button-hover); } .display { background-color: var(--display-bg); padding: 1rem; border-radius: var(--border-radius); margin-bottom: 1rem; position: relative; height: 5.5rem; overflow: hidden; transition: var(--transition); } .history { font-size: 0.9rem; color: #9ca3af; margin-bottom: 0.5rem; min-height: 1.25rem; overflow-x: auto; white-space: nowrap; scrollbar-width: none; } .history::-webkit-scrollbar { display: none; } .current { font-size: 2rem; color: var(--display-text); text-align: right; font-weight: 500; overflow-x: auto; white-space: nowrap; scrollbar-width: none; } .current::-webkit-scrollbar { display: none; } .buttons { display: grid; grid-template-columns: repeat(5, 1fr); gap: 0.5rem; } .button { background-color: var(--primary-button); border: none; color: var(--button-text); font-size: 1rem; font-weight: 500; padding: 0.75rem 0; border-radius: var(--border-radius); cursor: pointer; transition: var(--transition); box-shadow: var(--button-shadow); position: relative; overflow: hidden; } .button:hover { background-color: var(--primary-button-hover); transform: translateY(-2px); } .button:active { transform: translateY(0px); } .button::after { content: ''; display: block; position: absolute; width: 100%; height: 100%; top: 0; left: 0; pointer-events: none; background-image: radial-gradient(circle, #fff 10%, transparent 10.01%); background-repeat: no-repeat; background-position: 50%; transform: scale(10, 10); opacity: 0; transition: transform 0.5s, opacity 1s; } .button:active::after { transform: scale(0, 0); opacity: 0.3; transition: 0s; } .function { background-color: var(--function-button); } .function:hover { background-color: var(--function-button-hover); } .operator { background-color: var(--accent-button); } .operator:hover { background-color: var(--accent-button-hover); } .memory { background-color: #4b5563; font-size: 0.9rem; } .memory:hover { background-color: #6b7280; } .equals { grid-column: span 2; background-color: var(--accent-button); } .equals:hover { background-color: var(--accent-button-hover); } .trigonometry { background-color: #374151; font-size: 0.9rem; } .trigonometry:hover { background-color: #4b5563; } .tab-container { display: flex; margin-bottom: 1rem; border-radius: var(--border-radius); overflow: hidden; background-color: var(--display-bg); } .tab { flex: 1; padding: 0.5rem; text-align: center; cursor: pointer; transition: var(--transition); font-size: 0.85rem; border: none; background-color: var(--display-bg); color: var(--button-text); border-bottom: 2px solid transparent; } .tab.active { border-bottom: 2px solid var(--accent-button); font-weight: 600; } .advanced-functions { display: none; grid-template-columns: repeat(5, 1fr); gap: 0.5rem; margin-bottom: 0.5rem; } .advanced-functions.active { display: grid; } .tooltip { position: absolute; background-color: rgba(0, 0, 0, 0.8); color: white; padding: 4px 8px; border-radius: 4px; font-size: 12px; pointer-events: none; opacity: 0; transition: opacity 0.3s; z-index: 100; white-space: nowrap; } @media (max-width: 400px) { .calculator { padding: 1rem; max-width: 100%; } .buttons { gap: 0.35rem; } .button { font-size: 0.9rem; padding: 0.6rem 0; } .advanced-functions { gap: 0.35rem; } } /* Animations */ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(3, 105, 161, 0.4); } 70% { box-shadow: 0 0 0 10px rgba(3, 105, 161, 0); } 100% { box-shadow: 0 0 0 0 rgba(3, 105, 161, 0); } } .calculator { animation: fadeIn 0.5s ease-out; } .equals { animation: pulse 2s infinite; } </style> </head> <body> <div class="calculator"> <div class="header"> <h1>Scientific Calculator</h1> <button class="mode-toggle">DEG</button> </div> <div class="display"> <div class="history" id="history"></div> <div class="current" id="current">0</div> </div> <div class="tab-container"> <button class="tab active" data-tab="basic">Basic</button> <button class="tab" data-tab="advanced">Advanced</button> <button class="tab" data-tab="memory">Memory</button> </div> <div class="advanced-functions" id="advanced-functions"> <button class="button trigonometry" data-action="sin">sin</button> <button class="button trigonometry" data-action="cos">cos</button> <button class="button trigonometry" data-action="tan">tan</button> <button class="button function" data-action="log">log</button> <button class="button function" data-action="ln">ln</button> <button class="button trigonometry" data-action="asin">sin<sup>-1</sup></button> <button class="button trigonometry" data-action="acos">cos<sup>-1</sup></button> <button class="button trigonometry" data-action="atan">tan<sup>-1</sup></button> <button class="button function" data-action="pow" data-value="2">x²</button> <button class="button function" data-action="sqrt">√</button> <button class="button function" data-action="factorial">x!</button> <button class="button function" data-action="pow" data-value="3">x³</button> <button class="button function" data-action="cbrt">∛</button> <button class="button function" data-action="exp">e<sup>x</sup></button> <button class="button function" data-action="pow10">10<sup>x</sup></button> </div> <div class="memory-functions" id="memory-functions" style="display: none; margin-bottom: 0.5rem; grid-template-columns: repeat(5, 1fr); gap: 0.5rem;"> <button class="button memory" data-action="mc">MC</button> <button class="button memory" data-action="mr">MR</button> <button class="button memory" data-action="m+">M+</button> <button class="button memory" data-action="m-">M-</button> <button class="button memory" data-action="ms">MS</button> </div> <div class="buttons"> <button class="button function" data-action="clear">C</button> <button class="button function" data-action="bracket">()</button> <button class="button function" data-action="percent">%</button> <button class="button function" data-action="backspace">←</button> <button class="button operator" data-action="divide">÷</button> <button class="button" data-action="number" data-value="7">7</button> <button class="button" data-action="number" data-value="8">8</button> <button class="button" data-action="number" data-value="9">9</button> <button class="button function" data-action="pow" data-value="y">x<sup>y</sup></button> <button class="button operator" data-action="multiply">×</button> <button class="button" data-action="number" data-value="4">4</button> <button class="button" data-action="number" data-value="5">5</button> <button class="button" data-action="number" data-value="6">6</button> <button class="button function" data-action="pi">π</button> <button class="button operator" data-action="subtract">−</button> <button class="button" data-action="number" data-value="1">1</button> <button class="button" data-action="number" data-value="2">2</button> <button class="button" data-action="number" data-value="3">3</button> <button class="button function" data-action="e">e</button> <button class="button operator" data-action="add">+</button> <button class="button" data-action="toggle-sign">±</button> <button class="button" data-action="number" data-value="0">0</button> <button class="button" data-action="decimal">.</button> <button class="button equals" data-action="calculate">=</button> </div> </div> <div class="tooltip" id="tooltip"></div> <script> document.addEventListener('DOMContentLoaded', function() { const calculator = { displayValue: '0', firstOperand: null, waitingForSecondOperand: false, operator: null, memory: 0, history: '', angleMode: 'DEG', // DEG or RAD lastResult: null, bracketCount: 0 }; const display = { current: document.getElementById('current'), history: document.getElementById('history') }; // Tabs const tabs = document.querySelectorAll('.tab'); const advancedFunctions = document.getElementById('advanced-functions'); const memoryFunctions = document.getElementById('memory-functions'); tabs.forEach(tab => { tab.addEventListener('click', () => { tabs.forEach(t => t.classList.remove('active')); tab.classList.add('active'); const tabName = tab.getAttribute('data-tab'); advancedFunctions.classList.remove('active'); memoryFunctions.style.display = 'none'; if (tabName === 'advanced') { advancedFunctions.classList.add('active'); } else if (tabName === 'memory') { memoryFunctions.style.display = 'grid'; } }); }); // Mode toggle const modeToggle = document.querySelector('.mode-toggle'); modeToggle.addEventListener('click', () => { calculator.angleMode = calculator.angleMode === 'DEG' ? 'RAD' : 'DEG'; modeToggle.textContent = calculator.angleMode; }); // Update display function updateDisplay() { display.current.textContent = calculator.displayValue; display.history.textContent = calculator.history; } // Reset calculator function resetCalculator() { calculator.displayValue = '0'; calculator.firstOperand = null; calculator.waitingForSecondOperand = false; calculator.operator = null; calculator.history = ''; calculator.lastResult = null; calculator.bracketCount = 0; updateDisplay(); } // Input digit function inputDigit(digit) { const { displayValue, waitingForSecondOperand } = calculator; if (waitingForSecondOperand) { calculator.displayValue = digit; calculator.waitingForSecondOperand = false; } else { calculator.displayValue = displayValue === '0' ? digit : displayValue + digit; } updateDisplay(); } // Input decimal function inputDecimal() { if (calculator.waitingForSecondOperand) { calculator.displayValue = '0.'; calculator.waitingForSecondOperand = false; updateDisplay(); return; } if (!calculator.displayValue.includes('.')) { calculator.displayValue += '.'; updateDisplay(); } } // Toggle sign function toggleSign() { const currentValue = parseFloat(calculator.displayValue); if (currentValue === 0) return; calculator.displayValue = (currentValue * -1).toString(); updateDisplay(); } // Handle operators function handleOperator(nextOperator) { const { firstOperand, displayValue, operator } = calculator; const inputValue = parseFloat(displayValue); if (firstOperand === null && !isNaN(inputValue)) { calculator.firstOperand = inputValue; } else if (operator) { const result = performCalculation[operator](firstOperand, inputValue); calculator.displayValue = `${parseFloat(result.toFixed(10))}`; calculator.firstOperand = result; calculator.lastResult = result; } calculator.waitingForSecondOperand = true; calculator.operator = nextOperator; // Update history if (nextOperator === '=') { calculator.history = ''; } else { const operatorSymbol = getOperatorSymbol(nextOperator); if (calculator.history === '') { calculator.history = `${calculator.displayValue} ${operatorSymbol}`; } else { calculator.history += ` ${calculator.displayValue} ${operatorSymbol}`; } } updateDisplay(); } // Get operator symbol function getOperatorSymbol(operator) { const symbols = { 'add': '+', 'subtract': '−', 'multiply': '×', 'divide': '÷' }; return symbols[operator] || operator; } // Perform calculation const performCalculation = { 'add': (firstOperand, secondOperand) => firstOperand + secondOperand, 'subtract': (firstOperand, secondOperand) => firstOperand - secondOperand, 'multiply': (firstOperand, secondOperand) => firstOperand * secondOperand, 'divide': (firstOperand, secondOperand) => firstOperand / secondOperand, 'pow': (firstOperand, secondOperand) => Math.pow(firstOperand, secondOperand) }; // Convert degrees to radians if needed function convertToRadians(angle) { return calculator.angleMode === 'DEG' ? angle * (Math.PI / 180) : angle; } // Scientific functions function handleScientificFunction(action, value) { const currentValue = parseFloat(calculator.displayValue); let result; switch(action) { case 'sin': result = Math.sin(convertToRadians(currentValue)); break; case 'cos': result = Math.cos(convertToRadians(currentValue)); break; case 'tan': result = Math.tan(convertToRadians(currentValue)); break; case 'asin': result = Math.asin(currentValue); if (calculator.angleMode === 'DEG') result = result * (180 / Math.PI); break; case 'acos': result = Math.acos(currentValue); if (calculator.angleMode === 'DEG') result = result * (180 / Math.PI); break; case 'atan': result = Math.atan(currentValue); if (calculator.angleMode === 'DEG') result = result * (180 / Math.PI); break; case 'log': result = Math.log10(currentValue); break; case 'ln': result = Math.log(currentValue); break; case 'pow': if (value === '2') { result = Math.pow(currentValue, 2); } else if (value === '3') { result = Math.pow(currentValue, 3); } else if (value === 'y') { calculator.firstOperand = currentValue; calculator.operator = 'pow'; calculator.waitingForSecondOperand = true; calculator.history = `${currentValue} ^`; updateDisplay(); return; } break; case 'sqrt': result = Math.sqrt(currentValue); break; case 'cbrt': result = Math.cbrt(currentValue); break; case 'factorial': result = factorial(currentValue); break; case 'exp': result = Math.exp(currentValue); break; case 'pow10': result = Math.pow(10, currentValue); break; case 'pi': result = Math.PI; break; case 'e': result = Math.E; break; case 'percent': if (calculator.firstOperand !== null && calculator.operator) { result = (calculator.firstOperand * currentValue) / 100; } else { result = currentValue / 100; } break; case 'bracket': handleBracket(); return; } calculator.displayValue = `${parseFloat(result.toFixed(10))}`; calculator.lastResult = result; updateDisplay(); } // Factorial function function factorial(n) { if (n === 0 || n === 1) return 1; if (!Number.isInteger(n) || n < 0) return NaN; let result = 1; for (let i = 2; i <= n; i++) { result *= i; } return result; } // Handle brackets function handleBracket() { // In a simple calculator, we're just simulating brackets for display purposes // For a full implementation, expression parsing would be needed if (calculator.bracketCount === 0) { calculator.history += ' ('; calculator.bracketCount = 1; } else { calculator.history += ' )'; calculator.bracketCount = 0; } updateDisplay(); } // Memory operations function handleMemory(action) { const currentValue = parseFloat(calculator.displayValue); switch(action) { case 'mc': // Memory clear calculator.memory = 0; break; case 'mr': // Memory recall calculator.displayValue = calculator.memory.toString(); break; case 'm+': // Memory add calculator.memory += currentValue; break; case 'm-': // Memory subtract calculator.memory -= currentValue; break; case 'ms': // Memory store calculator.memory = currentValue; break; } updateDisplay(); // Visual feedback for memory operations const memoryButton = document.querySelector(`[data-action="${action}"]`); memoryButton.style.opacity = '0.5'; setTimeout(() => { memoryButton.style.opacity = '1'; }, 200); } // Event listeners function setupEventListeners() { const buttons = document.querySelector('.calculator'); buttons.addEventListener('click', (event) => { const { target } = event; if (!target.matches('button')) { return; } const action = target.dataset.action; const value = target.dataset.value; if (!action) { return; } switch(action) { case 'number': inputDigit(value); break; case 'decimal': inputDecimal(); break; case 'clear': resetCalculator(); break; case 'backspace': if (calculator.displayValue.length === 1) { calculator.displayValue = '0'; } else { calculator.displayValue = calculator.displayValue.slice(0, -1); } updateDisplay(); break; case 'toggle-sign': toggleSign(); break; case 'add': case 'subtract': case 'multiply': case 'divide': handleOperator(action); break; case 'calculate': handleOperator('='); break; case 'mc': case 'mr': case 'm+': case 'm-': case 'ms': handleMemory(action); break; default: handleScientificFunction(action, value); } // Add ripple effect const circle = document.createElement('div'); const diameter = Math.max(target.clientWidth, target.clientHeight); const radius = diameter / 2; circle.style.width = circle.style.height = `${diameter}px`; circle.style.left = `${event.clientX - target.offsetLeft - radius}px`; circle.style.top = `${event.clientY - target.offsetTop - radius}px`; circle.classList.add('ripple'); // Cleanup const ripple = target.querySelector('.ripple'); if (ripple) { ripple.remove(); } }); // Tooltip for buttons const allButtons = document.querySelectorAll('.button'); const tooltip = document.getElementById('tooltip'); allButtons.forEach(button => { button.addEventListener('mouseenter', (e) => { const action = button.dataset.action; let tooltipText = ''; switch(action) { case 'sin': tooltipText = 'Sine function'; break; case 'cos': tooltipText = 'Cosine function'; break; case 'tan': tooltipText = 'Tangent function'; break; case 'asin': tooltipText = 'Inverse sine function'; break; case 'acos': tooltipText = 'Inverse cosine function'; break; case 'atan': tooltipText = 'Inverse tangent function'; break; case 'log': tooltipText = 'Logarithm (base 10)'; break; case 'ln': tooltipText = 'Natural logarithm (base e)'; break; case 'pow': const value = button.dataset.value; if (value === '2') tooltipText = 'Square'; else if (value === '3') tooltipText = 'Cube'; else if (value === 'y') tooltipText = 'Power (x^y)'; break; case 'sqrt': tooltipText = 'Square root'; break; case 'cbrt': tooltipText = 'Cube root'; break; case 'factorial': tooltipText = 'Factorial (x!)'; break; case 'exp': tooltipText = 'Exponential (e^x)'; break; case 'pow10': tooltipText = 'Power of 10 (10^x)'; break; case 'pi': tooltipText = 'Pi constant (π)'; break; case 'e': tooltipText = 'Euler\'s number (e)'; break; case 'bracket': tooltipText = 'Parentheses'; break; case 'percent': tooltipText = 'Percentage'; break; case 'mc': tooltipText = 'Memory clear'; break; case 'mr': tooltipText = 'Memory recall'; break; case 'm+': tooltipText = 'Memory add'; break; case 'm-': tooltipText = 'Memory subtract'; break; case 'ms': tooltipText = 'Memory store'; break; } if (tooltipText) { tooltip.textContent = tooltipText; tooltip.style.opacity = '1'; const rect = button.getBoundingClientRect(); tooltip.style.left = `${rect.left + rect.width / 2}px`; tooltip.style.top = `${rect.top - 30}px`; tooltip.style.transform = 'translateX(-50%)'; } }); button.addEventListener('mouseleave', () => { tooltip.style.opacity = '0'; }); }); // Keyboard support document.addEventListener('keydown', (event) => { const { key } = event; if (/[0-9]/.test(key)) { event.preventDefault(); inputDigit(key); } else if (key === '.') { event.preventDefault(); inputDecimal(); } else if (key === 'Backspace') { event.preventDefault(); if (calculator.displayValue.length === 1) { calculator.displayValue = '0'; } else { calculator.displayValue = calculator.displayValue.slice(0, -1); } updateDisplay(); } else if (key === 'Escape') { event.preventDefault(); resetCalculator(); } else if (key === '+') { event.preventDefault(); handleOperator('add'); } else if (key === '-') { event.preventDefault(); handleOperator('subtract'); } else if (key === '*') { event.preventDefault(); handleOperator('multiply'); } else if (key === '/') { event.preventDefault(); handleOperator('divide'); } else if (key === 'Enter' || key === '=') { event.preventDefault(); handleOperator('='); } }); } // Initialize setupEventListeners(); updateDisplay(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>QuantViz Graphing Calculator</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } :root { --primary: #2c3e50; --secondary: #3498db; --accent: #1abc9c; --dark: #1a2433; --light: #ecf0f1; --error: #e74c3c; --success: #2ecc71; --grid: rgba(44, 62, 80, 0.1); } body { background-color: var(--light); color: var(--primary); height: 700px; width: 700px; overflow: hidden; display: flex; flex-direction: column; } .container { width: 100%; height: 100%; padding: 15px; display: flex; flex-direction: column; background: linear-gradient(145deg, #f6f8f9, #e6eaec); box-shadow: 0 8px 32px rgba(31, 38, 135, 0.1); border-radius: 12px; overflow: hidden; } .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; } .logo { display: flex; align-items: center; font-weight: 700; font-size: 1.4rem; color: var(--primary); } .logo-icon { margin-right: 8px; color: var(--secondary); font-size: 1.6rem; } .mode-switch { display: flex; background: var(--light); border-radius: 20px; padding: 5px; box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.1); } .mode-btn { background: none; border: none; padding: 6px 12px; border-radius: 15px; cursor: pointer; font-size: 0.85rem; font-weight: 600; color: var(--primary); transition: all 0.3s ease; } .mode-btn.active { background: var(--secondary); color: white; box-shadow: 0 2px 10px rgba(52, 152, 219, 0.4); } .calculator-body { flex: 1; display: flex; gap: 15px; height: calc(100% - 60px); } .graph-area { flex: 1; background: white; border-radius: 10px; overflow: hidden; position: relative; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05); transition: all 0.3s ease; } #graph-canvas { width: 100%; height: 100%; cursor: move; } .keypad-area { width: 280px; display: flex; flex-direction: column; gap: 10px; transition: all 0.3s ease; } .input-display { background: white; border-radius: 10px; padding: 15px; position: relative; min-height: 110px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05); } #function-input { width: 100%; border: none; outline: none; font-size: 1.1rem; color: var(--primary); background: transparent; padding: 5px 0; border-bottom: 2px solid var(--grid); } .function-list { margin-top: 10px; max-height: 60px; overflow-y: auto; } .function-item { display: flex; justify-content: space-between; align-items: center; padding: 4px 0; font-size: 0.9rem; border-bottom: 1px solid var(--grid); } .function-expression { color: var(--primary); font-weight: 500; } .function-color { width: 12px; height: 12px; border-radius: 50%; } .function-delete { cursor: pointer; opacity: 0.6; transition: opacity 0.2s; } .function-delete:hover { opacity: 1; } .buttons-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; flex: 1; } .btn { border: none; background: white; border-radius: 8px; font-size: 1rem; font-weight: 500; color: var(--primary); cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); display: flex; justify-content: center; align-items: center; } .btn:hover { background: #f8f9fa; transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } .btn:active { transform: translateY(1px); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } .btn.function { background: rgba(52, 152, 219, 0.1); color: var(--secondary); } .btn.clear { background: rgba(231, 76, 60, 0.1); color: var(--error); } .btn.equals { background: var(--secondary); color: white; grid-column: span 2; } .graph-controls { position: absolute; bottom: 15px; right: 15px; display: flex; gap: 8px; } .graph-control-btn { width: 36px; height: 36px; border-radius: 50%; background: white; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); border: none; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.2s ease; color: var(--primary); } .graph-control-btn:hover { background: var(--secondary); color: white; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(52, 152, 219, 0.4); } .graph-info { position: absolute; top: 15px; left: 15px; background: rgba(255, 255, 255, 0.8); padding: 6px 10px; border-radius: 6px; font-size: 0.8rem; color: var(--primary); backdrop-filter: blur(4px); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .tooltip { position: absolute; background: var(--dark); color: white; padding: 5px 10px; border-radius: 4px; font-size: 0.8rem; pointer-events: none; opacity: 0; transition: opacity 0.2s; z-index: 100; } .loading-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.8); display: none; justify-content: center; align-items: center; z-index: 10; } .loader { width: 40px; height: 40px; border: 4px solid rgba(52, 152, 219, 0.2); border-radius: 50%; border-top-color: var(--secondary); animation: spin 1s infinite linear; } @keyframes spin { to { transform: rotate(360deg); } } .grid-line { stroke: var(--grid); stroke-width: 1; } .axis-line { stroke: var(--primary); stroke-width: 2; } .axis-label { font-size: 10px; fill: var(--primary); } .function-path { fill: none; stroke-width: 2.5; } /* Animation for adding new functions */ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .function-item { animation: fadeIn 0.3s ease forwards; } /* Animation for plotting graph */ @keyframes draw { from { stroke-dashoffset: 1000; } to { stroke-dashoffset: 0; } } .animating { stroke-dasharray: 1000; animation: draw 1.5s ease forwards; } /* Responsive adjustments */ @media (max-width: 700px) { .calculator-body { flex-direction: column; } .keypad-area { width: 100%; order: 2; } .graph-area { order: 1; height: 350px; } } /* Custom tooltip for buttons */ .btn[data-tooltip]:hover::after { content: attr(data-tooltip); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: var(--dark); color: white; padding: 4px 8px; border-radius: 4px; font-size: 0.7rem; white-space: nowrap; z-index: 100; opacity: 0.9; } /* Dark mode styles */ body.dark-mode { background-color: var(--dark); color: var(--light); } body.dark-mode .container { background: linear-gradient(145deg, #222d3b, #1a2433); } body.dark-mode .logo { color: var(--light); } body.dark-mode .mode-switch { background: #263545; } body.dark-mode .mode-btn { color: var(--light); } body.dark-mode .graph-area, body.dark-mode .input-display { background: #263545; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); } body.dark-mode #function-input { color: var(--light); border-bottom-color: rgba(255, 255, 255, 0.1); } body.dark-mode .function-item { border-bottom-color: rgba(255, 255, 255, 0.1); } body.dark-mode .function-expression { color: var(--light); } body.dark-mode .btn { background: #263545; color: var(--light); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); } body.dark-mode .btn:hover { background: #304357; } body.dark-mode .btn.function { background: rgba(52, 152, 219, 0.2); } body.dark-mode .btn.clear { background: rgba(231, 76, 60, 0.2); } body.dark-mode .graph-control-btn { background: #263545; color: var(--light); } body.dark-mode .graph-info { background: rgba(38, 53, 69, 0.8); color: var(--light); } body.dark-mode .grid-line { stroke: rgba(255, 255, 255, 0.1); } body.dark-mode .axis-line { stroke: var(--light); } body.dark-mode .axis-label { fill: var(--light); } body.dark-mode .loading-overlay { background: rgba(26, 36, 51, 0.8); } </style> </head> <body> <div class="container"> <div class="header"> <div class="logo"> <span class="logo-icon">📊</span>QuantViz </div> <div class="mode-switch"> <button id="basic-mode" class="mode-btn active">Basic</button> <button id="advanced-mode" class="mode-btn">Advanced</button> </div> </div> <div class="calculator-body"> <div class="graph-area"> <svg id="graph-canvas" width="100%" height="100%"></svg> <div class="graph-info"> X: <span id="x-range">[-10, 10]</span> | Y: <span id="y-range">[-10, 10]</span> </div> <div class="graph-controls"> <button class="graph-control-btn" id="zoom-in" title="Zoom In">+</button> <button class="graph-control-btn" id="zoom-out" title="Zoom Out">−</button> <button class="graph-control-btn" id="reset-view" title="Reset View">⟲</button> </div> <div class="loading-overlay"> <div class="loader"></div> </div> </div> <div class="keypad-area"> <div class="input-display"> <input type="text" id="function-input" placeholder="Enter a function (e.g., x^2)" autocomplete="off"> <div class="function-list" id="function-list"> <!-- Functions will appear here --> </div> </div> <div class="buttons-grid"> <!-- Row 1 --> <button class="btn function" data-tooltip="Sine">sin</button> <button class="btn function" data-tooltip="Cosine">cos</button> <button class="btn function" data-tooltip="Tangent">tan</button> <button class="btn clear" data-tooltip="Clear All">AC</button> <!-- Row 2 --> <button class="btn function" data-tooltip="Power">^</button> <button class="btn function" data-tooltip="Square Root">√</button> <button class="btn function" data-tooltip="Logarithm">ln</button> <button class="btn" data-tooltip="Delete">⌫</button> <!-- Row 3 --> <button class="btn">7</button> <button class="btn">8</button> <button class="btn">9</button> <button class="btn">÷</button> <!-- Row 4 --> <button class="btn">4</button> <button class="btn">5</button> <button class="btn">6</button> <button class="btn">×</button> <!-- Row 5 --> <button class="btn">1</button> <button class="btn">2</button> <button class="btn">3</button> <button class="btn">−</button> <!-- Row 6 --> <button class="btn">0</button> <button class="btn">.</button> <button class="btn">x</button> <button class="btn">+</button> <!-- Row 7 --> <button class="btn">π</button> <button class="btn">e</button> <button class="btn equals" data-tooltip="Plot Function">Plot</button> </div> </div> </div> </div> <div class="tooltip" id="tooltip"></div> <script> document.addEventListener('DOMContentLoaded', function() { // DOM Elements const functionInput = document.getElementById('function-input'); const functionList = document.getElementById('function-list'); const graphCanvas = document.getElementById('graph-canvas'); const buttons = document.querySelectorAll('.btn'); const clearBtn = document.querySelector('.btn.clear'); const plotBtn = document.querySelector('.btn.equals'); const backspaceBtn = document.querySelector('.btn[data-tooltip="Delete"]'); const zoomInBtn = document.getElementById('zoom-in'); const zoomOutBtn = document.getElementById('zoom-out'); const resetViewBtn = document.getElementById('reset-view'); const xRangeDisplay = document.getElementById('x-range'); const yRangeDisplay = document.getElementById('y-range'); const tooltip = document.getElementById('tooltip'); const loadingOverlay = document.querySelector('.loading-overlay'); const basicModeBtn = document.getElementById('basic-mode'); const advancedModeBtn = document.getElementById('advanced-mode'); const body = document.body; // Graph state const graphState = { xMin: -10, xMax: 10, yMin: -10, yMax: 10, functions: [], isDragging: false, dragStartX: 0, dragStartY: 0, lastDragX: 0, lastDragY: 0, zoomLevel: 1, darkMode: false }; // Function colors const functionColors = [ '#3498db', '#e74c3c', '#2ecc71', '#f1c40f', '#9b59b6', '#1abc9c', '#d35400', '#34495e', '#16a085', '#c0392b' ]; // Initialize Graph initGraph(); // Mode Switching basicModeBtn.addEventListener('click', () => { basicModeBtn.classList.add('active'); advancedModeBtn.classList.remove('active'); // Set basic mode functions here }); advancedModeBtn.addEventListener('click', () => { advancedModeBtn.classList.add('active'); basicModeBtn.classList.remove('active'); // Set advanced mode functions here }); // Dark Mode Toggle - Double click on logo document.querySelector('.logo').addEventListener('dblclick', () => { body.classList.toggle('dark-mode'); graphState.darkMode = body.classList.contains('dark-mode'); drawGraph(); }); // Button click handlers buttons.forEach(button => { button.addEventListener('click', () => { const value = button.textContent; // Skip certain buttons if (value === 'AC' || value === '⌫' || value === 'Plot') { return; } // Handle function buttons if (['sin', 'cos', 'tan', 'ln', '√'].includes(value)) { functionInput.value += value + '('; } else if (value === '×') { functionInput.value += '*'; } else if (value === '÷') { functionInput.value += '/'; } else { functionInput.value += value; } functionInput.focus(); }); }); // Clear button clearBtn.addEventListener('click', () => { functionInput.value = ''; functionList.innerHTML = ''; graphState.functions = []; drawGraph(); }); // Backspace button backspaceBtn.addEventListener('click', () => { functionInput.value = functionInput.value.slice(0, -1); }); // Plot function plotBtn.addEventListener('click', addFunction); // Also plot on Enter key functionInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { addFunction(); } }); // Zoom controls zoomInBtn.addEventListener('click', () => { zoom(0.8); }); zoomOutBtn.addEventListener('click', () => { zoom(1.25); }); resetViewBtn.addEventListener('click', resetView); // Pan controls graphCanvas.addEventListener('mousedown', startDrag); graphCanvas.addEventListener('mousemove', drag); graphCanvas.addEventListener('mouseup', endDrag); graphCanvas.addEventListener('mouseleave', endDrag); graphCanvas.addEventListener('wheel', handleZoomWheel); // Initialize graph function initGraph() { drawGrid(); drawAxes(); } // Add a new function function addFunction() { if (!functionInput.value.trim()) return; const expression = functionInput.value.trim(); // Show loading state loadingOverlay.style.display = 'flex'; // Use setTimeout to give the loading indicator time to show setTimeout(() => { try { // Test the function to see if it's valid evaluateExpression(expression, 1); const color = functionColors[graphState.functions.length % functionColors.length]; graphState.functions.push({ expression: expression, color: color }); // Add to function list addFunctionToList(expression, color); // Draw the graph drawGraph(); // Clear input functionInput.value = ''; } catch (e) { // Show error as a tooltip showTooltip(plotBtn, 'Invalid function. Please check your syntax.'); console.error('Function evaluation error:', e); } finally { // Hide loading state loadingOverlay.style.display = 'none'; } }, 300); } // Add a function to the list display function addFunctionToList(expression, color) { const functionItem = document.createElement('div'); functionItem.className = 'function-item'; functionItem.innerHTML = ` <div class="function-expression">f(x) = ${expression}</div> <div class="function-controls"> <span class="function-color" style="background-color: ${color}"></span> <span class="function-delete">×</span> </div> `; // Delete function handler functionItem.querySelector('.function-delete').addEventListener('click', function() { const index = Array.from(functionList.children).indexOf(functionItem); if (index !== -1) { graphState.functions.splice(index, 1); functionItem.remove(); drawGraph(); } }); functionList.appendChild(functionItem); } // Draw the graph grid function drawGrid() { graphCanvas.innerHTML = ''; const width = graphCanvas.clientWidth; const height = graphCanvas.clientHeight; // Calculate grid spacing const xRange = graphState.xMax - graphState.xMin; const yRange = graphState.yMax - graphState.yMin; const xGridSize = width / xRange; const yGridSize = height / yRange; // Grid group const gridGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g'); gridGroup.classList.add('grid'); // Vertical grid lines (x-axis) for (let x = Math.ceil(graphState.xMin); x <= graphState.xMax; x++) { if (x === 0) continue; // Skip the origin line which will be drawn as axis const xPos = mapX(x); const line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); line.setAttribute('x1', xPos); line.setAttribute('y1', 0); line.setAttribute('x2', xPos); line.setAttribute('y2', height); line.classList.add('grid-line'); gridGroup.appendChild(line); } // Horizontal grid lines (y-axis) for (let y = Math.ceil(graphState.yMin); y <= graphState.yMax; y++) { if (y === 0) continue; // Skip the origin line which will be drawn as axis const yPos = mapY(y); const line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); line.setAttribute('x1', 0); line.setAttribute('y1', yPos); line.setAttribute('x2', width); line.setAttribute('y2', yPos); line.classList.add('grid-line'); gridGroup.appendChild(line); } graphCanvas.appendChild(gridGroup); } // Draw the x and y axes function drawAxes() { const width = graphCanvas.clientWidth; const height = graphCanvas.clientHeight; // Axes group const axesGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g'); axesGroup.classList.add('axes'); // X-axis const xAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line'); xAxis.setAttribute('x1', 0); xAxis.setAttribute('y1', mapY(0)); xAxis.setAttribute('x2', width); xAxis.setAttribute('y2', mapY(0)); xAxis.classList.add('axis-line'); axesGroup.appendChild(xAxis); // Y-axis const yAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line'); yAxis.setAttribute('x1', mapX(0)); yAxis.setAttribute('y1', 0); yAxis.setAttribute('x2', mapX(0)); yAxis.setAttribute('y2', height); yAxis.classList.add('axis-line'); axesGroup.appendChild(yAxis); // X-axis labels for (let x = Math.ceil(graphState.xMin); x <= graphState.xMax; x++) { if (x === 0) continue; // Skip origin // Only show every other label if the range is large if (graphState.xMax - graphState.xMin > 20 && x % 2 !== 0) continue; const xPos = mapX(x); const label = document.createElementNS('http://www.w3.org/2000/svg', 'text'); label.setAttribute('x', xPos); label.setAttribute('y', mapY(0) + 20); label.setAttribute('text-anchor', 'middle'); label.classList.add('axis-label'); label.textContent = x; axesGroup.appendChild(label); } // Y-axis labels for (let y = Math.ceil(graphState.yMin); y <= graphState.yMax; y++) { if (y === 0) continue; // Skip origin // Only show every other label if the range is large if (graphState.yMax - graphState.yMin > 20 && y % 2 !== 0) continue; const yPos = mapY(y); const label = document.createElementNS('http://www.w3.org/2000/svg', 'text'); label.setAttribute('x', mapX(0) - 10); label.setAttribute('y', yPos + 4); // Slight adjustment for text alignment label.setAttribute('text-anchor', 'end'); label.classList.add('axis-label'); label.textContent = y; axesGroup.appendChild(label); } graphCanvas.appendChild(axesGroup); } // Draw all functions function drawFunctions() { const functionsGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g'); functionsGroup.classList.add('functions'); graphState.functions.forEach((func, index) => { try { const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path.classList.add('function-path'); path.style.stroke = func.color; // Generate path data const pathData = generateFunctionPath(func.expression); path.setAttribute('d', pathData); // Add animation class for newly added functions if (index === graphState.functions.length - 1) { path.classList.add('animating'); } functionsGroup.appendChild(path); } catch (e) { console.error('Error drawing function:', e); } }); graphCanvas.appendChild(functionsGroup); } // Generate SVG path data for a function function generateFunctionPath(expression) { const width = graphCanvas.clientWidth; const step = (graphState.xMax - graphState.xMin) / (width * 2); // High resolution for smooth curves let pathData = ''; let isFirstPoint = true; let lastY = null; for (let xPixel = 0; xPixel <= width; xPixel += 0.5) { const x = graphState.xMin + (xPixel / width) * (graphState.xMax - graphState.xMin); try { const y = evaluateExpression(expression, x); // Skip if y is not a number or infinite if (isNaN(y) || !isFinite(y)) { isFirstPoint = true; continue; } // Skip if there's a large jump (discontinuity) if (lastY !== null && Math.abs(y - lastY) > (graphState.yMax - graphState.yMin) / 4) { isFirstPoint = true; lastY = y; continue; } lastY = y; // Check if y is in visible range if (y >= graphState.yMin && y <= graphState.yMax) { const xPos = xPixel; const yPos = mapY(y); if (isFirstPoint) { pathData += `M ${xPos} ${yPos}`; isFirstPoint = false; } else { pathData += ` L ${xPos} ${yPos}`; } } else { isFirstPoint = true; // Start a new path when coming back into view } } catch (e) { isFirstPoint = true; } } return pathData; } // Evaluate a math expression with given x function evaluateExpression(expression, x) { // Replace common mathematical functions and constants let expr = expression .replace(/\^/g, '**') .replace(/sin\(/g, 'Math.sin(') .replace(/cos\(/g, 'Math.cos(') .replace(/tan\(/g, 'Math.tan(') .replace(/ln\(/g, 'Math.log(') .replace(/log\(/g, 'Math.log10(') .replace(/sqrt\(/g, 'Math.sqrt(') .replace(/√\(/g, 'Math.sqrt(') .replace(/π/g, 'Math.PI') .replace(/e/g, 'Math.E'); // Create a function and evaluate const func = new Function('x', `return ${expr};`); return func(x); } // Map x coordinate from math space to screen space function mapX(x) { const width = graphCanvas.clientWidth; return ((x - graphState.xMin) / (graphState.xMax - graphState.xMin)) * width; } // Map y coordinate from math space to screen space function mapY(y) { const height = graphCanvas.clientHeight; return height - ((y - graphState.yMin) / (graphState.yMax - graphState.yMin)) * height; } // Draw the complete graph function drawGraph() { drawGrid(); drawAxes(); drawFunctions(); updateRangeDisplay(); } // Update range display function updateRangeDisplay() { xRangeDisplay.textContent = `[${graphState.xMin.toFixed(1)}, ${graphState.xMax.toFixed(1)}]`; yRangeDisplay.textContent = `[${graphState.yMin.toFixed(1)}, ${graphState.yMax.toFixed(1)}]`; } // Start dragging the graph function startDrag(e) { graphState.isDragging = true; graphState.dragStartX = e.clientX; graphState.dragStartY = e.clientY; graphState.lastDragX = graphState.dragStartX; graphState.lastDragY = graphState.dragStartY; graphCanvas.style.cursor = 'grabbing'; } // Drag the graph function drag(e) { if (!graphState.isDragging) return; const dx = e.clientX - graphState.lastDragX; const dy = e.clientY - graphState.lastDragY; graphState.lastDragX = e.clientX; graphState.lastDragY = e.clientY; const width = graphCanvas.clientWidth; const height = graphCanvas.clientHeight; const xDelta = (dx / width) * (graphState.xMax - graphState.xMin); const yDelta = (dy / height) * (graphState.yMax - graphState.yMin); graphState.xMin -= xDelta; graphState.xMax -= xDelta; graphState.yMin += yDelta; graphState.yMax += yDelta; drawGraph(); } // End dragging function endDrag() { graphState.isDragging = false; graphCanvas.style.cursor = 'move'; } // Handle mouse wheel for zooming function handleZoomWheel(e) { e.preventDefault(); // Determine zoom direction const zoomFactor = e.deltaY > 0 ? 1.1 : 0.9; // Get mouse position const rect = graphCanvas.getBoundingClientRect(); const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; // Calculate the math coordinates at the mouse position const xMouse = graphState.xMin + (mouseX / rect.width) * (graphState.xMax - graphState.xMin); const yMouse = graphState.yMax - (mouseY / rect.height) * (graphState.yMax - graphState.yMin); // Apply zoom centered on mouse position const newXRange = (graphState.xMax - graphState.xMin) * zoomFactor; const newYRange = (graphState.yMax - graphState.yMin) * zoomFactor; const xRatio = (xMouse - graphState.xMin) / (graphState.xMax - graphState.xMin); const yRatio = (yMouse - graphState.yMin) / (graphState.yMax - graphState.yMin); graphState.xMin = xMouse - xRatio * newXRange; graphState.xMax = xMouse + (1 - xRatio) * newXRange; graphState.yMin = yMouse - yRatio * newYRange; graphState.yMax = yMouse + (1 - yRatio) * newYRange; drawGraph(); } // Zoom function for zoom buttons function zoom(factor) { const centerX = (graphState.xMin + graphState.xMax) / 2; const centerY = (graphState.yMin + graphState.yMax) / 2; const newXRange = (graphState.xMax - graphState.xMin) * factor; const newYRange = (graphState.yMax - graphState.yMin) * factor;
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Fluid Converter</title> <style> :root { --primary: #3498db; --secondary: #2ecc71; --accent: #f39c12; --dark: #2c3e50; --light: #ecf0f1; --white: #ffffff; --shadow: 0 4px 12px rgba(0, 0, 0, 0.08); --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background-color: var(--light); color: var(--dark); overflow-x: hidden; height: 100vh; display: flex; align-items: center; justify-content: center; position: relative; } .background-pattern { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient(circle at 10% 20%, rgba(46, 204, 113, 0.05) 0%, transparent 20%), radial-gradient(circle at 90% 80%, rgba(52, 152, 219, 0.07) 0%, transparent 20%), radial-gradient(circle at 50% 50%, rgba(243, 156, 18, 0.05) 0%, transparent 30%); z-index: -1; opacity: 0.7; } .container { width: 100%; max-width: 600px; padding: 2rem; border-radius: 16px; background-color: var(--white); box-shadow: var(--shadow); position: relative; overflow: hidden; } h1 { text-align: center; margin-bottom: 1.5rem; color: var(--dark); font-weight: 700; font-size: 1.8rem; position: relative; } h1::after { content: ""; position: absolute; bottom: -10px; left: 50%; transform: translateX(-50%); width: 50px; height: 3px; background: linear-gradient(to right, var(--primary), var(--secondary)); border-radius: 3px; } .tagline { text-align: center; margin-bottom: 2rem; color: #7f8c8d; font-size: 0.9rem; } .conversion-panel { display: flex; flex-direction: column; gap: 1.5rem; } .category-selector { display: flex; justify-content: center; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap; } .category-btn { padding: 0.6rem 1rem; border: none; background-color: var(--light); color: var(--dark); border-radius: 50px; cursor: pointer; font-size: 0.85rem; font-weight: 500; transition: var(--transition); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .category-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } .category-btn.active { background-color: var(--primary); color: white; } .conversion-group { display: flex; flex-direction: column; gap: 1rem; } .unit-row { display: flex; align-items: center; gap: 1rem; position: relative; } .unit-row input { flex: 1; padding: 1rem; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 1rem; transition: var(--transition); background-color: #f9f9f9; } .unit-row input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1); background-color: white; } .unit-selector { position: relative; width: 120px; } .unit-selector select { width: 100%; padding: 1rem; border: 2px solid #e0e0e0; border-radius: 8px; appearance: none; background-color: #f9f9f9; cursor: pointer; font-size: 0.9rem; font-weight: 500; transition: var(--transition); } .unit-selector select:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1); } .unit-selector::after { content: "▼"; position: absolute; top: 50%; right: 10px; transform: translateY(-50%); color: #95a5a6; font-size: 0.7rem; pointer-events: none; } .swap-btn { position: absolute; left: 50%; transform: translateX(-50%); width: 40px; height: 40px; background-color: var(--white); border: 2px solid #e0e0e0; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); z-index: 10; box-shadow: var(--shadow); } .swap-btn:hover { background-color: var(--primary); border-color: var(--primary); transform: translateX(-50%) rotate(180deg); } .swap-btn:hover svg { fill: white; } .swap-btn svg { width: 20px; height: 20px; fill: var(--dark); transition: var(--transition); } .formula-display { margin-top: 1.5rem; padding: 1rem; background-color: rgba(236, 240, 241, 0.5); border-radius: 8px; font-size: 0.9rem; color: #7f8c8d; text-align: center; transition: var(--transition); overflow: hidden; max-height: 0; opacity: 0; } .formula-display.visible { max-height: 100px; opacity: 1; } .info-btn { position: absolute; top: 1rem; right: 1rem; width: 30px; height: 30px; border-radius: 50%; background-color: var(--light); border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); } .info-btn svg { width: 16px; height: 16px; fill: #95a5a6; transition: var(--transition); } .info-btn:hover { background-color: var(--primary); } .info-btn:hover svg { fill: white; } .tooltip { position: absolute; top: 0; left: 0; background-color: var(--dark); color: white; padding: 0.7rem; border-radius: 8px; font-size: 0.8rem; z-index: 100; opacity: 0; visibility: hidden; transition: var(--transition); width: max-content; max-width: 200px; box-shadow: var(--shadow); } /* Decorative elements */ .decoration { position: absolute; border-radius: 50%; z-index: -1; } .decoration-1 { top: -20px; left: -20px; width: 60px; height: 60px; background-color: rgba(52, 152, 219, 0.1); } .decoration-2 { bottom: -30px; right: -30px; width: 80px; height: 80px; background-color: rgba(46, 204, 113, 0.1); } .pulse { animation: pulse 2s infinite; } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .fade-in { animation: fadeIn 0.5s ease forwards; } .hidden { display: none; } footer { position: absolute; bottom: 0.5rem; left: 0; width: 100%; text-align: center; font-size: 0.7rem; color: #95a5a6; } /* Responsive styles */ @media (max-width: 500px) { .container { padding: 1.5rem; border-radius: 12px; } h1 { font-size: 1.5rem; } .category-btn { padding: 0.5rem 0.8rem; font-size: 0.8rem; } .unit-row { flex-direction: column; gap: 0.8rem; } .unit-selector { width: 100%; } .swap-btn { position: relative; left: auto; transform: none; margin: 0.5rem auto; } } </style> </head> <body> <div class="background-pattern"></div> <div class="container"> <h1>Fluid Converter</h1> <p class="tagline">Convert between units with buttery-smooth precision</p> <div class="decoration decoration-1"></div> <div class="decoration decoration-2"></div> <button class="info-btn" id="infoBtn"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/> </svg> <div class="tooltip" id="tooltip">Hover over the conversion formula to see how calculations are performed. Use the swap button to quickly reverse units.</div> </button> <div class="conversion-panel"> <div class="category-selector"> <button class="category-btn active" data-category="length">Length</button> <button class="category-btn" data-category="weight">Weight</button> <button class="category-btn" data-category="temperature">Temperature</button> <button class="category-btn" data-category="volume">Volume</button> <button class="category-btn" data-category="time">Time</button> </div> <div class="conversion-group" id="lengthGroup"> <div class="unit-row"> <input type="number" id="fromLength" placeholder="Enter value"> <div class="unit-selector"> <select id="fromLengthUnit"> <option value="meters">Meters</option> <option value="kilometers">Kilometers</option> <option value="centimeters">Centimeters</option> <option value="millimeters">Millimeters</option> <option value="miles">Miles</option> <option value="yards">Yards</option> <option value="feet">Feet</option> <option value="inches">Inches</option> </select> </div> </div> <div class="swap-btn" id="lengthSwap"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"/> </svg> </div> <div class="unit-row"> <input type="number" id="toLength" placeholder="Result" readonly> <div class="unit-selector"> <select id="toLengthUnit"> <option value="meters">Meters</option> <option value="kilometers">Kilometers</option> <option value="centimeters">Centimeters</option> <option value="millimeters">Millimeters</option> <option value="miles">Miles</option> <option value="yards">Yards</option> <option value="feet" selected>Feet</option> <option value="inches">Inches</option> </select> </div> </div> <div class="formula-display" id="lengthFormula"> 1 meter = 3.281 feet </div> </div> <div class="conversion-group hidden" id="weightGroup"> <div class="unit-row"> <input type="number" id="fromWeight" placeholder="Enter value"> <div class="unit-selector"> <select id="fromWeightUnit"> <option value="kilograms">Kilograms</option> <option value="grams">Grams</option> <option value="milligrams">Milligrams</option> <option value="pounds">Pounds</option> <option value="ounces">Ounces</option> <option value="tons">Metric Tons</option> </select> </div> </div> <div class="swap-btn" id="weightSwap"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"/> </svg> </div> <div class="unit-row"> <input type="number" id="toWeight" placeholder="Result" readonly> <div class="unit-selector"> <select id="toWeightUnit"> <option value="kilograms">Kilograms</option> <option value="grams">Grams</option> <option value="milligrams">Milligrams</option> <option value="pounds" selected>Pounds</option> <option value="ounces">Ounces</option> <option value="tons">Metric Tons</option> </select> </div> </div> <div class="formula-display" id="weightFormula"> 1 kilogram = 2.205 pounds </div> </div> <div class="conversion-group hidden" id="temperatureGroup"> <div class="unit-row"> <input type="number" id="fromTemp" placeholder="Enter value"> <div class="unit-selector"> <select id="fromTempUnit"> <option value="celsius">Celsius</option> <option value="fahrenheit">Fahrenheit</option> <option value="kelvin">Kelvin</option> </select> </div> </div> <div class="swap-btn" id="tempSwap"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"/> </svg> </div> <div class="unit-row"> <input type="number" id="toTemp" placeholder="Result" readonly> <div class="unit-selector"> <select id="toTempUnit"> <option value="celsius">Celsius</option> <option value="fahrenheit" selected>Fahrenheit</option> <option value="kelvin">Kelvin</option> </select> </div> </div> <div class="formula-display" id="tempFormula"> °C × 9/5 + 32 = °F </div> </div> <div class="conversion-group hidden" id="volumeGroup"> <div class="unit-row"> <input type="number" id="fromVolume" placeholder="Enter value"> <div class="unit-selector"> <select id="fromVolumeUnit"> <option value="liters">Liters</option> <option value="milliliters">Milliliters</option> <option value="cubicMeters">Cubic Meters</option> <option value="gallons">Gallons (US)</option> <option value="quarts">Quarts (US)</option> <option value="pints">Pints (US)</option> <option value="cups">Cups (US)</option> <option value="fluidOunces">Fluid Ounces (US)</option> </select> </div> </div> <div class="swap-btn" id="volumeSwap"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"/> </svg> </div> <div class="unit-row"> <input type="number" id="toVolume" placeholder="Result" readonly> <div class="unit-selector"> <select id="toVolumeUnit"> <option value="liters">Liters</option> <option value="milliliters">Milliliters</option> <option value="cubicMeters">Cubic Meters</option> <option value="gallons" selected>Gallons (US)</option> <option value="quarts">Quarts (US)</option> <option value="pints">Pints (US)</option> <option value="cups">Cups (US)</option> <option value="fluidOunces">Fluid Ounces (US)</option> </select> </div> </div> <div class="formula-display" id="volumeFormula"> 1 liter = 0.264 gallons (US) </div> </div> <div class="conversion-group hidden" id="timeGroup"> <div class="unit-row"> <input type="number" id="fromTime" placeholder="Enter value"> <div class="unit-selector"> <select id="fromTimeUnit"> <option value="seconds">Seconds</option> <option value="minutes">Minutes</option> <option value="hours">Hours</option> <option value="days">Days</option> <option value="weeks">Weeks</option> <option value="months">Months</option> <option value="years">Years</option> </select> </div> </div> <div class="swap-btn" id="timeSwap"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"/> </svg> </div> <div class="unit-row"> <input type="number" id="toTime" placeholder="Result" readonly> <div class="unit-selector"> <select id="toTimeUnit"> <option value="seconds">Seconds</option> <option value="minutes" selected>Minutes</option> <option value="hours">Hours</option> <option value="days">Days</option> <option value="weeks">Weeks</option> <option value="months">Months</option> <option value="years">Years</option> </select> </div> </div> <div class="formula-display" id="timeFormula"> 1 second = 0.0167 minutes </div> </div> </div> </div> <script> // Conversion factors and formulas const conversionFactors = { // Length conversions (to meters) length: { meters: 1, kilometers: 1000, centimeters: 0.01, millimeters: 0.001, miles: 1609.34, yards: 0.9144, feet: 0.3048, inches: 0.0254 }, // Weight conversions (to kilograms) weight: { kilograms: 1, grams: 0.001, milligrams: 0.000001, pounds: 0.453592, ounces: 0.0283495, tons: 1000 }, // Volume conversions (to liters) volume: { liters: 1, milliliters: 0.001, cubicMeters: 1000, gallons: 3.78541, quarts: 0.946353, pints: 0.473176, cups: 0.236588, fluidOunces: 0.0295735 }, // Time conversions (to seconds) time: { seconds: 1, minutes: 60, hours: 3600, days: 86400, weeks: 604800, months: 2592000, // 30-day month years: 31536000 // 365-day year } }; // Unit display names for formulas const unitDisplayNames = { meters: "meters", kilometers: "kilometers", centimeters: "centimeters", millimeters: "millimeters", miles: "miles", yards: "yards", feet: "feet", inches: "inches", kilograms: "kilograms", grams: "grams", milligrams: "milligrams", pounds: "pounds", ounces: "ounces", tons: "metric tons", liters: "liters", milliliters: "milliliters", cubicMeters: "cubic meters", gallons: "gallons (US)", quarts: "quarts (US)", pints: "pints (US)", cups: "cups (US)", fluidOunces: "fluid ounces (US)", seconds: "seconds", minutes: "minutes", hours: "hours", days: "days", weeks: "weeks", months: "months", years: "years" }; // Get DOM elements const categoryBtns = document.querySelectorAll('.category-btn'); const conversionGroups = document.querySelectorAll('.conversion-group'); // Info button tooltip const infoBtn = document.getElementById('infoBtn'); const tooltip = document.getElementById('tooltip'); infoBtn.addEventListener('mouseenter', () => { tooltip.style.opacity = '1'; tooltip.style.visibility = 'visible'; // Position the tooltip const infoBtnRect = infoBtn.getBoundingClientRect(); tooltip.style.top = `${infoBtnRect.bottom + 10}px`; tooltip.style.left = `${infoBtnRect.left - tooltip.offsetWidth/2 + infoBtnRect.width/2}px`; }); infoBtn.addEventListener('mouseleave', () => { tooltip.style.opacity = '0'; tooltip.style.visibility = 'hidden'; }); // Category switching categoryBtns.forEach(btn => { btn.addEventListener('click', () => { // Remove active class from all buttons categoryBtns.forEach(b => b.classList.remove('active')); // Add active class to clicked button btn.classList.add('active'); // Hide all conversion groups conversionGroups.forEach(group => group.classList.add('hidden')); // Show the selected conversion group const category = btn.dataset.category; document.getElementById(`${category}Group`).classList.remove('hidden'); document.getElementById(`${category}Group`).classList.add('fade-in'); // Update the formula display updateFormula(category); }); }); // Set up event listeners for each category ['length', 'weight', 'temperature', 'volume', 'time'].forEach(category => { // Get input elements for this category const fromInput = document.getElementById(`from${capitalize(category)}`); const toInput = document.getElementById(`to${capitalize(category)}`); const fromUnit = document.getElementById(`from${capitalize(category)}Unit`); const toUnit = document.getElementById(`to${capitalize(category)}Unit`); const swapBtn = document.getElementById(`${category}Swap`); const formulaDisplay = document.getElementById(`${category}Formula`); // Input change event if (fromInput) { fromInput.addEventListener('input', () => { convert(category); }); } // Unit selection change events if (fromUnit) { fromUnit.addEventListener('change', () => { convert(category); updateFormula(category); }); } if (toUnit) { toUnit.addEventListener('change', () => { convert(category); updateFormula(category); }); } // Swap button click if (swapBtn) { swapBtn.addEventListener('click', () => { // Swap unit selections const tempUnit = fromUnit.value; fromUnit.value = toUnit.value; toUnit.value = tempUnit; // Swap values if to input has a value if (toInput.value !== "") { const tempValue = fromInput.value; fromInput.value = toInput.value; toInput.value = tempValue; } // Update conversion and formula convert(category); updateFormula(category); // Add animation to the formula display formulaDisplay.classList.add('pulse'); setTimeout(() => { formulaDisplay.classList.remove('pulse'); }, 2000); }); } // Show formula on hover if (formulaDisplay) { formulaDisplay.addEventListener('mouseenter', () => { formulaDisplay.classList.add('visible'); }); formulaDisplay.addEventListener('mouseleave', () => { formulaDisplay.classList.remove('visible'); }); } }); // Conversion function function convert(category) { const fromInput = document.getElementById(`from${capitalize(category)}`); const toInput = document.getElementById(`to${capitalize(category)}`); const fromUnit = document.getElementById(`from${capitalize(category)}Unit`); const toUnit = document.getElementById(`to${capitalize(category)}Unit`); if (!fromInput || !fromUnit || !toInput || !toUnit) return; const fromValue = parseFloat(fromInput.value); if (isNaN(fromValue)) { toInput.value = ""; return; } const from = fromUnit.value; const to = toUnit.value; // Handle temperature conversion separately (non-linear) if (category === 'temperature') { toInput.value = convertTemperature(fromValue, from, to); return; } // For linear conversions const factors = conversionFactors[category]; if (!factors) return; // Convert to base unit then to target unit const baseValue = fromValue * factors[from]; const result = baseValue / factors[to]; // Format the result based on precision needed toInput.value = formatResult(result); } // Temperature conversion (special case - non-linear) function convertTemperature(value, fromUnit, toUnit) { let result; // Convert to Celsius first let celsius; if (fromUnit === 'celsius') { celsius = value; } else if (fromUnit === 'fahrenheit') { celsius = (value - 32) * 5/9; } else if (fromUnit === 'kelvin') { celsius = value - 273.15; } // Convert from Celsius to target unit if (toUnit === 'celsius') { result = celsius; } else if (toUnit === 'fahrenheit') { result = celsius * 9/5 + 32; } else if (toUnit === 'kelvin') { result = celsius + 273.15; } return formatResult(result); } // Format result with appropriate precision function formatResult(value) { if (Math.abs(value) < 0.000001) return "0"; if (Math.abs(value) >= 1000000) return value.toExponential(6); // Dynamic precision based on value if (Math.abs(value) < 0.1) return value.toPrecision(6); if (Math.abs(value) < 1) return value.toFixed(6); if (Math.abs(value) < 10) return value.toFixed(5); if (Math.abs(value) < 100) return value.toFixed(4); if (Math.abs(value) < 1000) return value.toFixed(3); if (Math.abs(value) < 10000) return value.toFixed(2); return value.toFixed(2); } // Update formula display function updateFormula(category) { const formulaDisplay = document.getElementById(`${category}Formula`); if (!formulaDisplay) return; const fromUnit = document.getElementById(`from${capitalize(category)}Unit`); const toUnit = document.getElementById(`to${capitalize(category)}Unit`); if (!fromUnit || !toUnit) return; const from = fromUnit.value; const to = toUnit.value; // Handle temperature formulas if (category === 'temperature') { let formula = ''; if (from === 'celsius' && to === 'fahrenheit') { formula = '°C × 9/5 + 32 = °F'; } else if (from === 'fahrenheit' && to === 'celsius') { formula = '(°F - 32) × 5/9 = °C'; } else if (from === 'celsius' && to === 'kelvin') { formula = '°C + 273.15 = K'; } else if (from === 'kelvin' && to === 'celsius') { formula = 'K - 273.15 = °C'; } else if (from === 'fahrenheit' && to === 'kelvin') { formula = '(°F - 32) × 5/9 + 273.15 = K'; } else if (from === 'kelvin' && to === 'fahrenheit') { formula = '(K - 273.15) × 9/5 + 32 = °F'; } else { formula = `${from} = ${to}`; } formulaDisplay.textContent = formula; return; } // For linear conversions const factors = conversionFactors[category]; if (!factors) return; // Calculate the conversion factor const conversionFactor = factors[from] / factors[to]; // Format the factor for display let formattedFactor; if (conversionFactor === 1) { formattedFactor = "1"; } else if (conversionFactor < 0.000001 || conversionFactor > 1000000) { formattedFactor = conversionFactor.toExponential(6); } else if (conversionFactor < 0.001) { formattedFactor = conversionFactor.toPrecision(6); } else if (conversionFactor < 1) { formattedFactor = conversionFactor.toFixed(6); } else if (conversionFactor < 10) { formattedFactor = conversionFactor.toFixed(3); } else { formattedFactor = conversionFactor.toFixed(2); } formulaDisplay.textContent = `1 ${unitDisplayNames[from]} = ${formattedFactor} ${unitDisplayNames[to]}`; } // Capitalize first letter (helper function) function capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1); } // Initialize the first category's formula updateFormula('length'); // Perform initial conversion convert('length'); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Splitz: The Fun Tip Calculator</title> <style> :root { --primary: #FF6B6B; --secondary: #4ECDC4; --accent: #FFE66D; --dark: #292F36; --light: #F7FFF7; } * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Poppins', sans-serif; } body { background-color: var(--light); height: 100vh; display: flex; justify-content: center; align-items: center; overflow: hidden; color: var(--dark); } .container { width: 100%; max-width: 680px; height: 680px; background: linear-gradient(135deg, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.4)); border-radius: 24px; backdrop-filter: blur(10px); box-shadow: 0 8px 32px rgba(31, 38, 135, 0.1); padding: 2rem; position: relative; overflow: hidden; } h1 { color: var(--primary); font-size: 2.5rem; margin-bottom: 1rem; text-align: center; position: relative; } h1::after { content: ""; position: absolute; bottom: -10px; left: 50%; transform: translateX(-50%); width: 100px; height: 4px; background: var(--secondary); border-radius: 2px; } .tagline { text-align: center; margin-bottom: 2rem; color: var(--dark); font-weight: 500; } .calculator { background-color: white; border-radius: 16px; padding: 1.5rem; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05); } .input-group { margin-bottom: 1.5rem; position: relative; } label { display: block; margin-bottom: 0.5rem; font-weight: 600; color: var(--dark); } input { width: 100%; padding: 0.8rem 1rem; border: 2px solid #e1e1e1; border-radius: 12px; font-size: 1.1rem; transition: all 0.3s ease; } input:focus { outline: none; border-color: var(--secondary); box-shadow: 0 0 0 3px rgba(78, 205, 196, 0.2); } .currency-symbol { position: absolute; left: 12px; top: 38px; font-weight: bold; color: var(--dark); } .with-symbol input { padding-left: 28px; } .tip-options { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; margin-bottom: 1.5rem; } .tip-btn { flex: 1; min-width: 80px; padding: 0.8rem 0.5rem; background-color: #f0f0f0; border: none; border-radius: 12px; cursor: pointer; font-weight: 600; transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); } .tip-btn:hover { transform: translateY(-3px); background-color: var(--accent); color: var(--dark); } .tip-btn.active { background-color: var(--secondary); color: white; transform: translateY(-3px); box-shadow: 0 4px 12px rgba(78, 205, 196, 0.3); } .custom-tip { width: 100%; } .custom-tip input { padding-right: 30px; } .percent-symbol { position: absolute; right: 12px; top: 38px; font-weight: bold; color: var(--dark); } .people-counter { display: flex; align-items: center; justify-content: space-between; background: #f9f9f9; border-radius: 12px; padding: 0.5rem; margin-bottom: 1.5rem; } .counter-btn { width: 40px; height: 40px; border-radius: 50%; border: none; background-color: var(--primary); color: white; font-size: 1.5rem; cursor: pointer; transition: all 0.2s ease; display: flex; justify-content: center; align-items: center; } .counter-btn:hover { background-color: #ff5252; transform: scale(1.1); } .counter-btn:disabled { background-color: #ccc; cursor: not-allowed; transform: scale(1); } .people-count { font-size: 1.5rem; font-weight: bold; min-width: 40px; text-align: center; } .results { background: linear-gradient(135deg, var(--primary), #FF9E9E); border-radius: 16px; padding: 1.5rem; color: white; margin-top: 1rem; box-shadow: 0 8px 24px rgba(255, 107, 107, 0.3); transform: translateY(0); transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); } .result-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; } .result-label { font-size: 1rem; } .result-amount { font-size: 1.8rem; font-weight: bold; } .per-person { font-size: 0.85rem; opacity: 0.9; } .reset-btn { width: 100%; padding: 1rem; background-color: var(--accent); border: none; border-radius: 12px; font-weight: bold; cursor: pointer; transition: all 0.3s ease; margin-top: 1rem; font-size: 1.1rem; color: var(--dark); box-shadow: 0 4px 12px rgba(255, 230, 109, 0.3); } .reset-btn:hover { background-color: #FFD800; transform: translateY(-3px); } .bubble { position: absolute; background: linear-gradient(135deg, rgba(255, 107, 107, 0.2), rgba(78, 205, 196, 0.2)); border-radius: 50%; animation: float 8s infinite ease-in-out; } .bubble:nth-child(1) { width: 100px; height: 100px; top: -50px; left: 10%; animation-delay: 0s; } .bubble:nth-child(2) { width: 140px; height: 140px; bottom: -70px; right: 10%; animation-delay: 2s; } .bubble:nth-child(3) { width: 80px; height: 80px; top: 50%; right: -40px; animation-delay: 4s; } .bubble:nth-child(4) { width: 60px; height: 60px; bottom: 30%; left: -30px; animation-delay: 6s; } @keyframes float { 0% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-20px) rotate(10deg); } 100% { transform: translateY(0) rotate(0deg); } } .celebrate { animation: celebrate 0.8s ease-out; } @keyframes celebrate { 0% { transform: translateY(20px); opacity: 0; } 50% { transform: translateY(-10px); } 100% { transform: translateY(0); opacity: 1; } } .tip-action { text-align: center; margin-top: 1.5rem; font-size: 0.9rem; color: #666; } @media (max-width: 600px) { .container { height: auto; padding: 1.5rem; border-radius: 16px; } h1 { font-size: 2rem; } .tip-options { flex-wrap: wrap; } .tip-btn { min-width: 60px; padding: 0.7rem 0.5rem; } .result-amount { font-size: 1.5rem; } } </style> </head> <body> <div class="container"> <div class="bubble"></div> <div class="bubble"></div> <div class="bubble"></div> <div class="bubble"></div> <h1>Splitz</h1> <p class="tagline">Split bills, not friendships! 🍕</p> <div class="calculator"> <div class="input-group with-symbol"> <label for="bill-amount">Bill Amount</label> <span class="currency-symbol">$</span> <input type="number" id="bill-amount" placeholder="0.00" min="0" step="0.01"> </div> <div class="input-group"> <label>Select Tip %</label> <div class="tip-options"> <button class="tip-btn" data-tip="15">15%</button> <button class="tip-btn" data-tip="18">18%</button> <button class="tip-btn" data-tip="20">20%</button> <button class="tip-btn" data-tip="25">25%</button> </div> <div class="custom-tip"> <input type="number" id="custom-tip" placeholder="Custom %" min="0" max="100"> <span class="percent-symbol">%</span> </div> </div> <div class="input-group"> <label>Number of People</label> <div class="people-counter"> <button class="counter-btn" id="decrease-people">-</button> <span class="people-count" id="people-count">1</span> <button class="counter-btn" id="increase-people">+</button> </div> </div> <div class="results"> <div class="result-row"> <div> <div class="result-label">Tip Amount</div> <div class="per-person">per person</div> </div> <div class="result-amount" id="tip-per-person">$0.00</div> </div> <div class="result-row"> <div> <div class="result-label">Total Amount</div> <div class="per-person">per person</div> </div> <div class="result-amount" id="total-per-person">$0.00</div> </div> </div> <button class="reset-btn" id="reset-btn">Reset</button> <div class="tip-action" id="tip-action">Enter bill amount to get started</div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Get DOM elements const billInput = document.getElementById('bill-amount'); const customTipInput = document.getElementById('custom-tip'); const tipButtons = document.querySelectorAll('.tip-btn'); const decreaseBtn = document.getElementById('decrease-people'); const increaseBtn = document.getElementById('increase-people'); const peopleCount = document.getElementById('people-count'); const tipPerPerson = document.getElementById('tip-per-person'); const totalPerPerson = document.getElementById('total-per-person'); const resetBtn = document.getElementById('reset-btn'); const results = document.querySelector('.results'); const tipAction = document.getElementById('tip-action'); // Initial state let bill = 0; let tipPercentage = 0; let people = 1; let activeTipButton = null; // Functions function calculateTip() { if (bill === 0) { tipAction.textContent = "Enter bill amount to get started"; tipPerPerson.textContent = "$0.00"; totalPerPerson.textContent = "$0.00"; return; } if (tipPercentage === 0) { tipAction.textContent = "Select a tip percentage"; } else if (people === 0) { tipAction.textContent = "Can't split by zero people!"; } else { const tipAmount = bill * (tipPercentage / 100); const tipPerPersonValue = tipAmount / people; const totalPerPersonValue = (bill + tipAmount) / people; tipPerPerson.textContent = `$${tipPerPersonValue.toFixed(2)}`; totalPerPerson.textContent = `$${totalPerPersonValue.toFixed(2)}`; // Show some encouragement based on tip percentage if (tipPercentage < 15) { tipAction.textContent = "A bit light on the tip there..."; } else if (tipPercentage >= 15 && tipPercentage < 20) { tipAction.textContent = "Thanks for being a good tipper!"; } else if (tipPercentage >= 20 && tipPercentage < 25) { tipAction.textContent = "Wow, very generous tip!"; } else { tipAction.textContent = "Incredible! The staff will love you! 👏"; } // Add celebrate animation results.classList.add('celebrate'); setTimeout(() => { results.classList.remove('celebrate'); }, 800); } } function reset() { bill = 0; tipPercentage = 0; people = 1; billInput.value = ''; customTipInput.value = ''; peopleCount.textContent = '1'; if (activeTipButton) { activeTipButton.classList.remove('active'); activeTipButton = null; } decreaseBtn.disabled = true; tipPerPerson.textContent = "$0.00"; totalPerPerson.textContent = "$0.00"; tipAction.textContent = "Enter bill amount to get started"; } // Event Listeners billInput.addEventListener('input', function() { bill = parseFloat(this.value) || 0; calculateTip(); }); tipButtons.forEach(btn => { btn.addEventListener('click', function() { if (activeTipButton) { activeTipButton.classList.remove('active'); } activeTipButton = this; this.classList.add('active'); tipPercentage = parseFloat(this.dataset.tip); customTipInput.value = ''; calculateTip(); }); }); customTipInput.addEventListener('input', function() { if (this.value) { tipPercentage = parseFloat(this.value) || 0; if (activeTipButton) { activeTipButton.classList.remove('active'); activeTipButton = null; } calculateTip(); } else { tipPercentage = 0; calculateTip(); } }); decreaseBtn.addEventListener('click', function() { if (people > 1) { people--; peopleCount.textContent = people; if (people === 1) { decreaseBtn.disabled = true; } calculateTip(); } }); increaseBtn.addEventListener('click', function() { people++; peopleCount.textContent = people; decreaseBtn.disabled = false; calculateTip(); }); resetBtn.addEventListener('click', reset); // Initial setup decreaseBtn.disabled = true; reset(); // Add focus effects const inputs = document.querySelectorAll('input'); inputs.forEach(input => { input.addEventListener('focus', function() { this.parentElement.classList.add('focused'); }); input.addEventListener('blur', function() { this.parentElement.classList.remove('focused'); }); }); }); </script> </body> </html>
<html> <head> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } :root { --primary: #1f6feb; --primary-light: #58a6ff; --secondary: #8957e5; --accent: #3fb950; --text: #e6edf3; --text-secondary: #8b949e; --bg: #0d1117; --bg-secondary: #161b22; --bg-tertiary: #21262d; --border: #30363d; --shadow: rgba(0, 0, 0, 0.4); } body { background-color: var(--bg); color: var(--text); width: 100%; height: 100vh; display: flex; justify-content: center; align-items: center; padding: 20px; overflow-x: hidden; } .calculator-container { width: 100%; max-width: 700px; height: 700px; background-color: var(--bg-secondary); border-radius: 12px; box-shadow: 0 8px 24px var(--shadow); overflow: hidden; display: flex; flex-direction: column; } .header { padding: 20px; background: linear-gradient(135deg, var(--primary), var(--secondary)); position: relative; overflow: hidden; } .header h1 { font-size: 24px; margin-bottom: 8px; position: relative; z-index: 2; } .header p { font-size: 14px; opacity: 0.9; position: relative; z-index: 2; max-width: 80%; } .header::after { content: ''; position: absolute; top: -50px; right: -50px; width: 150px; height: 150px; background: rgba(255, 255, 255, 0.1); border-radius: 50%; z-index: 1; } .calculator-body { flex: 1; display: flex; flex-direction: column; padding: 20px; overflow-y: auto; } .input-section { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 25px; } @media (max-width: 580px) { .input-section { grid-template-columns: 1fr; } } .input-group { display: flex; flex-direction: column; gap: 8px; } .input-group label { font-size: 13px; color: var(--text-secondary); display: flex; justify-content: space-between; } .input-group label .info-tooltip { cursor: pointer; color: var(--primary-light); position: relative; } .input-group label .info-tooltip::before { content: attr(data-tooltip); position: absolute; bottom: 125%; right: 0; width: 220px; padding: 8px 12px; border-radius: 6px; background-color: var(--bg-tertiary); color: var(--text); font-size: 12px; opacity: 0; pointer-events: none; transform: translateY(10px); transition: all 0.2s ease; z-index: 10; line-height: 1.4; } .input-group label .info-tooltip:hover::before { opacity: 1; transform: translateY(0); } .input-control { position: relative; } .input-control input, .input-control select { width: 100%; padding: 12px; font-size: 15px; border: 1px solid var(--border); border-radius: 6px; background-color: var(--bg-tertiary); color: var(--text); transition: all 0.2s ease; outline: none; } .input-control input:focus, .input-control select:focus { border-color: var(--primary); box-shadow: 0 0 0 2px rgba(56, 139, 253, 0.25); } .input-control::after { content: attr(data-unit); position: absolute; right: 12px; top: 50%; transform: translateY(-50%); color: var(--text-secondary); pointer-events: none; } .slider-container { margin-top: 6px; width: 100%; position: relative; } .slider-container input[type="range"] { -webkit-appearance: none; width: 100%; height: 6px; border-radius: 3px; background: var(--bg-tertiary); outline: none; } .slider-container input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--primary); cursor: pointer; transition: all 0.15s ease; } .slider-container input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.2); background: var(--primary-light); } .results-section { background-color: var(--bg-tertiary); border-radius: 8px; padding: 20px; flex: 1; display: flex; flex-direction: column; overflow: hidden; } .results-tabs { display: flex; border-bottom: 1px solid var(--border); margin-bottom: 15px; } .tab { padding: 10px 16px; font-size: 14px; cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.2s ease; } .tab.active { color: var(--primary-light); border-bottom-color: var(--primary); } .tab:hover:not(.active) { color: var(--text); background-color: rgba(255, 255, 255, 0.05); } .tab-content { display: none; flex: 1; flex-direction: column; } .tab-content.active { display: flex; } .summary-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-bottom: 20px; } @media (max-width: 580px) { .summary-cards { grid-template-columns: 1fr; } } .summary-card { background-color: var(--bg-secondary); border-radius: 6px; padding: 15px; display: flex; flex-direction: column; gap: 8px; border: 1px solid var(--border); transition: transform 0.2s ease; } .summary-card:hover { transform: translateY(-2px); } .summary-card .card-title { font-size: 13px; color: var(--text-secondary); } .summary-card .card-value { font-size: 20px; font-weight: 600; } .summary-card .card-change { font-size: 13px; color: var(--accent); } .chart-container { flex: 1; min-height: 180px; position: relative; border-radius: 6px; overflow: hidden; } .chart-container canvas { width: 100%; height: 100%; } .chart-placeholder { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; background-color: var(--bg-secondary); font-size: 14px; color: var(--text-secondary); } .breakdown-list { overflow-y: auto; max-height: 100%; } .breakdown-item { display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); } .breakdown-item:last-child { border-bottom: none; } .breakdown-item .year { font-weight: 600; } .breakdown-item .values { display: flex; gap: 15px; } .breakdown-item .values span { min-width: 100px; text-align: right; } .breakdown-item:hover { background-color: rgba(255, 255, 255, 0.03); } .fine-print { font-size: 12px; color: var(--text-secondary); margin-top: 15px; text-align: center; } .toggle-detail-btn { margin-top: 15px; background-color: transparent; border: 1px solid var(--border); color: var(--text); padding: 8px 15px; border-radius: 6px; cursor: pointer; font-size: 13px; transition: all 0.2s ease; align-self: center; } .toggle-detail-btn:hover { background-color: var(--bg-tertiary); border-color: var(--primary-light); } .detail-section { max-height: 0; overflow: hidden; transition: max-height 0.5s ease; } .detail-section.open { max-height: 300px; margin-top: 15px; } .recalculate-btn { display: block; width: 100%; margin-top: 20px; padding: 12px; background: linear-gradient(135deg, var(--primary), var(--secondary)); border: none; border-radius: 6px; color: white; font-size: 16px; font-weight: 500; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 4px 12px rgba(56, 139, 253, 0.25); } .recalculate-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(56, 139, 253, 0.35); } /* Custom animation for the loading */ @keyframes shimmer { 0% { background-position: -468px 0; } 100% { background-position: 468px 0; } } .loading .summary-card .card-value, .loading .summary-card .card-change { background: linear-gradient(to right, var(--bg-tertiary) 8%, var(--bg-secondary) 18%, var(--bg-tertiary) 33%); background-size: 800px 104px; animation: shimmer 1.5s infinite linear; height: 20px; border-radius: 4px; color: transparent; } /* Tooltip animation */ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* Chart animation */ @keyframes chartGrow { from { height: 0; opacity: 0; } to { height: 100%; opacity: 1; } } .chart-container.animate { animation: chartGrow 0.8s ease-out forwards; } /* Confetti effect */ .confetti { position: fixed; width: 10px; height: 10px; background-color: #f00; z-index: 1000; opacity: 0; pointer-events: none; } </style> </head> <body> <div class="calculator-container"> <div class="header"> <h1>Investment Returns Calculator</h1> <p>Visualize how your investments could grow over time with our data-driven calculator.</p> </div> <div class="calculator-body"> <div class="input-section"> <div class="input-group"> <label for="initial-investment"> Initial Investment <span class="info-tooltip" data-tooltip="The amount you start investing with today. This is your principal amount.">ⓘ</span> </label> <div class="input-control" data-unit="$"> <input type="number" id="initial-investment" value="10000" min="0" max="1000000"> </div> </div> <div class="input-group"> <label for="contribution"> Monthly Contribution <span class="info-tooltip" data-tooltip="Regular additional investments made on a monthly basis. Consistency leads to significant growth over time.">ⓘ</span> </label> <div class="input-control" data-unit="$"> <input type="number" id="contribution" value="500" min="0" max="10000"> </div> </div> <div class="input-group"> <label for="time-horizon"> Time Horizon <span class="info-tooltip" data-tooltip="The length of time you plan to hold your investment. Longer periods typically allow for more compound growth.">ⓘ</span> </label> <div class="input-control" data-unit="years"> <input type="number" id="time-horizon" value="20" min="1" max="50"> </div> <div class="slider-container"> <input type="range" id="time-horizon-slider" min="1" max="50" value="20"> </div> </div> <div class="input-group"> <label for="return-rate"> Expected Annual Return <span class="info-tooltip" data-tooltip="The yearly rate of return on your investment. Historical average for S&P 500 is around 10% before inflation.">ⓘ</span> </label> <div class="input-control" data-unit="%"> <input type="number" id="return-rate" value="8" min="0" max="30" step="0.1"> </div> <div class="slider-container"> <input type="range" id="return-rate-slider" min="0" max="30" value="8" step="0.1"> </div> </div> <div class="input-group"> <label for="risk-level"> Risk Profile <span class="info-tooltip" data-tooltip="Your investment risk tolerance. This affects the visualization of potential volatility but not the base calculation.">ⓘ</span> </label> <div class="input-control"> <select id="risk-level"> <option value="conservative">Conservative (4-6%)</option> <option value="moderate" selected>Moderate (6-8%)</option> <option value="aggressive">Aggressive (8-10%+)</option> </select> </div> </div> <div class="input-group"> <label for="inflation-rate"> Inflation Rate <span class="info-tooltip" data-tooltip="The average annual inflation rate reduces the future purchasing power of your investment. Historical average is around 2-3%.">ⓘ</span> </label> <div class="input-control" data-unit="%"> <input type="number" id="inflation-rate" value="2.5" min="0" max="10" step="0.1"> </div> </div> </div> <button class="recalculate-btn" id="calculate-btn">Calculate Returns</button> <div class="results-section"> <div class="results-tabs"> <div class="tab active" data-tab="summary">Summary</div> <div class="tab" data-tab="chart">Growth Chart</div> <div class="tab" data-tab="breakdown">Year-by-Year</div> </div> <div class="tab-content active" id="summary-tab"> <div class="summary-cards"> <div class="summary-card"> <div class="card-title">Final Balance</div> <div class="card-value">$0</div> <div class="card-change">+0%</div> </div> <div class="summary-card"> <div class="card-title">Total Contributions</div> <div class="card-value">$0</div> <div class="card-change">0% of final</div> </div> <div class="summary-card"> <div class="card-title">Total Growth</div> <div class="card-value">$0</div> <div class="card-change">0% of final</div> </div> </div> <div class="chart-container"> <div class="chart-placeholder">Investment breakdown will appear here after calculation</div> <canvas id="summary-chart"></canvas> </div> <button class="toggle-detail-btn" id="toggle-detail">Show Additional Details</button> <div class="detail-section"> <div class="summary-cards"> <div class="summary-card"> <div class="card-title">Inflation Adjusted Value</div> <div class="card-value" id="inflation-adjusted-value">$0</div> <div class="card-change" id="inflation-impact">-0%</div> </div> <div class="summary-card"> <div class="card-title">Average Annual Return</div> <div class="card-value" id="average-annual-return">0%</div> <div class="card-change">Effective Rate</div> </div> <div class="summary-card"> <div class="card-title">Investment Multiplier</div> <div class="card-value" id="investment-multiplier">0×</div> <div class="card-change">Return on $1</div> </div> </div> </div> </div> <div class="tab-content" id="chart-tab"> <div class="chart-container"> <div class="chart-placeholder">Growth chart will appear here after calculation</div> <canvas id="growth-chart"></canvas> </div> </div> <div class="tab-content" id="breakdown-tab"> <div class="breakdown-list" id="breakdown-list"> <div class="chart-placeholder">Year-by-year breakdown will appear here after calculation</div> </div> </div> <div class="fine-print"> This calculator provides estimates based on the information you provide. Actual results may vary due to market conditions, fees, and other factors. </div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> document.addEventListener('DOMContentLoaded', function() { // Initialize elements const initialInvestmentInput = document.getElementById('initial-investment'); const contributionInput = document.getElementById('contribution'); const timeHorizonInput = document.getElementById('time-horizon'); const timeHorizonSlider = document.getElementById('time-horizon-slider'); const returnRateInput = document.getElementById('return-rate'); const returnRateSlider = document.getElementById('return-rate-slider'); const riskLevelSelect = document.getElementById('risk-level'); const inflationRateInput = document.getElementById('inflation-rate'); const calculateBtn = document.getElementById('calculate-btn'); const tabs = document.querySelectorAll('.tab'); const tabContents = document.querySelectorAll('.tab-content'); const toggleDetailBtn = document.getElementById('toggle-detail'); const detailSection = document.querySelector('.detail-section'); // Summary elements const finalBalanceValue = document.querySelector('.summary-card:nth-child(1) .card-value'); const finalBalanceChange = document.querySelector('.summary-card:nth-child(1) .card-change'); const totalContributionsValue = document.querySelector('.summary-card:nth-child(2) .card-value'); const totalContributionsChange = document.querySelector('.summary-card:nth-child(2) .card-change'); const totalGrowthValue = document.querySelector('.summary-card:nth-child(3) .card-value'); const totalGrowthChange = document.querySelector('.summary-card:nth-child(3) .card-change'); // Detail elements const inflationAdjustedValue = document.getElementById('inflation-adjusted-value'); const inflationImpact = document.getElementById('inflation-impact'); const averageAnnualReturn = document.getElementById('average-annual-return'); const investmentMultiplier = document.getElementById('investment-multiplier'); // Chart elements const summaryChartCanvas = document.getElementById('summary-chart'); const growthChartCanvas = document.getElementById('growth-chart'); const breakdownList = document.getElementById('breakdown-list'); // Initialize Chart.js instances let summaryChart = null; let growthChart = null; // Sync number inputs with sliders timeHorizonSlider.addEventListener('input', function() { timeHorizonInput.value = this.value; }); timeHorizonInput.addEventListener('input', function() { timeHorizonSlider.value = this.value; }); returnRateSlider.addEventListener('input', function() { returnRateInput.value = this.value; }); returnRateInput.addEventListener('input', function() { returnRateSlider.value = this.value; }); // Handle tab switching tabs.forEach(tab => { tab.addEventListener('click', function() { // Remove active class from all tabs tabs.forEach(t => t.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); // Add active class to clicked tab this.classList.add('active'); const tabId = `${this.dataset.tab}-tab`; document.getElementById(tabId).classList.add('active'); }); }); // Toggle detail section toggleDetailBtn.addEventListener('click', function() { detailSection.classList.toggle('open'); this.textContent = detailSection.classList.contains('open') ? 'Hide Additional Details' : 'Show Additional Details'; }); // Calculate investment returns calculateBtn.addEventListener('click', function() { // Get input values const initialInvestment = parseFloat(initialInvestmentInput.value); const monthlyContribution = parseFloat(contributionInput.value); const timeHorizon = parseInt(timeHorizonInput.value); const annualReturnRate = parseFloat(returnRateInput.value) / 100; const inflationRate = parseFloat(inflationRateInput.value) / 100; // Validate inputs if (isNaN(initialInvestment) || isNaN(monthlyContribution) || isNaN(timeHorizon) || isNaN(annualReturnRate) || isNaN(inflationRate)) { alert('Please enter valid numbers for all fields'); return; } // Show loading state document.querySelector('.summary-cards').classList.add('loading'); // Simulate processing time for better UX setTimeout(() => { calculateAndDisplayResults( initialInvestment, monthlyContribution, timeHorizon, annualReturnRate, inflationRate ); // Remove loading state document.querySelector('.summary-cards').classList.remove('loading'); // Create confetti effect createConfetti(); }, 800); }); function calculateAndDisplayResults(initialInvestment, monthlyContribution, timeHorizon, annualReturnRate, inflationRate) { // Monthly rate const monthlyRate = annualReturnRate / 12; // Calculate year-by-year results const yearlyData = []; let balance = initialInvestment; let totalContributions = initialInvestment; for (let year = 1; year <= timeHorizon; year++) { let yearStartBalance = balance; for (let month = 1; month <= 12; month++) { // Add monthly contribution balance += monthlyContribution; totalContributions += monthlyContribution; // Apply monthly growth balance *= (1 + monthlyRate); } yearlyData.push({ year, startBalance: yearStartBalance, endBalance: balance, annualContribution: monthlyContribution * 12, growth: balance - yearStartBalance - (monthlyContribution * 12) }); } // Final calculations const finalBalance = balance; const totalGrowth = finalBalance - totalContributions; const growthPercentage = (totalGrowth / totalContributions) * 100; // Calculate inflation-adjusted value const inflationAdjusted = finalBalance / Math.pow(1 + inflationRate, timeHorizon); const inflationImpactPercentage = ((finalBalance - inflationAdjusted) / finalBalance) * 100; // Calculate investment multiplier const multiplier = finalBalance / initialInvestment; // Update summary cards finalBalanceValue.textContent = formatCurrency(finalBalance); finalBalanceChange.textContent = `+${growthPercentage.toFixed(1)}%`; totalContributionsValue.textContent = formatCurrency(totalContributions); totalContributionsChange.textContent = `${((totalContributions / finalBalance) * 100).toFixed(1)}% of final`; totalGrowthValue.textContent = formatCurrency(totalGrowth); totalGrowthChange.textContent = `${((totalGrowth / finalBalance) * 100).toFixed(1)}% of final`; // Update detail cards inflationAdjustedValue.textContent = formatCurrency(inflationAdjusted); inflationImpact.textContent = `-${inflationImpactPercentage.toFixed(1)}%`; averageAnnualReturn.textContent = `${(annualReturnRate * 100).toFixed(1)}%`; investmentMultiplier.textContent = `${multiplier.toFixed(2)}×`; // Update summary chart updateSummaryChart(totalContributions, totalGrowth); // Update growth chart updateGrowthChart(yearlyData); // Update breakdown list updateBreakdownList(yearlyData); } function updateSummaryChart(totalContributions, totalGrowth) { const ctx = summaryChartCanvas.getContext('2d'); if (summaryChart) { summaryChart.destroy(); } summaryChart = new Chart(ctx, { type: 'doughnut', data: { labels: ['Your Contributions', 'Investment Growth'], datasets: [{ data: [totalContributions, totalGrowth], backgroundColor: [ '#58a6ff', '#3fb950' ], borderColor: [ '#0d1117', '#0d1117' ], borderWidth: 2, hoverOffset: 5 }] }, options: { cutout: '65%', responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { color: '#e6edf3', font: { size: 12 }, padding: 20 } }, tooltip: { callbacks: { label: function(context) { const label = context.label || ''; const value = context.raw; const percentage = ((value / (totalContributions + totalGrowth)) * 100).toFixed(1); return `${label}: ${formatCurrency(value)} (${percentage}%)`; } } } }, animation: { animateRotate: true, animateScale: true, duration: 1500, easing: 'easeOutQuart' } } }); // Remove placeholder document.querySelector('#summary-tab .chart-placeholder').style.display = 'none'; } function updateGrowthChart(yearlyData) { const ctx = growthChartCanvas.getContext('2d'); // Extract data const labels = yearlyData.map(data => `Year ${data.year}`); const balanceData = yearlyData.map(data => data.endBalance); const contributionData = yearlyData.map((data, index) => { if (index === 0) { return yearlyData[0].startBalance + yearlyData[0].annualContribution; } else { return yearlyData[index-1].endBalance + data.annualContribution - yearlyData[index-1].endBalance; } }); if (growthChart) { growthChart.destroy(); } growthChart = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [ { label: 'Balance', data: balanceData, borderColor: '#58a6ff', backgroundColor: 'rgba(88, 166, 255, 0.1)', fill: true, tension: 0.4, borderWidth: 2 }, { label: 'Contributions', data: contributionData, borderColor: '#8957e5', backgroundColor: 'rgba(137, 87, 229, 0.1)', fill: true, tension: 0.4, borderWidth: 2, borderDash: [5, 5] } ] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, grid: { color: 'rgba(255, 255, 255, 0.05)' }, ticks: { color: '#8b949e', callback: function(value) { if (value >= 1000000) { return '$' + (value / 1000000).toFixed(1) + 'M'; } else if (value >= 1000) { return '$' + (value / 1000).toFixed(0) + 'K'; } return '$' + value; } } }, x: { grid: { color: 'rgba(255, 255, 255, 0.05)' }, ticks: { color: '#8b949e', maxRotation: 45, minRotation: 45 } } }, plugins: { legend: { labels: { color: '#e6edf3', font: { size: 12 } } }, tooltip: { mode: 'index', intersect: false, callbacks: { label: function(context) { let label = context.dataset.label || ''; if (label) { label += ': '; } if (context.parsed.y !== null) { label += formatCurrency(context.parsed.y); } return label; } } } }, animation: { duration: 1500, easing: 'easeOutQuart' } } }); // Remove placeholder document.querySelector('#chart-tab .chart-placeholder').style.display = 'none'; } function updateBreakdownList(yearlyData) { // Clear previous content breakdownList.innerHTML = ''; // Create header const header = document.createElement('div'); header.className = 'breakdown-item'; header.innerHTML = ` <div class="year">Year</div> <div class="values"> <span>Starting</span> <span>Contributions</span> <span>Growth</span> <span>Ending</span> </div> `; breakdownList.appendChild(header); // Add year-by-year breakdown yearlyData.forEach((data, index) => { const item = document.createElement('div'); item.className = 'breakdown-item'; item.innerHTML = ` <div class="year">Year ${data.year}</div> <div class="values"> <span>${formatCurrency(data.startBalance)}</span> <span>${formatCurrency(data.annualContribution)}</span> <span>${formatCurrency(data.growth)}</span> <span>${formatCurrency(data.endBalance)}</span> </div> `; // Add with delay for animation effect setTimeout(() => { breakdownList.appendChild(item); item.style.opacity = 0; setTimeout(() => { item.style.opacity = 1; item.style.transition = 'opacity 0.3s ease'; }, 10); }, index * 50); }); } function formatCurrency(value) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(value); } function createConfetti() { const colors = ['#58a6ff', '#3fb950', '#8957e5', '#f78166', '#1f6feb']; const confettiCount = 100; for (let i = 0; i < confettiCount; i++) { const confetti = document.createElement('div'); confetti.className = 'confetti'; confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; confetti.style.left = Math.random() * 100 + '%'; confetti.style.top = -10 + 'px'; confetti.style.transform = 'rotate(' + (Math.random() * 360) + 'deg)'; document.body.appendChild(confetti); const size = Math.random() * 5 + 5; confetti.style.width = size + 'px'; confetti.style.height = size + 'px'; // Animate the confetti const animation = confetti.animate([ { top: '-10px', opacity: 1, transform: 'rotate(0deg) translateX(0)' }, { top: '700px', opacity: 0, transform: 'rotate(' + (Math.random() * 1000) + 'deg) translateX(' + (Math.random() * 200 - 100) + 'px)' } ], { duration: Math.random() * 3000 + 2000, easing: 'cubic-bezier(0,.9,.57,1)' }); animation.onfinish = function() { confetti.remove(); }; } } // Initial calculation on page load with default values window.onload = function() { calculateBtn.click(); }; }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>CurrencyWave Converter</title> <style> :root { --primary: #5B21B6; --secondary: #8B5CF6; --tertiary: #C4B5FD; --background: #F5F3FF; --white: #FFFFFF; --dark: #1E1B4B; --success: #10B981; --error: #EF4444; --neutral: #6B7280; --shadow: rgba(91, 33, 182, 0.1); --gradient: linear-gradient(135deg, var(--primary), var(--secondary)); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background-color: var(--background); display: flex; align-items: center; justify-content: center; min-height: 700px; color: var(--dark); padding: 20px; } .container { width: 100%; max-width: 700px; background: var(--white); border-radius: 24px; box-shadow: 0 10px 30px var(--shadow); overflow: hidden; position: relative; } .header { background: var(--gradient); padding: 25px 30px; color: var(--white); position: relative; overflow: hidden; } .header h1 { font-size: 28px; font-weight: 700; margin-bottom: 8px; position: relative; z-index: 2; } .header p { font-size: 16px; opacity: 0.9; position: relative; z-index: 2; max-width: 80%; } .pattern { position: absolute; top: 0; right: 0; height: 100%; width: 40%; opacity: 0.2; background-image: repeating-linear-gradient( 45deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1) 15px, transparent 15px, transparent 30px ); z-index: 1; } .converter { padding: 30px; } .conversion-row { display: flex; align-items: stretch; margin-bottom: 25px; position: relative; } .amount-field, .currency-select { flex: 1; position: relative; } .amount-field { margin-right: 15px; } label { display: block; font-size: 14px; font-weight: 600; margin-bottom: 8px; color: var(--dark); } input, select { width: 100%; padding: 15px; border: 2px solid var(--tertiary); border-radius: 12px; font-size: 16px; background-color: var(--white); transition: all 0.25s ease; color: var(--dark); } input:focus, select:focus { outline: none; border-color: var(--secondary); box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.2); } .currency-select { position: relative; } .select-wrapper { position: relative; } .flag-icon { width: 24px; height: 24px; position: absolute; left: 15px; top: 50%; transform: translateY(-50%); border-radius: 50%; object-fit: cover; border: 1px solid rgba(0, 0, 0, 0.1); } select { padding-left: 50px; appearance: none; background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="%235B21B6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>'); background-repeat: no-repeat; background-position: right 15px center; background-size: 16px; } .swap-btn { width: 50px; height: 50px; background: var(--gradient); border: none; border-radius: 50%; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); cursor: pointer; box-shadow: 0 4px 10px rgba(91, 33, 182, 0.3); z-index: 10; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; } .swap-btn:hover { transform: translate(-50%, -50%) scale(1.1); box-shadow: 0 6px 15px rgba(91, 33, 182, 0.4); } .swap-btn:focus { outline: none; } .swap-icon { width: 24px; height: 24px; fill: var(--white); transition: transform 0.3s ease; } .swap-btn:hover .swap-icon { transform: rotate(180deg); } .result { background: var(--background); padding: 20px; border-radius: 16px; margin-top: 10px; transition: all 0.3s ease; overflow: hidden; } .rate-info { display: flex; justify-content: space-between; align-items: center; margin-top: 15px; font-size: 14px; color: var(--neutral); } .pulse { animation: pulse 1.5s ease-in-out; } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } .spinner { width: 20px; height: 20px; border: 3px solid rgba(91, 33, 182, 0.3); border-radius: 50%; border-top-color: var(--primary); animation: spin 1s ease-in-out infinite; display: inline-block; margin-right: 10px; vertical-align: middle; } @keyframes spin { to { transform: rotate(360deg); } } .update-status { display: flex; align-items: center; font-size: 14px; color: var(--neutral); } .status-dot { width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; background-color: var(--success); } .popular-pairs { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 30px; } .pair-chip { background: var(--background); border: 1px solid var(--tertiary); border-radius: 100px; padding: 8px 15px; font-size: 14px; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; } .pair-chip:hover { background: var(--tertiary); transform: translateY(-2px); } .pair-chip img { width: 16px; height: 16px; border-radius: 50%; margin-right: 6px; border: 1px solid rgba(0, 0, 0, 0.1); } .footer { margin-top: 25px; padding-top: 20px; border-top: 1px solid var(--tertiary); display: flex; justify-content: space-between; align-items: center; font-size: 14px; color: var(--neutral); } .theme-toggle { background: none; border: none; cursor: pointer; display: flex; align-items: center; color: var(--primary); font-weight: 600; font-size: 14px; } .theme-icon { margin-right: 6px; width: 16px; height: 16px; } @media (max-width: 600px) { .header { padding: 20px; } .converter { padding: 20px; } .conversion-row { flex-direction: column; gap: 25px; margin-bottom: 60px; } .amount-field { margin-right: 0; } .swap-btn { top: 100%; } .popular-pairs { justify-content: center; } } /* Animation classes */ .fade-in { animation: fadeIn 0.5s ease-in-out; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .slide-up { animation: slideUp 0.5s ease-out; } @keyframes slideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } .notification { position: fixed; bottom: 20px; right: 20px; background: var(--dark); color: var(--white); padding: 15px 20px; border-radius: 12px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); display: flex; align-items: center; transform: translateX(120%); transition: transform 0.3s ease; z-index: 1000; } .notification.show { transform: translateX(0); } .notification-icon { margin-right: 10px; color: var(--success); } /* Loading skeleton styles */ .skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #f8f8f8 50%, #f0f0f0 75%); background-size: 200% 100%; animation: loading 1.5s infinite; border-radius: 12px; height: 24px; margin: 5px 0; } @keyframes loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } </style> </head> <body> <div class="container"> <div class="header"> <h1>CurrencyWave Converter</h1> <p>Convert between 170+ world currencies with live exchange rates</p> <div class="pattern"></div> </div> <div class="converter"> <div class="conversion-row"> <div class="amount-field"> <label for="amount">Amount</label> <input type="number" id="amount" placeholder="Enter amount" value="1" min="0" step="0.01"> </div> <button class="swap-btn" id="swapBtn" title="Swap currencies"> <svg class="swap-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path fill="currentColor" d="M8 5H4V1H2v4a2 2 0 0 0 2 2h4V5zm12-4h-4v2h4v4h2V3a2 2 0 0 0-2-2zM8 19H4a2 2 0 0 1-2-2v-4h2v4h4v2zm14-6h-2v4h-4v2h4a2 2 0 0 0 2-2v-4z"/> </svg> </button> <div class="currency-select"> <label for="fromCurrency">From</label> <div class="select-wrapper"> <img id="fromFlag" class="flag-icon" src="" alt="Flag"> <select id="fromCurrency"></select> </div> </div> </div> <div class="conversion-row"> <div class="amount-field"> <label for="result">Converted Amount</label> <input type="text" id="result" readonly> </div> <div class="currency-select"> <label for="toCurrency">To</label> <div class="select-wrapper"> <img id="toFlag" class="flag-icon" src="" alt="Flag"> <select id="toCurrency"></select> </div> </div> </div> <div class="result slide-up" id="resultCard"> <div class="rate-info"> <span id="conversionRate">1 USD = 0.85 EUR</span> <div class="update-status"> <span class="status-dot"></span> <span id="updateTime">Updated just now</span> </div> </div> </div> <div class="popular-pairs"> <h3 style="width: 100%; margin-bottom: 10px;">Popular pairs</h3> <div class="pair-chip" data-from="USD" data-to="EUR"> <img src="https://flagcdn.com/w20/us.png" alt="USD"> USD → EUR </div> <div class="pair-chip" data-from="EUR" data-to="GBP"> <img src="https://flagcdn.com/w20/eu.png" alt="EUR"> EUR → GBP </div> <div class="pair-chip" data-from="USD" data-to="JPY"> <img src="https://flagcdn.com/w20/us.png" alt="USD"> USD → JPY </div> <div class="pair-chip" data-from="GBP" data-to="USD"> <img src="https://flagcdn.com/w20/gb.png" alt="GBP"> GBP → USD </div> </div> <div class="footer"> <div>Exchange rates provided by <strong>Open Exchange Rates</strong></div> <button class="theme-toggle" id="themeToggle"> <svg class="theme-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> Light </button> </div> </div> </div> <div class="notification" id="notification"> <svg class="notification-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path> <polyline points="22 4 12 14.01 9 11.01"></polyline> </svg> <span id="notificationText">Rates updated successfully</span> </div> <script> document.addEventListener('DOMContentLoaded', () => { // Currency data with flags const currencies = [ { code: 'USD', name: 'US Dollar', flag: 'us' }, { code: 'EUR', name: 'Euro', flag: 'eu' }, { code: 'GBP', name: 'British Pound', flag: 'gb' }, { code: 'JPY', name: 'Japanese Yen', flag: 'jp' }, { code: 'AUD', name: 'Australian Dollar', flag: 'au' }, { code: 'CAD', name: 'Canadian Dollar', flag: 'ca' }, { code: 'CHF', name: 'Swiss Franc', flag: 'ch' }, { code: 'CNY', name: 'Chinese Yuan', flag: 'cn' }, { code: 'INR', name: 'Indian Rupee', flag: 'in' }, { code: 'MXN', name: 'Mexican Peso', flag: 'mx' }, { code: 'SGD', name: 'Singapore Dollar', flag: 'sg' }, { code: 'NZD', name: 'New Zealand Dollar', flag: 'nz' }, { code: 'BRL', name: 'Brazilian Real', flag: 'br' }, { code: 'RUB', name: 'Russian Ruble', flag: 'ru' }, { code: 'ZAR', name: 'South African Rand', flag: 'za' } ]; // DOM elements const fromCurrency = document.getElementById('fromCurrency'); const toCurrency = document.getElementById('toCurrency'); const amount = document.getElementById('amount'); const result = document.getElementById('result'); const swapBtn = document.getElementById('swapBtn'); const fromFlag = document.getElementById('fromFlag'); const toFlag = document.getElementById('toFlag'); const conversionRate = document.getElementById('conversionRate'); const updateTime = document.getElementById('updateTime'); const resultCard = document.getElementById('resultCard'); const pairChips = document.querySelectorAll('.pair-chip'); const themeToggle = document.getElementById('themeToggle'); const notification = document.getElementById('notification'); const notificationText = document.getElementById('notificationText'); // Mock exchange rates const exchangeRates = { USD: 1, EUR: 0.93, GBP: 0.78, JPY: 149.65, AUD: 1.52, CAD: 1.35, CHF: 0.88, CNY: 7.18, INR: 83.12, MXN: 16.75, SGD: 1.34, NZD: 1.64, BRL: 5.02, RUB: 90.85, ZAR: 18.42 }; // Populate currency dropdowns currencies.forEach(currency => { const fromOption = document.createElement('option'); fromOption.value = currency.code; fromOption.textContent = `${currency.code} - ${currency.name}`; fromCurrency.appendChild(fromOption); const toOption = document.createElement('option'); toOption.value = currency.code; toOption.textContent = `${currency.code} - ${currency.name}`; toCurrency.appendChild(toOption); }); // Set default values fromCurrency.value = 'USD'; toCurrency.value = 'EUR'; fromFlag.src = `https://flagcdn.com/w20/${getCurrencyByCode('USD').flag}.png`; toFlag.src = `https://flagcdn.com/w20/${getCurrencyByCode('EUR').flag}.png`; // Convert currency when inputs change amount.addEventListener('input', convertCurrency); fromCurrency.addEventListener('change', () => { updateFlag(fromCurrency, fromFlag); convertCurrency(); showUpdatingStatus(); }); toCurrency.addEventListener('change', () => { updateFlag(toCurrency, toFlag); convertCurrency(); showUpdatingStatus(); }); // Swap currencies swapBtn.addEventListener('click', () => { const temp = fromCurrency.value; fromCurrency.value = toCurrency.value; toCurrency.value = temp; updateFlag(fromCurrency, fromFlag); updateFlag(toCurrency, toFlag); resultCard.classList.add('pulse'); setTimeout(() => { resultCard.classList.remove('pulse'); }, 1500); convertCurrency(); showUpdatingStatus(); showNotification('Currencies swapped'); }); // Quick selection of popular pairs pairChips.forEach(chip => { chip.addEventListener('click', () => { fromCurrency.value = chip.dataset.from; toCurrency.value = chip.dataset.to; updateFlag(fromCurrency, fromFlag); updateFlag(toCurrency, toFlag); chip.style.transform = 'scale(1.05)'; setTimeout(() => { chip.style.transform = ''; }, 200); convertCurrency(); showUpdatingStatus(); }); }); // Theme toggle (light/dark mode placeholder) themeToggle.addEventListener('click', () => { showNotification('Theme feature coming soon!'); }); // Helper function to get currency data by code function getCurrencyByCode(code) { return currencies.find(c => c.code === code); } // Update flag image when currency changes function updateFlag(selectElement, flagElement) { const currency = getCurrencyByCode(selectElement.value); flagElement.src = `https://flagcdn.com/w20/${currency.flag}.png`; flagElement.alt = currency.code; } // Simulate a loading/updating state function showUpdatingStatus() { updateTime.innerHTML = '<span class="spinner"></span> Updating...'; setTimeout(() => { updateTime.innerHTML = 'Updated just now'; }, 1000); } // Show notification function showNotification(message) { notificationText.textContent = message; notification.classList.add('show'); setTimeout(() => { notification.classList.remove('show'); }, 3000); } // Convert currency and update UI function convertCurrency() { const fromRate = exchangeRates[fromCurrency.value]; const toRate = exchangeRates[toCurrency.value]; const amountValue = parseFloat(amount.value); if (isNaN(amountValue)) { result.value = ''; return; } const convertedAmount = (amountValue / fromRate) * toRate; result.value = convertedAmount.toFixed(2); // Update the conversion rate text const rate = (1 / fromRate) * toRate; conversionRate.textContent = `1 ${fromCurrency.value} = ${rate.toFixed(4)} ${toCurrency.value}`; } // Initial conversion convertCurrency(); }); </script> </body> </html>
<html> <head> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } :root { --primary: #6A8D73; --secondary: #DFE6DA; --accent: #F2B880; --danger: #E8998D; --text: #3A3E3B; --light-text: #6C7C7C; --background: #F8F9F6; --card: #FFFFFF; --shadow: rgba(0, 0, 0, 0.08); --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } body { background-color: var(--background); color: var(--text); height: 700px; width: 700px; overflow-x: hidden; margin: 0 auto; padding: 20px; display: flex; flex-direction: column; } .container { max-width: 700px; margin: 0 auto; width: 100%; overflow-y: auto; display: flex; flex-direction: column; flex: 1; } h1, h2, h3, h4 { margin-bottom: 10px; font-weight: 600; } h1 { font-size: 24px; margin-bottom: 5px; } .tagline { color: var(--light-text); font-size: 14px; margin-bottom: 20px; } .dashboard { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 16px; margin-bottom: 20px; } .summary-card { background: var(--card); border-radius: 12px; padding: 16px; box-shadow: 0 2px 8px var(--shadow); transition: var(--transition); display: flex; flex-direction: column; } .summary-card:hover { transform: translateY(-3px); box-shadow: 0 6px 12px var(--shadow); } .card-label { font-size: 12px; color: var(--light-text); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; } .card-value { font-size: 20px; font-weight: 700; margin-bottom: 8px; } .progress-wrapper { background-color: var(--secondary); height: 6px; border-radius: 3px; margin-top: auto; overflow: hidden; } .progress-bar { height: 100%; border-radius: 3px; width: 0%; transition: width 1s ease-in-out; } .income .progress-bar { background-color: var(--primary); } .expenses .progress-bar { background-color: var(--danger); } .savings .progress-bar { background-color: var(--accent); } .balance .progress-bar { background-color: var(--primary); } .tab-container { margin-top: 20px; margin-bottom: 20px; } .tab-nav { display: flex; border-bottom: 1px solid var(--secondary); margin-bottom: 20px; } .tab-button { padding: 12px 20px; background: none; border: none; color: var(--light-text); font-size: 14px; font-weight: 600; cursor: pointer; transition: var(--transition); position: relative; } .tab-button.active { color: var(--primary); } .tab-button.active::after { content: ''; position: absolute; bottom: -1px; left: 0; width: 100%; height: 2px; background-color: var(--primary); } .tab-content { display: none; } .tab-content.active { display: block; animation: fadeIn 0.3s ease; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .form-group { margin-bottom: 20px; } .form-row { display: grid; grid-template-columns: 1fr 120px; gap: 12px; align-items: center; margin-bottom: 12px; background: var(--card); padding: 12px 16px; border-radius: 8px; box-shadow: 0 2px 4px var(--shadow); transition: var(--transition); } .form-row:hover { transform: translateX(3px); box-shadow: 0 3px 6px var(--shadow); } .form-row label { font-size: 14px; font-weight: 500; } input, select { width: 100%; padding: 10px 12px; border: 1px solid var(--secondary); border-radius: 6px; font-size: 14px; transition: var(--transition); } input:focus, select:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 2px rgba(106, 141, 115, 0.2); } button { background-color: var(--primary); color: white; border: none; border-radius: 6px; padding: 12px 20px; font-size: 14px; font-weight: 600; cursor: pointer; transition: var(--transition); } button:hover { background-color: #5A7C63; transform: translateY(-2px); } .add-item-btn { display: flex; align-items: center; justify-content: center; gap: 8px; background-color: var(--secondary); color: var(--text); margin-top: 12px; width: 100%; } .add-item-btn:hover { background-color: #D0D9C8; } .budget-item { display: grid; grid-template-columns: 1fr 100px 50px; gap: 12px; align-items: center; padding: 14px 16px; background: var(--card); border-radius: 8px; margin-bottom: 10px; box-shadow: 0 2px 4px var(--shadow); transition: var(--transition); position: relative; overflow: hidden; } .budget-item:hover { transform: translateY(-2px); box-shadow: 0 4px 8px var(--shadow); } .budget-item-category { font-weight: 500; } .budget-item-amount { font-weight: 600; text-align: right; } .budget-item.income { border-left: 3px solid var(--primary); } .budget-item.expense { border-left: 3px solid var(--danger); } .delete-btn { background: none; border: none; color: var(--light-text); cursor: pointer; padding: 5px; transition: var(--transition); } .delete-btn:hover { color: var(--danger); transform: scale(1.2); } .chart-container { background: var(--card); border-radius: 12px; padding: 20px; margin-top: 20px; box-shadow: 0 2px 8px var(--shadow); } .bar-chart { display: flex; height: 200px; align-items: flex-end; gap: 20px; margin-top: 20px; } .bar { flex: 1; background-color: var(--secondary); border-radius: 6px 6px 0 0; position: relative; transition: height 1s cubic-bezier(0.34, 1.56, 0.64, 1); min-height: 4px; } .bar-income { background-color: var(--primary); } .bar-expenses { background-color: var(--danger); } .bar-savings { background-color: var(--accent); } .bar-label { position: absolute; bottom: -25px; left: 0; right: 0; text-align: center; font-size: 12px; color: var(--light-text); } .bar-value { position: absolute; top: -25px; left: 0; right: 0; text-align: center; font-size: 12px; font-weight: 600; } .budget-items-list { max-height: 250px; overflow-y: auto; margin-bottom: 20px; padding-right: 5px; } .budget-items-list::-webkit-scrollbar { width: 5px; } .budget-items-list::-webkit-scrollbar-track { background: var(--secondary); border-radius: 10px; } .budget-items-list::-webkit-scrollbar-thumb { background: var(--primary); border-radius: 10px; } .empty-state { text-align: center; padding: 20px; color: var(--light-text); font-size: 14px; } .confetti { position: absolute; width: 10px; height: 10px; background-color: var(--accent); border-radius: 50%; pointer-events: none; opacity: 0; } @keyframes confetti-fall { 0% { transform: translateY(0) rotate(0deg); opacity: 1; } 100% { transform: translateY(100px) rotate(360deg); opacity: 0; } } .tooltip { position: absolute; background: var(--text); color: white; padding: 8px 12px; border-radius: 6px; font-size: 12px; pointer-events: none; opacity: 0; transition: opacity 0.2s; z-index: 100; white-space: nowrap; } .tooltip::before { content: ''; position: absolute; bottom: -5px; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid var(--text); } @media (max-width: 600px) { .dashboard { grid-template-columns: 1fr 1fr; } .form-row { grid-template-columns: 1fr; } .budget-item { grid-template-columns: 1fr 80px 40px; font-size: 14px; } .bar-chart { height: 150px; } } </style> </head> <body> <div class="container"> <h1>Mindful Money Manager</h1> <p class="tagline">Track, plan, and watch your finances flourish</p> <div class="dashboard"> <div class="summary-card income"> <div class="card-label">Monthly Income</div> <div class="card-value">$<span id="income-value">0</span></div> <div class="progress-wrapper"> <div class="progress-bar" id="income-progress"></div> </div> </div> <div class="summary-card expenses"> <div class="card-label">Monthly Expenses</div> <div class="card-value">$<span id="expenses-value">0</span></div> <div class="progress-wrapper"> <div class="progress-bar" id="expenses-progress"></div> </div> </div> <div class="summary-card savings"> <div class="card-label">Savings</div> <div class="card-value">$<span id="savings-value">0</span></div> <div class="progress-wrapper"> <div class="progress-bar" id="savings-progress"></div> </div> </div> <div class="summary-card balance"> <div class="card-label">Budget Health</div> <div class="card-value"><span id="budget-health">Calculating...</span></div> <div class="progress-wrapper"> <div class="progress-bar" id="balance-progress"></div> </div> </div> </div> <div class="chart-container"> <h3>Monthly Overview</h3> <div class="bar-chart"> <div class="bar bar-income" id="chart-income"> <div class="bar-value">$0</div> <div class="bar-label">Income</div> </div> <div class="bar bar-expenses" id="chart-expenses"> <div class="bar-value">$0</div> <div class="bar-label">Expenses</div> </div> <div class="bar bar-savings" id="chart-savings"> <div class="bar-value">$0</div> <div class="bar-label">Savings</div> </div> </div> </div> <div class="tab-container"> <div class="tab-nav"> <button class="tab-button active" data-tab="income">Income</button> <button class="tab-button" data-tab="expenses">Expenses</button> <button class="tab-button" data-tab="summary">Budget Review</button> </div> <div class="tab-content active" id="income-tab"> <div class="form-group"> <div class="form-row"> <label for="income-source">Source of Income</label> <input type="text" id="income-source" placeholder="e.g. Salary, Freelance"> </div> <div class="form-row"> <label for="income-amount">Amount ($)</label> <input type="number" id="income-amount" placeholder="0.00" min="0"> </div> <button id="add-income-btn">Add Income Source</button> </div> <h3>Income Sources</h3> <div class="budget-items-list" id="income-items"> <div class="empty-state">No income sources added yet. Add your first source above.</div> </div> </div> <div class="tab-content" id="expenses-tab"> <div class="form-group"> <div class="form-row"> <label for="expense-category">Expense Category</label> <select id="expense-category"> <option value="Housing">Housing</option> <option value="Food">Food & Dining</option> <option value="Transportation">Transportation</option> <option value="Utilities">Utilities</option> <option value="Healthcare">Healthcare</option> <option value="Entertainment">Entertainment</option> <option value="Personal">Personal Care</option> <option value="Savings">Savings</option> <option value="Debt">Debt Payments</option> <option value="Other">Other</option> </select> </div> <div class="form-row"> <label for="expense-description">Description</label> <input type="text" id="expense-description" placeholder="e.g. Rent, Groceries"> </div> <div class="form-row"> <label for="expense-amount">Amount ($)</label> <input type="number" id="expense-amount" placeholder="0.00" min="0"> </div> <button id="add-expense-btn">Add Expense</button> </div> <h3>Monthly Expenses</h3> <div class="budget-items-list" id="expense-items"> <div class="empty-state">No expenses added yet. Add your first expense above.</div> </div> </div> <div class="tab-content" id="summary-tab"> <h3>Budget Analysis</h3> <div class="budget-items-list" id="summary-items"> <div class="empty-state">Add income and expenses to see your budget summary.</div> </div> <h3>Insights</h3> <div id="budget-insights" class="summary-card"> <p>Your budget insights will appear here once you add income and expenses.</p> </div> </div> </div> </div> <div class="tooltip" id="tooltip"></div> <script> // Budget data structure let budgetData = { income: [], expenses: [], totalIncome: 0, totalExpenses: 0, savings: 0 }; // DOM elements const incomeTab = document.getElementById('income-tab'); const expensesTab = document.getElementById('expenses-tab'); const summaryTab = document.getElementById('summary-tab'); const tabButtons = document.querySelectorAll('.tab-button'); const incomeItemsList = document.getElementById('income-items'); const expenseItemsList = document.getElementById('expense-items'); const summaryItemsList = document.getElementById('summary-items'); const budgetInsights = document.getElementById('budget-insights'); // Add event listeners to tab buttons tabButtons.forEach(button => { button.addEventListener('click', () => { // Toggle active class tabButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); // Show the corresponding tab content const tabId = button.getAttribute('data-tab'); document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); document.getElementById(`${tabId}-tab`).classList.add('active'); }); }); // Add income document.getElementById('add-income-btn').addEventListener('click', function() { const source = document.getElementById('income-source').value.trim(); const amount = parseFloat(document.getElementById('income-amount').value); if (source && !isNaN(amount) && amount > 0) { // Add to budget data budgetData.income.push({ source, amount }); budgetData.totalIncome += amount; // Clear form document.getElementById('income-source').value = ''; document.getElementById('income-amount').value = ''; // Update display updateIncomeList(); updateSummary(); updateDashboard(); // Confetti for celebration createConfetti(event); } else { showTooltip(this, "Please enter a valid source and amount"); } }); // Add expense document.getElementById('add-expense-btn').addEventListener('click', function() { const category = document.getElementById('expense-category').value; const description = document.getElementById('expense-description').value.trim(); const amount = parseFloat(document.getElementById('expense-amount').value); if (description && !isNaN(amount) && amount > 0) { // Add to budget data budgetData.expenses.push({ category, description, amount }); budgetData.totalExpenses += amount; // Clear form document.getElementById('expense-description').value = ''; document.getElementById('expense-amount').value = ''; // Update display updateExpenseList(); updateSummary(); updateDashboard(); } else { showTooltip(this, "Please enter a valid description and amount"); } }); // Update income list function updateIncomeList() { if (budgetData.income.length === 0) { incomeItemsList.innerHTML = '<div class="empty-state">No income sources added yet. Add your first source above.</div>'; return; } incomeItemsList.innerHTML = ''; budgetData.income.forEach((item, index) => { const itemElement = document.createElement('div'); itemElement.className = 'budget-item income'; itemElement.innerHTML = ` <div class="budget-item-category">${item.source}</div> <div class="budget-item-amount">$${item.amount.toFixed(2)}</div> <button class="delete-btn" data-index="${index}" data-type="income">×</button> `; incomeItemsList.appendChild(itemElement); }); // Add delete event listeners document.querySelectorAll('.delete-btn[data-type="income"]').forEach(btn => { btn.addEventListener('click', deleteItem); }); } // Update expense list function updateExpenseList() { if (budgetData.expenses.length === 0) { expenseItemsList.innerHTML = '<div class="empty-state">No expenses added yet. Add your first expense above.</div>'; return; } expenseItemsList.innerHTML = ''; budgetData.expenses.forEach((item, index) => { const itemElement = document.createElement('div'); itemElement.className = 'budget-item expense'; itemElement.innerHTML = ` <div class="budget-item-category">${item.description}<br><small>${item.category}</small></div> <div class="budget-item-amount">$${item.amount.toFixed(2)}</div> <button class="delete-btn" data-index="${index}" data-type="expenses">×</button> `; expenseItemsList.appendChild(itemElement); }); // Add delete event listeners document.querySelectorAll('.delete-btn[data-type="expenses"]').forEach(btn => { btn.addEventListener('click', deleteItem); }); } // Delete income or expense item function deleteItem(e) { const index = parseInt(e.target.getAttribute('data-index')); const type = e.target.getAttribute('data-type'); if (type === 'income') { budgetData.totalIncome -= budgetData.income[index].amount; budgetData.income.splice(index, 1); updateIncomeList(); } else if (type === 'expenses') { budgetData.totalExpenses -= budgetData.expenses[index].amount; budgetData.expenses.splice(index, 1); updateExpenseList(); } updateSummary(); updateDashboard(); } // Update summary function updateSummary() { budgetData.savings = budgetData.totalIncome - budgetData.totalExpenses; // Update summary tab if (budgetData.income.length === 0 && budgetData.expenses.length === 0) { summaryItemsList.innerHTML = '<div class="empty-state">Add income and expenses to see your budget summary.</div>'; budgetInsights.innerHTML = '<p>Your budget insights will appear here once you add income and expenses.</p>'; return; } // Category breakdown const categories = {}; budgetData.expenses.forEach(expense => { if (!categories[expense.category]) { categories[expense.category] = 0; } categories[expense.category] += expense.amount; }); // Sort categories by amount const sortedCategories = Object.entries(categories) .sort((a, b) => b[1] - a[1]) .map(([category, amount]) => ({ category, amount })); // Display summary summaryItemsList.innerHTML = ''; // Display income vs. expenses const overviewItem = document.createElement('div'); overviewItem.className = 'budget-item'; overviewItem.innerHTML = ` <div class="budget-item-category"><strong>Monthly Overview</strong></div> <div class="budget-item-amount">$${budgetData.totalIncome.toFixed(2)} - $${budgetData.totalExpenses.toFixed(2)}</div> <div></div> `; summaryItemsList.appendChild(overviewItem); // Display savings const savingsItem = document.createElement('div'); savingsItem.className = 'budget-item ' + (budgetData.savings >= 0 ? 'income' : 'expense'); savingsItem.innerHTML = ` <div class="budget-item-category"><strong>Net Savings</strong></div> <div class="budget-item-amount">$${budgetData.savings.toFixed(2)}</div> <div></div> `; summaryItemsList.appendChild(savingsItem); // Display category breakdown if (sortedCategories.length > 0) { const breakdownHeader = document.createElement('div'); breakdownHeader.className = 'budget-item'; breakdownHeader.innerHTML = ` <div class="budget-item-category"><strong>Top Expense Categories</strong></div> <div></div> <div></div> `; summaryItemsList.appendChild(breakdownHeader); sortedCategories.forEach(cat => { const percentage = (cat.amount / budgetData.totalExpenses) * 100; const categoryItem = document.createElement('div'); categoryItem.className = 'budget-item'; categoryItem.innerHTML = ` <div class="budget-item-category">${cat.category}</div> <div class="budget-item-amount">$${cat.amount.toFixed(2)}</div> <div>${percentage.toFixed(0)}%</div> `; summaryItemsList.appendChild(categoryItem); }); } // Budget insights let insights = ''; if (budgetData.savings < 0) { insights += `<p>⚠️ <strong>Spending Alert:</strong> You're spending $${Math.abs(budgetData.savings).toFixed(2)} more than you earn. Consider reducing expenses in ${sortedCategories[0]?.category || 'your top category'}.</p>`; } else if (budgetData.savings > 0) { const savingsRate = (budgetData.savings / budgetData.totalIncome) * 100; if (savingsRate < 10) { insights += `<p>👍 <strong>Positive Savings:</strong> You're saving ${savingsRate.toFixed(0)}% of your income. Aim for at least 20% for long-term financial health.</p>`; } else if (savingsRate < 20) { insights += `<p>🌱 <strong>Growing Savings:</strong> Saving ${savingsRate.toFixed(0)}% of your income is good! Just a bit more to reach the recommended 20%.</p>`; } else { insights += `<p>🎉 <strong>Excellent Saving:</strong> You're saving ${savingsRate.toFixed(0)}% of your income! This puts you on track for strong financial health.</p>`; } } // Housing expense check const housingExpense = categories['Housing'] || 0; const housingPercentage = (housingExpense / budgetData.totalIncome) * 100; if (housingPercentage > 30 && housingExpense > 0) { insights += `<p>🏠 <strong>Housing Check:</strong> Housing costs are ${housingPercentage.toFixed(0)}% of your income, above the recommended 30%.</p>`; } // Top expense category insights if (sortedCategories.length > 0) { const topCategory = sortedCategories[0]; const topCategoryPercentage = (topCategory.amount / budgetData.totalExpenses) * 100; if (topCategoryPercentage > 40) { insights += `<p>📊 <strong>Category Alert:</strong> ${topCategory.category} represents ${topCategoryPercentage.toFixed(0)}% of your expenses. Consider if this aligns with your priorities.</p>`; } } if (insights === '') { insights = '<p>Add more income and expense details for personalized insights.</p>'; } budgetInsights.innerHTML = insights; } // Update dashboard numbers and charts function updateDashboard() { // Update summary numbers document.getElementById('income-value').textContent = budgetData.totalIncome.toFixed(2); document.getElementById('expenses-value').textContent = budgetData.totalExpenses.toFixed(2); document.getElementById('savings-value').textContent = budgetData.savings.toFixed(2); // Progress bars const maxValue = Math.max(budgetData.totalIncome, budgetData.totalExpenses) || 1; document.getElementById('income-progress').style.width = `${(budgetData.totalIncome / maxValue) * 100}%`; document.getElementById('expenses-progress').style.width = `${(budgetData.totalExpenses / maxValue) * 100}%`; // Savings progress const savingsProgress = document.getElementById('savings-progress'); const balanceProgress = document.getElementById('balance-progress'); const budgetHealth = document.getElementById('budget-health'); if (budgetData.totalIncome > 0) { const savingsRate = (budgetData.savings / budgetData.totalIncome) * 100; savingsProgress.style.width = `${Math.max(0, savingsRate)}%`; // Budget health indicator let healthPercent = 0; let healthText = ''; if (budgetData.savings < 0) { healthPercent = 0; healthText = 'Deficit'; } else if (savingsRate < 10) { healthPercent = 25; healthText = 'At Risk'; } else if (savingsRate < 20) { healthPercent = 50; healthText = 'Stable'; } else if (savingsRate < 30) { healthPercent = 75; healthText = 'Good'; } else { healthPercent = 100; healthText = 'Excellent'; } balanceProgress.style.width = `${healthPercent}%`; budgetHealth.textContent = healthText; } else { savingsProgress.style.width = '0%'; balanceProgress.style.width = '0%'; budgetHealth.textContent = 'Calculating...'; } // Bar chart const chartHeight = 200; const chartMax = Math.max(budgetData.totalIncome, budgetData.totalExpenses, budgetData.savings > 0 ? budgetData.savings : 0) || 1; const incomeBar = document.getElementById('chart-income'); const expensesBar = document.getElementById('chart-expenses'); const savingsBar = document.getElementById('chart-savings'); const incomeHeight = (budgetData.totalIncome / chartMax) * chartHeight; const expensesHeight = (budgetData.totalExpenses / chartMax) * chartHeight; const savingsHeight = (Math.max(0, budgetData.savings) / chartMax) * chartHeight; incomeBar.style.height = `${incomeHeight}px`; expensesBar.style.height = `${expensesHeight}px`; savingsBar.style.height = `${savingsHeight}px`; incomeBar.querySelector('.bar-value').textContent = `$${budgetData.totalIncome.toFixed(0)}`; expensesBar.querySelector('.bar-value').textContent = `$${budgetData.totalExpenses.toFixed(0)}`; savingsBar.querySelector('.bar-value').textContent = `$${Math.max(0, budgetData.savings).toFixed(0)}`; } // Confetti animation for adding income function createConfetti(event) { const colors = ['#6A8D73', '#F2B880', '#DFE6DA']; const confettiCount = 20; for (let i = 0; i < confettiCount; i++) { const confetti = document.createElement('div'); confetti.className = 'confetti'; confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; confetti.style.left = `${event.clientX + (Math.random() * 40 - 20)}px`; confetti.style.top = `${event.clientY + (Math.random() * 40 - 20)}px`; document.body.appendChild(confetti); // Animate the confetti confetti.style.animation = `confetti-fall ${0.5 + Math.random() * 1}s forwards`; // Remove the confetti element after animation completes setTimeout(() => { confetti.remove(); }, 1500); } } // Tooltip function function showTooltip(element, message) { const tooltip = document.getElementById('tooltip'); tooltip.textContent = message; const rect = element.getBoundingClientRect(); tooltip.style.left = `${rect.left + rect.width / 2}px`; tooltip.style.top = `${rect.top - 30}px`; tooltip.style.opacity = '1'; setTimeout(() => { tooltip.style.opacity = '0'; }, 2000); } // Initialize the dashboard on load updateDashboard(); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>VitraBMI - Smart Health Metrics</title> <style> @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap'); :root { --underweight: linear-gradient(135deg, #74ebd5, #ACB6E5); --normal: linear-gradient(135deg, #76b852, #8DC26F); --overweight: linear-gradient(135deg, #f2994a, #f2c94c); --obese: linear-gradient(135deg, #ff416c, #ff4b2b); --severe-obese: linear-gradient(135deg, #b71c1c, #880e4f); --input-bg: rgba(255, 255, 255, 0.15); --card-bg: rgba(255, 255, 255, 0.12); --shadow: 0 8px 32px rgba(0, 0, 0, 0.1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'DM Sans', sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #f0f4f8; transition: background 1s ease; overflow: hidden; } .container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; padding: 2rem; position: relative; overflow: hidden; } .background { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: var(--normal); z-index: -1; transition: background 1s ease; } .bubble { position: absolute; border-radius: 50%; background: rgba(255, 255, 255, 0.1); animation: float 8s infinite ease-in-out; z-index: -1; } @keyframes float { 0%, 100% { transform: translateY(0) scale(1); } 50% { transform: translateY(-20px) scale(1.05); } } .header { text-align: center; margin-bottom: 2rem; color: white; } .header h1 { font-size: 2.5rem; margin-bottom: 0.5rem; font-weight: 700; } .header p { font-size: 1rem; opacity: 0.9; } .bmi-card { background: var(--card-bg); backdrop-filter: blur(10px); border-radius: 20px; padding: 2rem; box-shadow: var(--shadow); border: 1px solid rgba(255, 255, 255, 0.2); color: white; margin-bottom: 1.5rem; } .input-group { display: flex; flex-direction: column; margin-bottom: 1.5rem; } .input-group:last-of-type { margin-bottom: 2rem; } .input-label { display: flex; align-items: center; margin-bottom: 0.5rem; font-weight: 500; } .input-icon { margin-right: 0.5rem; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; } .input-field { display: flex; align-items: center; position: relative; } .input-field input { width: 100%; padding: 1rem 1.5rem; border: none; border-radius: 10px; background: var(--input-bg); font-size: 1rem; color: white; transition: all 0.3s; outline: none; letter-spacing: 0.5px; } .input-field input::placeholder { color: rgba(255, 255, 255, 0.6); } .input-field input:focus { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5); } .input-unit { position: absolute; right: 1rem; font-size: 0.9rem; color: rgba(255, 255, 255, 0.8); } .calculate-btn { width: 100%; padding: 1rem; border: none; border-radius: 10px; background: rgba(255, 255, 255, 0.2); color: white; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s; position: relative; overflow: hidden; } .calculate-btn:hover { background: rgba(255, 255, 255, 0.3); } .calculate-btn:active { transform: scale(0.98); } .calculate-btn::after { content: ''; position: absolute; width: 100%; height: 100%; top: 0; left: -100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: 0.4s; } .calculate-btn:hover::after { left: 100%; } .result { background: var(--card-bg); backdrop-filter: blur(10px); border-radius: 20px; padding: 2rem; box-shadow: var(--shadow); border: 1px solid rgba(255, 255, 255, 0.2); color: white; text-align: center; opacity: 0; transform: translateY(20px); transition: all 0.5s ease; } .result.visible { opacity: 1; transform: translateY(0); } .bmi-value { font-size: 3rem; font-weight: 700; margin: 1rem 0; } .bmi-category { font-size: 1.2rem; font-weight: 500; margin-bottom: 1rem; } .bmi-interpretation { margin-bottom: 1.5rem; line-height: 1.5; } .bmi-scale { width: 100%; height: 8px; background: rgba(255, 255, 255, 0.2); border-radius: 10px; position: relative; margin: 2rem 0 1rem; } .bmi-marker { position: absolute; width: 16px; height: 16px; background: white; border-radius: 50%; top: 50%; transform: translate(-50%, -50%); box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); transition: left 0.5s ease-out; } .scale-labels { display: flex; justify-content: space-between; font-size: 0.75rem; color: rgba(255, 255, 255, 0.9); } .insights { display: flex; justify-content: space-between; margin-top: 1.5rem; } .insight-item { text-align: center; flex: 1; padding: 0.5rem; } .insight-value { font-size: 1.2rem; font-weight: 700; margin-bottom: 0.25rem; } .insight-label { font-size: 0.8rem; opacity: 0.8; } .error-msg { color: #ff8a80; font-size: 0.85rem; margin-top: 0.5rem; display: none; } .error-msg.visible { display: block; animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both; } @keyframes shake { 10%, 90% { transform: translateX(-1px); } 20%, 80% { transform: translateX(2px); } 30%, 50%, 70% { transform: translateX(-4px); } 40%, 60% { transform: translateX(4px); } } .info-tooltip { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 50%; background: rgba(255, 255, 255, 0.3); font-size: 12px; margin-left: 8px; cursor: pointer; position: relative; } .tooltip-content { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.8); padding: 8px 12px; border-radius: 6px; font-size: 12px; width: 200px; opacity: 0; pointer-events: none; transition: opacity 0.3s; z-index: 10; text-align: center; } .info-tooltip:hover .tooltip-content { opacity: 1; } @media (max-width: 600px) { .container { padding: 1.5rem; } .header h1 { font-size: 2rem; } .bmi-card, .result { padding: 1.5rem; } .bmi-value { font-size: 2.5rem; } } </style> </head> <body> <div class="container"> <div class="background"></div> <!-- Animated background bubbles --> <div class="bubble" style="width: 150px; height: 150px; top: 10%; left: 10%; animation-delay: 0s;"></div> <div class="bubble" style="width: 100px; height: 100px; top: 20%; right: 15%; animation-delay: 1s;"></div> <div class="bubble" style="width: 80px; height: 80px; bottom: 15%; left: 20%; animation-delay: 2s;"></div> <div class="bubble" style="width: 50px; height: 50px; bottom: 30%; right: 25%; animation-delay: 3s;"></div> <div class="header"> <h1>VitraBMI</h1> <p>Track your health metrics with precision and insight</p> </div> <div class="bmi-card"> <div class="input-group"> <div class="input-label"> <div class="input-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path> <rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect> </svg> </div> <span>Height</span> <div class="info-tooltip">? <div class="tooltip-content">Enter your height in centimeters</div> </div> </div> <div class="input-field"> <input type="number" id="height" placeholder="Enter your height" min="100" max="250"> <div class="input-unit">cm</div> </div> <div class="error-msg" id="height-error">Please enter a valid height (100-250 cm)</div> </div> <div class="input-group"> <div class="input-label"> <div class="input-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="10"></circle> <line x1="12" y1="8" x2="12" y2="16"></line> <line x1="8" y1="12" x2="16" y2="12"></line> </svg> </div> <span>Weight</span> <div class="info-tooltip">? <div class="tooltip-content">Enter your weight in kilograms</div> </div> </div> <div class="input-field"> <input type="number" id="weight" placeholder="Enter your weight" min="30" max="300"> <div class="input-unit">kg</div> </div> <div class="error-msg" id="weight-error">Please enter a valid weight (30-300 kg)</div> </div> <button class="calculate-btn" id="calculate-btn">Calculate BMI</button> </div> <div class="result" id="result"> <div class="bmi-value" id="bmi-value">25.9</div> <div class="bmi-category" id="bmi-category">Overweight</div> <div class="bmi-interpretation" id="bmi-interpretation"> Your BMI suggests you're in the overweight range. Focusing on balanced nutrition and regular physical activity could be beneficial. </div> <div class="bmi-scale"> <div class="bmi-marker" id="bmi-marker"></div> </div> <div class="scale-labels"> <span>18.5</span> <span>25</span> <span>30</span> <span>35</span> <span>40</span> </div> <div class="insights"> <div class="insight-item"> <div class="insight-value" id="ideal-weight">65-70</div> <div class="insight-label">Ideal Weight (kg)</div> </div> <div class="insight-item"> <div class="insight-value" id="calories">2100</div> <div class="insight-label">Daily Calories</div> </div> <div class="insight-item"> <div class="insight-value" id="water">2.5</div> <div class="insight-label">Water (L/day)</div> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const heightInput = document.getElementById('height'); const weightInput = document.getElementById('weight'); const calculateBtn = document.getElementById('calculate-btn'); const resultEl = document.getElementById('result'); const bmiValueEl = document.getElementById('bmi-value'); const bmiCategoryEl = document.getElementById('bmi-category'); const bmiInterpretationEl = document.getElementById('bmi-interpretation'); const bmiMarkerEl = document.getElementById('bmi-marker'); const backgroundEl = document.querySelector('.background'); const heightErrorEl = document.getElementById('height-error'); const weightErrorEl = document.getElementById('weight-error'); const idealWeightEl = document.getElementById('ideal-weight'); const caloriesEl = document.getElementById('calories'); const waterEl = document.getElementById('water'); // Add input animation [heightInput, weightInput].forEach(input => { input.addEventListener('focus', () => { input.parentElement.style.transform = 'translateY(-3px)'; input.parentElement.style.boxShadow = '0 5px 15px rgba(0, 0, 0, 0.1)'; }); input.addEventListener('blur', () => { input.parentElement.style.transform = 'translateY(0)'; input.parentElement.style.boxShadow = 'none'; }); }); calculateBtn.addEventListener('click', calculateBMI); // Add input validation heightInput.addEventListener('input', validateHeight); weightInput.addEventListener('input', validateWeight); function validateHeight() { const height = parseFloat(heightInput.value); if (isNaN(height) || height < 100 || height > 250) { heightErrorEl.classList.add('visible'); return false; } else { heightErrorEl.classList.remove('visible'); return true; } } function validateWeight() { const weight = parseFloat(weightInput.value); if (isNaN(weight) || weight < 30 || weight > 300) { weightErrorEl.classList.add('visible'); return false; } else { weightErrorEl.classList.remove('visible'); return true; } } function calculateBMI() { const heightValid = validateHeight(); const weightValid = validateWeight(); if (!heightValid || !weightValid) { resultEl.classList.remove('visible'); return; } const height = parseFloat(heightInput.value) / 100; // cm to m const weight = parseFloat(weightInput.value); const bmi = weight / (height * height); const roundedBMI = Math.round(bmi * 10) / 10; // Set BMI value bmiValueEl.textContent = roundedBMI.toFixed(1); // Set BMI category and interpretation let category, interpretation, gradient; let markerPosition; if (bmi < 18.5) { category = "Underweight"; interpretation = "Your BMI indicates you're underweight. Consider consulting with a healthcare provider about a nutritional plan to achieve a healthier weight."; gradient = 'var(--underweight)'; markerPosition = (bmi / 40) * 100; } else if (bmi < 25) { category = "Normal weight"; interpretation = "Congratulations! Your BMI is in the healthy range. Maintain this with balanced nutrition and regular physical activity."; gradient = 'var(--normal)'; markerPosition = (bmi / 40) * 100; } else if (bmi < 30) { category = "Overweight"; interpretation = "Your BMI suggests you're in the overweight range. Focusing on balanced nutrition and regular physical activity could be beneficial."; gradient = 'var(--overweight)'; markerPosition = (bmi / 40) * 100; } else if (bmi < 35) { category = "Obese (Class I)"; interpretation = "Your BMI indicates obesity. Consider consulting with healthcare providers about strategies for weight management."; gradient = 'var(--obese)'; markerPosition = (bmi / 40) * 100; } else { category = "Severely Obese"; interpretation = "Your BMI indicates severe obesity. Medical guidance is recommended for developing a comprehensive health plan."; gradient = 'var(--severe-obese)'; markerPosition = Math.min((bmi / 40) * 100, 95); } bmiCategoryEl.textContent = category; bmiInterpretationEl.textContent = interpretation; backgroundEl.style.background = gradient; bmiMarkerEl.style.left = `${markerPosition}%`; // Calculate additional insights const heightInM = height; const idealWeightLow = Math.round(18.5 * heightInM * heightInM); const idealWeightHigh = Math.round(24.9 * heightInM * heightInM); idealWeightEl.textContent = `${idealWeightLow}-${idealWeightHigh}`; // Rough estimate of daily calories based on weight and average activity const calories = Math.round(weight * 24 * 1.4); caloriesEl.textContent = calories; // Rough estimate of daily water intake (2.5-3L for average adult) const water = (weight < 70) ? 2.5 : (weight < 90) ? 3 : 3.5; waterEl.textContent = water.toFixed(1); // Show result with animation resultEl.classList.add('visible'); } // Add some random animations to bubbles for more dynamic effect const bubbles = document.querySelectorAll('.bubble'); bubbles.forEach(bubble => { const randomX = Math.random() * 20 - 10; const randomY = Math.random() * 20 - 10; bubble.style.transform = `translate(${randomX}px, ${randomY}px)`; }); }); </script> </body> </html>