HTML, CSS & JS Calculator
Build a modern responsive calculator with CSS Grid layout, neo-brutalist styling, and interactive JavaScript arithmetic functions.
Overview & What You Will Learn
What We Are Building
In this project, we are building a fully interactive digital calculator from scratch using core front-end web technologies (HTML, CSS, and JavaScript). The calculator performs standard mathematical operations including addition, subtraction, multiplication, division, percentage calculations, clear screen (AC), and backspace deletion.
How We Are Building It
We start by structuring the calculator interface with semantic HTML5 elements using custom data-* attributes for buttons. We then style the layout into a clean 4x4 matrix using CSS Grid, CSS custom properties (variables), and Neo-Brutalist shadow borders. Finally, we attach JavaScript event listeners to capture button clicks, track active state, handle mathematical expressions, and render live output updates on the screen.
Key Skills & Concepts You Will Learn:
- Structuring UI components with semantic HTML <button> and <div> tags
- Creating responsive keypads using 4-column CSS Grid layouts
- Using CSS custom properties (--variables) and active press feedback states
- Handling DOM click events and querying elements with data-* attributes
- Managing application state for operands, operators, and mathematical evaluation
Interactive Live Demo
Step-by-Step Tutorial
We begin by defining the calculator wrapper using a <div> element. Inside, we create a display screen containing two child elements: one for the previous operation history and one for the current number input.
<div class="calculator">
<div class="display">
<div id="previous-op" class="previous-op"></div>
<div id="current-op" class="current-op">0</div>
</div>
<!-- Keypad buttons added in Step 2 -->
</div>- The
#current-opelement displays active typing while#previous-optracks pending calculations.
Next, we build a 4x4 matrix of calculator keys using semantic <button> elements. We attach custom data-* attributes (such as data-number and data-action) to cleanly distinguish numeric digits from mathematical operator keys.
<div class="buttons">
<button class="btn btn-action" data-action="clear">AC</button>
<button class="btn btn-action" data-action="delete">DEL</button>
<button class="btn btn-operator" data-action="operator">%</button>
<button class="btn btn-operator" data-action="operator">÷</button>
<button class="btn" data-number="7">7</button>
<button class="btn" data-number="8">8</button>
<button class="btn" data-number="9">9</button>
<button class="btn btn-operator" data-action="operator">×</button>
<button class="btn" data-number="4">4</button>
<button class="btn" data-number="5">5</button>
<button class="btn" data-number="6">6</button>
<button class="btn btn-operator" data-action="operator">-</button>
<button class="btn" data-number="1">1</button>
<button class="btn" data-number="2">2</button>
<button class="btn" data-number="3">3</button>
<button class="btn btn-operator" data-action="operator">+</button>
<button class="btn" data-number="0">0</button>
<button class="btn" data-number=".">.</button>
<button class="btn btn-equals" data-action="equals">=</button>
</div>- Using
data-*attributes allows JavaScript to query buttons cleanly without clogging class names.
To arrange the calculator keys into an aligned matrix, we use CSS Grid Layout with grid-template-columns: repeat(4, 1fr) and the CSS gap property. We configure color tokens using CSS Custom Properties (--variables) and apply a distinct shadow border using CSS box-shadow.
:root {
--bg-calc: #1e1e2e;
--bg-display: #181825;
--text-color: #f8f8f2;
}
.calculator {
width: 100%;
max-width: 360px;
margin: 0 auto;
background-color: var(--bg-calc);
border: 3px solid #111;
border-radius: 16px;
padding: 20px;
box-shadow: 6px 6px 0px #111;
}
.buttons {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}- Setting
max-width: 360pxensures the calculator remains perfectly sized on mobile screens.
We style the display screen and key buttons for hover and press feedback. To make the equals (=) button span across two grid columns, we use grid-column: span 2 along with a vibrant green highlight color.
.display {
background-color: var(--bg-display);
padding: 16px;
border-radius: 10px;
text-align: right;
margin-bottom: 20px;
min-height: 80px;
}
.btn {
padding: 14px;
font-size: 1.2rem;
font-weight: bold;
border-radius: 8px;
cursor: pointer;
transition: transform 0.1s ease;
}
.btn-equals {
grid-column: span 2;
background-color: #2ecc71;
}- Use
grid-column: span 2to extend any grid item across multiple grid tracks.
In vanilla JavaScript, we store state for currentOperand, previousOperand, and operation. When numeric buttons are clicked, we append digits to the current display string using DOM manipulation.
let currentOperand = '0';
let previousOperand = '';
let operation = null;
const currentDisplay = document.getElementById('current-op');
const previousDisplay = document.getElementById('previous-op');
function appendNumber(number) {
if (number === '.' && currentOperand.includes('.')) return;
if (currentOperand === '0' && number !== '.') {
currentOperand = number;
} else {
currentOperand += number;
}
updateDisplay();
}- Check for decimal points to prevent entering multiple decimals in a single number (e.g.
12.3.4).
Finally, we implement the compute() function to evaluate math operations (`+`, `-`, `×`, `÷`, `%`), handle clear (`AC`), and delete (`DEL`) actions dynamically.
function compute() {
let computation;
const prev = parseFloat(previousOperand);
const current = parseFloat(currentOperand);
if (isNaN(prev) || isNaN(current)) return;
switch (operation) {
case '+': computation = prev + current; break;
case '-': computation = prev - current; break;
case '×': computation = prev * current; break;
case '÷': computation = current === 0 ? 'Error' : prev / current; break;
case '%': computation = prev % current; break;
}
currentOperand = computation.toString();
operation = undefined;
previousOperand = '';
updateDisplay();
}- Always validate inputs with
isNaN()before running mathematical operations. - Gracefully handle division by zero (
prev / 0) to prevent screen errors.
Complete Project Source Code
<div class="calculator">
<div class="display">
<div id="previous-op" class="previous-op"></div>
<div id="current-op" class="current-op">0</div>
</div>
<div class="buttons">
<button class="btn btn-action" data-action="clear">AC</button>
<button class="btn btn-action" data-action="delete">DEL</button>
<button class="btn btn-operator" data-action="operator">%</button>
<button class="btn btn-operator" data-action="operator">÷</button>
<button class="btn" data-number="7">7</button>
<button class="btn" data-number="8">8</button>
<button class="btn" data-number="9">9</button>
<button class="btn btn-operator" data-action="operator">×</button>
<button class="btn" data-number="4">4</button>
<button class="btn" data-number="5">5</button>
<button class="btn" data-number="6">6</button>
<button class="btn btn-operator" data-action="operator">-</button>
<button class="btn" data-number="1">1</button>
<button class="btn" data-number="2">2</button>
<button class="btn" data-number="3">3</button>
<button class="btn btn-operator" data-action="operator">+</button>
<button class="btn" data-number="0">0</button>
<button class="btn" data-number=".">.</button>
<button class="btn btn-equals" data-action="equals">=</button>
</div>
</div>