Source Code

<div class="calc">
  <div class="calc-screen">
    <div class="calc-history" id="calcHistory">&nbsp;</div>
    <div class="calc-display" id="calcDisplay">0</div>
  </div>
  <div class="calc-keys">
    <button class="calc-key fn" onclick="press('C')">AC</button>
    <button class="calc-key fn" onclick="press('±')">±</button>
    <button class="calc-key fn" onclick="press('%')">%</button>
    <button class="calc-key op" data-op="÷" onclick="press('÷')">÷</button>

    <button class="calc-key" onclick="press('7')">7</button>
    <button class="calc-key" onclick="press('8')">8</button>
    <button class="calc-key" onclick="press('9')">9</button>
    <button class="calc-key op" data-op="×" onclick="press('×')">×</button>

    <button class="calc-key" onclick="press('4')">4</button>
    <button class="calc-key" onclick="press('5')">5</button>
    <button class="calc-key" onclick="press('6')">6</button>
    <button class="calc-key op" data-op="−" onclick="press('−')">−</button>

    <button class="calc-key" onclick="press('1')">1</button>
    <button class="calc-key" onclick="press('2')">2</button>
    <button class="calc-key" onclick="press('3')">3</button>
    <button class="calc-key op" data-op="+" onclick="press('+')">+</button>

    <button class="calc-key zero" onclick="press('0')">0</button>
    <button class="calc-key" onclick="press('.')">.</button>
    <button class="calc-key eq" onclick="press('=')">=</button>
  </div>
</div>

Calculator — Keypad UI HTML CSS JS Snippet

Calculator · Forms · Plain HTML, CSS & JS · Live preview

What's included

Features

Four-variable state machine
cur, prev, op, and fresh model the whole calculator; press dispatches every key through it for correct, predictable behaviour.
Chained operations
Pressing an operator with one already pending computes the intermediate result first — matching how physical calculators chain math.
Float-noise rounding
Results round via Math.round(n * 1e10) / 1e10, so 0.1 + 0.2 shows 0.3 instead of a long binary artefact.
Divide-by-zero handling
Non-finite results are detected and shown as "Error" rather than Infinity or NaN.
History line
A second screen line shows prev op so the in-progress operation is always visible above the current value.
Active-operator highlight
The pending operator key inverts colour while it awaits the next operand, preventing "which key did I press?" confusion.
Overflow-safe display
Long numbers fall back to toPrecision and the display ellipsises, so values never break the layout.
Full keyboard support
A keydown map routes physical keys (digits, + - * /, Enter, Esc, %) through the same press function as the buttons.

About this UI Snippet

Calculator — Chained-Operation Engine, Operator Highlight & Keyboard Support

Screenshot of the Calculator snippet rendered live

A calculator is a deceptively rich UI exercise: the keypad is easy, but the state machine behind it — chaining operations, replacing a pending operator, handling decimals, percentages, sign flips, and divide-by-zero — is where most clones break. This snippet implements a genuinely working calculator in plain HTML, CSS, and vanilla JavaScript: a clean keypad, a two-line screen, a chained-operation engine, an active-operator highlight, error handling, and full physical-keyboard support.

The classic four-variable state machine

State is just four values: cur (the string being typed), prev (the stored operand), op (the pending operator), and fresh (whether the next digit starts a new number). press dispatches every key through this model. Digits append to cur unless fresh is set, in which case they start a new number — this single flag is what makes typing after an operator or an equals behave correctly. The decimal key guards against multiple dots. This is the time-tested model real calculators use, and it handles the tricky cases that naive "build an expression string and eval it" approaches get wrong.

Chained operations

Pressing an operator when one is already pending computes the intermediate result first (2 + 3 + 4 shows 5 then 9), exactly like a physical calculator, by calling compute before storing the new operator. compute applies prev op cur, rounds the result with Math.round(n * 1e10) / 1e10 to kill floating-point noise (so 0.1 + 0.2 is 0.3, not 0.30000000000000004), and detects non-finite results to show "Error" on divide-by-zero.

Two-line screen and operator highlight

The screen shows a small history line (prev op) above the large current value, so users can see the operation in progress. The active operator key highlights (inverts colour) while it is pending and fresh is true, a small affordance that prevents the "which operator did I press?" confusion. Long results fall back to toPrecision so they never overflow the display.

Two-line screen and operator highlight

The screen shows a small history line above the large current value, so users can see the operation in progress (prev op) while typing the next operand. The current value falls back to toPrecision(8) when it grows beyond twelve characters and the display ellipsises, so a long or repeating result never breaks the layout. The pending operator key inverts its colour while it waits for the next number — a small affordance that answers the common "which operator did I press?" question without any extra UI.

Real keyboard support

A keydown listener maps physical keys to the same press function: digits and . directly, * / + - to the on-screen operators, Enter/= to equals, Escape to clear, and %. Because the mouse buttons and the keyboard both route through one press dispatcher, the two input paths can never diverge or get out of sync — a single source of truth for every key, which is exactly what keeps the state machine reliable.

It is a self-contained widget you can drop anywhere. Pair it with a tip calculator, a currency input for amounts, or a mortgage calculator for finance tools.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to trace every branch of the press function by hand to see why this calculator behaves correctly. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly what role the fresh flag plays in deciding whether a digit key starts a new number or appends to the current one, and why compute is called before storing a new operator when one is already pending. The same assistant can help optimize it — asking whether the fmt function's rounding to 1e10 precision handles every edge case of repeating decimals, or whether the keydown listener could accidentally double-fire an action already triggered by a button's onclick. It's also useful for extending the calculator: ask it to add parenthesized expressions with real operator precedence, a running memory (M+/M-/MR) feature, or a calculation history panel. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a working calculator in plain HTML, CSS, and JavaScript that models a real four-function calculator's behavior (left-to-right chained operations, not full mathematical operator precedence) — no eval, no expression parser.

Requirements:
- Maintain exactly four pieces of state: the string currently being typed, the previously stored operand, the pending operator (or none), and a boolean flag indicating whether the next digit should start a fresh number or append to the current one.
- Route every single key press (digit, decimal point, operator, equals, clear, sign-flip, percent) through one shared dispatch function, so there is exactly one code path for both on-screen button clicks and physical keyboard input.
- When an operator is pressed while a different operator is already pending and the user isn't fresh off just having pressed an operator, compute the intermediate result first using the stored operand and the current typed value, so chained expressions like 2 + 3 + 4 show the intermediate 5 before continuing, matching real calculator behavior rather than deferring to full operator precedence.
- Round every computed result to eliminate floating-point representation noise (so 0.1 + 0.2 displays as 0.3, not a long trailing-digit artifact), and detect non-finite results (division by zero) to display a distinct "Error" state instead of Infinity or NaN.
- Show two display lines: a small history line above showing the stored operand and pending operator while a calculation is in progress, and a large current-value line below; long values must fall back to reduced precision formatting so they never overflow or wrap the display.
- Visually highlight whichever operator button is currently pending (and only while the calculator is in the "fresh" state awaiting the next number), and attach a global keydown listener mapping number keys, the four math symbols, Enter, Escape, and percent to the exact same dispatch function used by the on-screen buttons.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA dark calculator with a two-line screen and a 4-column keypad appears, showing "0".
  2. 2
    Do a calculationTap 1 2 × 6 = — the history line shows the operation and the display shows 72.
  3. 3
    Chain operationsType 2 + 3 + 4 — pressing the second + shows the intermediate 5, then = gives 9, like a real calculator.
  4. 4
    Use AC, ±, and %AC clears everything, ± flips the sign of the current number, and % divides it by 100.
  5. 5
    Type on your keyboardUse the number keys, + − * /, Enter for =, and Esc to clear — all routed through the same logic.
  6. 6
    See error handlingDivide by zero (e.g. 5 ÷ 0 =) and the display shows "Error" instead of Infinity.

Real-world uses

Common Use Cases

Embedded utility calculator
Drop a working calculator into a tools page or sidebar. Sit it alongside a tip calculator and BMI calculator.
Finance and budgeting apps
Quick arithmetic next to inputs; combine with a currency input and mortgage calculator.
Point-of-sale and checkout
A keypad for manual totals or change; pair with an order summary for the cart side.
Education and learning tools
Teach operator precedence and arithmetic with a tactile, keyboard-friendly calculator.
Calculator app clones
A faithful iOS/Android-style calculator UI as a starting point for a fuller app, including scientific keys.
Form helper widgets
Let users compute a value before pasting it into a field, useful in invoicing, quoting, and data-entry tools.
Related: Chip Multiselect
See the Chip Multiselect for a related forms pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

eval is a security risk and gives surprising results for calculator-style chaining (it applies full operator precedence, whereas a basic calculator evaluates left-to-right as you press). The four-variable state machine here mirrors how physical calculators actually behave, avoids eval entirely, and correctly handles editing the current number, chaining, and re-pressing operators.

Replace the two-operand model with a tokeniser plus the shunting-yard algorithm to build Reverse Polish Notation, then evaluate the RPN stack. Show the full expression in the history line and only compute on =. This gives 2 + 3 × 4 = 14 (precedence) instead of the left-to-right 20 a basic calculator produces — choose based on whether you want a calculator or an expression evaluator.

This snippet caps the display with toPrecision(8) for long values. For scientific use, format with toExponential beyond a threshold, or use a big-number/decimal library to avoid IEEE-754 precision limits entirely. Always round display output (as done here) so accumulated floating-point error never shows.

Yes — the keys are real <button>s (focusable, Enter/Space activatable) and a global keydown map mirrors them, so it is fully operable without a mouse. For screen readers, add an aria-live="polite" on the display so results are announced, and give each key an aria-label where the glyph is ambiguous (e.g. ± as "plus or minus", ÷ as "divide").

In React, hold cur, prev, op, and fresh in useState (or a useReducer for the dispatch model) and render the display from them; attach the keydown listener in a useEffect with cleanup. In Vue, use refs and a press method. In Angular, keep the state on the component and a @HostListener('keydown'). The state machine logic ports verbatim.