Skip to content

Instantly share code, notes, and snippets.

@MattWenJun
Created March 15, 2026 06:51
Show Gist options
  • Select an option

  • Save MattWenJun/e60205e24dbd692009abcac534bcaf4d to your computer and use it in GitHub Desktop.

Select an option

Save MattWenJun/e60205e24dbd692009abcac534bcaf4d to your computer and use it in GitHub Desktop.
Luna Calculator - Interactive Demo
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Luna Calculator</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.calculator {
background: rgba(255,255,255,0.08);
backdrop-filter: blur(20px);
border-radius: 24px;
padding: 28px;
box-shadow: 0 20px 60px rgba(0,0,0,0.4);
border: 1px solid rgba(255,255,255,0.1);
opacity: 0;
transform: translateY(30px);
animation: slideUp 0.6s ease forwards;
}
@keyframes slideUp {
to { opacity: 1; transform: translateY(0); }
}
.display {
background: rgba(0,0,0,0.3);
border-radius: 16px;
padding: 20px 24px;
margin-bottom: 20px;
text-align: right;
min-height: 90px;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.display .expression {
color: rgba(255,255,255,0.4);
font-size: 16px;
min-height: 24px;
word-break: break-all;
}
.display .result {
color: #fff;
font-size: 42px;
font-weight: 300;
letter-spacing: 1px;
}
.buttons {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.btn {
width: 72px;
height: 64px;
border: none;
border-radius: 16px;
font-size: 22px;
font-weight: 500;
cursor: pointer;
transition: all 0.15s ease;
opacity: 0;
transform: scale(0.5);
position: relative;
overflow: hidden;
}
.btn::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle at var(--x, 50%) var(--y, 50%), rgba(255,255,255,0.3), transparent 60%);
opacity: 0;
transition: opacity 0.3s;
}
.btn:hover::after { opacity: 1; }
.btn:active { transform: scale(0.95) !important; }
.btn.pop { animation: popIn 0.3s ease forwards; }
@keyframes popIn {
0% { opacity: 0; transform: scale(0.5); }
70% { transform: scale(1.08); }
100% { opacity: 1; transform: scale(1); }
}
.btn.num {
background: rgba(255,255,255,0.12);
color: #fff;
}
.btn.num:hover { background: rgba(255,255,255,0.2); }
.btn.op {
background: rgba(255,165,0,0.7);
color: #fff;
}
.btn.op:hover { background: rgba(255,165,0,0.9); }
.btn.func {
background: rgba(255,255,255,0.06);
color: rgba(255,255,255,0.7);
font-size: 18px;
}
.btn.func:hover { background: rgba(255,255,255,0.12); }
.btn.eq {
background: linear-gradient(135deg, #e94560, #c23152);
color: #fff;
}
.btn.eq:hover { background: linear-gradient(135deg, #ff6b81, #e94560); }
.btn.zero {
grid-column: span 2;
width: 100%;
}
.title {
text-align: center;
color: rgba(255,255,255,0.3);
font-size: 12px;
letter-spacing: 3px;
text-transform: uppercase;
margin-bottom: 16px;
}
</style>
</head>
<body>
<div class="calculator">
<div class="title">🌙 Luna Calculator</div>
<div class="display">
<div class="expression" id="expr"></div>
<div class="result" id="result">0</div>
</div>
<div class="buttons">
<button class="btn func" data-v="AC">AC</button>
<button class="btn func" data-v="±">±</button>
<button class="btn func" data-v="%">%</button>
<button class="btn op" data-v="÷">÷</button>
<button class="btn num" data-v="7">7</button>
<button class="btn num" data-v="8">8</button>
<button class="btn num" data-v="9">9</button>
<button class="btn op" data-v="×">×</button>
<button class="btn num" data-v="4">4</button>
<button class="btn num" data-v="5">5</button>
<button class="btn num" data-v="6">6</button>
<button class="btn op" data-v="-"></button>
<button class="btn num" data-v="1">1</button>
<button class="btn num" data-v="2">2</button>
<button class="btn num" data-v="3">3</button>
<button class="btn op" data-v="+">+</button>
<button class="btn num zero" data-v="0">0</button>
<button class="btn num" data-v=".">.</button>
<button class="btn eq" data-v="=">=</button>
</div>
</div>
<script>
// Animate buttons popping in one by one
const btns = document.querySelectorAll('.btn');
btns.forEach((btn, i) => {
setTimeout(() => btn.classList.add('pop'), 80 + i * 60);
btn.addEventListener('mousemove', e => {
const r = btn.getBoundingClientRect();
btn.style.setProperty('--x', ((e.clientX - r.left) / r.width * 100) + '%');
btn.style.setProperty('--y', ((e.clientY - r.top) / r.height * 100) + '%');
});
});
let current = '0', previous = '', operator = '', shouldReset = false;
const resultEl = document.getElementById('result');
const exprEl = document.getElementById('expr');
function updateDisplay() {
resultEl.textContent = current;
if (previous && operator) {
exprEl.textContent = previous + ' ' + operator + ' ';
} else {
exprEl.textContent = '';
}
}
function calculate(a, op, b) {
a = parseFloat(a); b = parseFloat(b);
switch(op) {
case '+': return a + b;
case '−': return a - b;
case '×': return a * b;
case '÷': return b === 0 ? 'Error' : a / b;
}
}
document.querySelector('.buttons').addEventListener('click', e => {
const btn = e.target.closest('.btn');
if (!btn) return;
const v = btn.dataset.v;
if (v >= '0' && v <= '9' || v === '.') {
if (shouldReset || current === '0' && v !== '.') {
current = v === '.' ? '0.' : v;
shouldReset = false;
} else {
if (v === '.' && current.includes('.')) return;
current += v;
}
} else if (['+','−','×','÷'].includes(v)) {
if (previous && operator && !shouldReset) {
const r = calculate(previous, operator, current);
current = String(r);
previous = current;
} else {
previous = current;
}
operator = v;
shouldReset = true;
} else if (v === '=') {
if (!previous || !operator) return;
const r = calculate(previous, operator, current);
exprEl.textContent = previous + ' ' + operator + ' ' + current + ' =';
current = String(parseFloat(r.toFixed(10)));
previous = '';
operator = '';
shouldReset = true;
} else if (v === 'AC') {
current = '0'; previous = ''; operator = '';
} else if (v === '±') {
current = String(-parseFloat(current));
} else if (v === '%') {
current = String(parseFloat(current) / 100);
}
updateDisplay();
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment