Neu Calculator
A 260 × 372 px neumorphic calculator that really calculates: inset display, 19 raised keys that press in on click, a purple-gradient = key; chained operations, %, ±, division by zero shown as “Err”, results rounded to 12 digits — and several independent calculators on the same page.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Glassmorphism has 47 effects, including 6 free. Explore the category →
3 usage examples



How it works
All the relief comes from two shadows on a single tint. The case .neu-calc (260 × 372 px, padding: 20px, border-radius: 24px) is painted #e0e5ec and carries #b8bec7 10px 10px 20px bottom right and #fff -10px -10px 20px top left: the light comes from the top-left corner. The display .neu-calc-display (220 × 56 px, 14 px radius) takes the same shadows as inset at 4 px — it is carved into the case — and right-aligns the number in 24 px bold, #374151. The grid .neu-calc-grid lays out 19 <button>s in 4 columns (47.5 × 44 px, 10 px gap, 12 px radius), the 0 spans two columns. Each key is raised (4px 4px 8px) and turns hollow on :active (inset 2px 2px 4px), with transition: .15s: measured in Chromium, Firefox and WebKit, the computed shadow under the finger is indeed the inverted one. Operators are purple #8b5cf6, the = key a 135deg gradient #8b5cf6 → #a78bfa with a 12 px purple glow.
The JavaScript (an IIFE, nothing global) creates one instance per .neu-calc found in the page and listens to clicks on the case; each key carries its value in data-key. Operations chain from left to right, like a pocket or till calculator: 12 + 30 × 2 = gives 84 (not 72, there is no operator precedence). % divides the displayed value by 100: 200 % → 2, so 12.5 × 80 % = 10; but 12.5 − 20 % = 12.3 (the key does not compute “20% of 12.5”). ± flips the sign, C resets everything, a second decimal point is ignored. 5 ÷ 0 = shows Err; after that only a digit or C is accepted. Results are rounded to 12 significant digits (0.1 + 0.2 shows 0.3) and switch to 1.42857143e-3 notation beyond 15 characters; input stops at 14 digits, which the display holds without overflowing even with a minus sign (measured in all three engines: 9 px to spare for − and 14 digits; 16 digits would overflow by 10 px). The decimal separator is the point.
Four decisions, each checked by a measurement. The background: set on a black frame, the calculator's light #fff shadow would become a halo (19.8:1 against black, 1.3:1 on its own surface); the .demo-preview frame therefore declares the same #e0e5ec surface and 32 px of vertical margin, without imposing anything on body. One instance per calculator: no global function called through onclick, no display looked up by id — with those two shortcuts, two copies on a page get mixed up (4 and 2 typed on the second write “42” into the first); the script also works under a security policy that forbids inline scripts. Decimals: results are rounded, otherwise 0.1 + 0.2 would show 0.30000000000000004, and input is capped so the 16th digit never leaves the display. After a division by zero, only fresh input is accepted, so NaN never shows. The keys carry type="button": placed inside a form, they do not submit it (checked).
Accessibility
- Keyboard: the 19 keys are real
<button>s. Measured in all three engines, Tab walks through them in reading order (C first), Enter and Space activate them (7 twice → “77”), and the browser focus ring stays visible (outline: auto). Physical keyboard keys (digits, +, Enter) are not listened to: for a kiosk or a touch scale it does not matter; on a website, add akeydownhandler that triggers the same click on[data-key]. - Screen readers: the display is a plain
div; its changes are not announced. Addrole="status"(oraria-live="polite") on.neu-calc-displayso the result is read after =. The ±, ×, ÷ and − keys are read by their symbol, differently depending on the reader: anaria-label(“plus or minus”, “multiply”, “divide”, “minus”) removes the ambiguity. - Contrast: the display number
#374151on#e0e5ecmeasures 8.1:1. Key digits#6b7280(14.4 px, weight 600) only measure 3.8:1 and operators#8b5cf63.3:1, below the 4.5:1 threshold for body text; the white of the = key drops to 2.7:1 on the light end of the#a78bfagradient (4.2:1 on#8b5cf6). Darken digits to#4b5563(6.0:1) and operators to#6d28d9(5.6:1) if the calculator is used for purchases. The relief itself is low-contrast by nature (dark shadow 1.5:1, light shadow 1.3:1 on the surface): key shape must not be the only cue — here the symbol is. - Motion: only the shadow changes, in 0.15 s, and only while pressing. No movement, no continuous animation:
prefers-reduced-motionhas nothing to cut.
Browser compatibility
CSS: double and inset box-shadow, CSS grid, linear-gradient, :active. JavaScript: querySelectorAll().forEach, closest(), toPrecision() — no library, no external resource. Tested pasted into a blank page in Chromium 151, Firefox 153 and Playwright's WebKit 26.5: same results for the same calculations (84, 2, Err, 0.3, 1.42857143e-3), same shadow on :active, two independent calculators. The requested font is Inter: if it is not loaded on your page, the browser uses its default sans-serif font and digit widths vary slightly (14 digits and the sign fit in the display with the default font of Chromium, Firefox and WebKit, measured).
Without JavaScript, the calculator displays in full (display at 0, raised keys that press in) but does not calculate. NodeList.forEach is missing before Chrome 51, Firefox 50 and Safari 10: replace it with Array.prototype.forEach.call if you target those versions. On iOS, Safari only applies :active on touch if a touchstart listener exists on the page (behavior documented by Apple, not measured here): document.addEventListener('touchstart', function () {}, { passive: true }) is enough to see keys press in under the finger; the calculation itself works either way.
The code
HTML structure to paste into your page (the CSS + JS come with Premium):
<div class="demo-preview">
<div class="neu-calc-scene">
<div class="neu-calc">
<div class="neu-calc-display">0</div>
<div class="neu-calc-grid">
<button type="button" class="neu-calc-key neu-calc-key--op" data-key="C">C</button>
…
<button type="button" class="neu-calc-key" data-key="7">7</button>
…
<button type="button" class="neu-calc-key neu-calc-key--eq" data-key="=">=</button>
</div>
</div>
</div>
</div>
The full HTML + CSS + JS of Neu Calculator, ready to paste — plus all 840 effects. Or $299 once, lifetime access.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Surface tint (CSS — background: #e0e5ec on .neu-calc, the display, the keys and .demo-preview) |
#e0e5ec (bluish gray) | Change it everywhere at once: neumorphism only works when background and surface share the same tint. Then recompute both shadows: one about 15% darker (#b8bec7 here) and one lighter (#fff). On a background of another tint, the light shadow turns into a halo. |
Relief depth (CSS — box-shadow of .neu-calc and .neu-calc-key) |
case 10 px / 20 px blur · keys 4 px / 8 px | Keep the blur at twice the offset. Beyond 6 px on keys, the shadows of two neighboring keys overlap in the 10 px gap. |
Key size (CSS — .neu-calc-key { height: 44px }, .neu-calc-grid { gap: 10px }, .neu-calc { width: 260px }) |
47.5 × 44 px, 10 px gap | Width follows the case (4 columns). 300 px wide gives 57.5 px keys (61 px with 16 px padding and an 8 px gap, as in the workshop scene). 38 px high with an 8 px gap brings the case from 372 down to 334 px. |
Operator and = color (CSS — .neu-calc-key--op { color }, .neu-calc-key--eq { background }) |
#8b5cf6 and #8b5cf6 → #a78bfa gradient | One accent color is enough. For readability, aim for 4.5:1 on #e0e5ec: #6d28d9 gives 5.6:1, the original purple 3.3:1. |
Add or rename a key (HTML — data-key) |
C, ±, %, ÷, ×, -, +, =, ., 0-9 | The button text is free (“AC”, “−”); only the data-key value matters to the calculation. Minus is written data-key="-", even when the key shows “−”. |
Input length (JS — cur.replace("-", "").length >= 14; results: cur.length > 15) |
14 digits typed, 15 characters displayed | Adjust both to the display width: about 13.4 px per digit at 24 px. With a narrower display (the scale scene, 202 px) or a wider font, lower both limits together. |
FAQ
.neu-calc element present at load: each has its own display and calculations (measured with two copies — 42 typed on the second, the first stays at 0). There is no id and no global function. For a calculator added after load (a modal), run the initialization block again on that new element.