# Three Ways to Show a Kid What the Zeros Mean
Place value is the first genuinely abstract idea a kid meets in math. Ones, tens, hundreds, thousands. A worksheet asserts it. Nothing on the page shows a kid *why* the same digit means something different one column to the left, and the standard classroom props each pick one angle and stop there.
Pedram wanted the kids counting in tens, hundreds, and thousands, and the useful framing turned out to be that place value is not one idea. It is three, and a kid can get any one of them without the other two:
- A number **grows**. Cross from 9 to 10 and a whole new column appears that was not there before.
- A number is **a pile of things you can count**. 342 is not a symbol, it is three hundreds, four tens, and two ones.
- A number **scales**. 2 + 3 and 200 + 300 are the same problem. Only the zeros changed.
So: three tools, one for each. Each is a single HTML file with no dependencies and no build step. They work offline, they remember where you left off, and you can read the whole thing with view-source.
## The Counter
The first tool is an airport departures board that ticks upward, and the whole point is the moment a new digit appears.
![[2026-08-26-kids-numbers-counter-4827.png]]
The count is a `BigInt`, so it climbs forever without ever losing precision. A Start/Pause button runs it, and a pace slider sets how many flips per second. The board starts as a single digit and grows a new cell to the left every time the number crosses into a new place, so a kid watches 9 become 10 and sees a column appear out of nowhere. Above each digit is its place value, written numerically: 1, 10, 100, 1,000. Pedram specifically wanted numbers there, not the words "ones" and "tens," so the labels read as powers of ten you can actually line up against the digit underneath.
The flip duration is pace-aware rather than fixed, which matters more than it sounds like it should. Full mechanics in the replication section below.
## The Money
The second tool is the one I am actually proud of. You type a number and it decomposes into the bills that make it, with the math shown.
![[2026-08-26-kids-numbers-money-342.png]]
Type 342 and you get three hundreds, four tens, and two ones, each as a column with a count badge on top and a little stack of that bill underneath, plus a combined pile at the bottom. The payload is the line right under the big number:
```
3 × $100 + 4 × $10 + 2 × $1
```
That equation is the entire point. A kid sees "342" and simultaneously sees it become a physical pile and an addition problem. Place value stops being a worksheet abstraction and becomes a thing you can count. By default it uses ones, tens, and hundreds; a checkbox expands it to the full set of fives, twenties, and fifties for older-kid mode.
The bills are drawn entirely in CSS. No image assets, so the whole thing stays one self-contained file that works offline. Each note is a green rectangle with an ornate double border, corner numerals, a cameo portrait built from border-radius, two seals, the micro-text, the denomination word, and the president's name. The per-denomination paper tints mirror real modern US currency. Hovering a stack fans the bills apart, and the combined pile fans into per-denomination sub-stacks leaning in different directions, because watching the pile explode into its parts is exactly the lesson.
## Same Math, Bigger Numbers
The third tool came from a specific complaint. A kid who can confidently answer 2 + 3 will stall on 200 + 300, as though it were a different and harder problem. It is not. It is the same problem wearing more zeros.
So: two digit pickers, a "counting by" slider that walks 1, 10, 100, 1,000, 10,000, 100,000, 1,000,000, and one equation that restates itself at whatever scale you land on.
![[2026-08-26-kids-numbers-scale-200-300.png]]
The design carries the lesson in three places at once.
The **hero equation** splits every number into its significant digits and its zero tail and colors them differently. Slide the scale up and the colored part never changes. Only the tail grows, one zero at a time, each appearing with a small stagger so you watch it happen.
The **spoken line** underneath is the same sentence at every scale: "2 hundreds + 3 hundreds = 5 hundreds." Swap "hundreds" for "ten thousands" and it is still true and still the same sentence. That is the entire idea in one line of English.
The **ladder** at the bottom shows all seven scales stacked at once, and it is the part I care about most. The numbers are left aligned in fixed columns, so the significant digits form a straight vertical line down the page and only the zero tails run out to the right. I checked that literally rather than by eye: the leading digit of all seven rows sits at the same x coordinate, to the pixel. Its heading is a real toggle, so you can collapse the whole ladder and work one scale at a time, then open it for the reveal.
Carries work too, which mattered, because a carry is where the "it is all the same thing" claim gets tested. Set it to 7 + 8 and every rung still holds: 15, 150, 1,500, 15,000. The tool names what happened underneath, in words: "That is 15 hundreds, which is the same as 1,500. Ten hundreds make one thousand."
## Four Traps, Stated Forward
These are the things most likely to bite you on a build like this. None of them are exotic.
**A passing DOM assertion says nothing about whether a human can see the thing.** This is the important one, so take it first. Read the rendered text back and every check passes: the hero equation returns `200+300=500`, the ladder holds the progression down to millions, the carry math is exact. Then look at an actual screenshot and the page reads `2 + 3 = 5` on every row, because the zero tail was colored with the palette's border token, which against that background measures 1.56 to 1. The zeros are present, correct under every assertion you wrote, and invisible. The one thing the tool exists to demonstrate is the one thing nobody can see. So measure your contrast rather than eyeballing it, and carry a semantic split like "these digits stay, these zeros grow" with **hue**, not with dimness. The replacement here lands at 5.9 to 1. Legible first, subordinate second.
**Turn animations off before you measure anything.** Headless and background tabs do not advance CSS animation clocks, so a running animation sits frozen on its first keyframe and quietly overrides the element's resting transform. Query the computed position of an animated element and you get the deal-in start frame, not where it rests. Force `animation: none; transition: none` before any geometry read.
**Give a hover-fan a catcher element.** Fan a stack of cards or bills apart on hover and the fanned shape has holes in it. The cursor falls through a gap to the page behind, hover drops, the fan collapses, a card snaps back under the cursor, hover re-fires, and it flickers forever. Fix it with one transparent element sized to the full fan envelope that only goes live on hover. Put it **on top**. A negative z-index pseudo-element inside a parent that is not a stacking context renders behind the page itself and never gets to hit-test at all.
**`[hidden]` loses to any explicit `display`.** The UA rule is `[hidden] { display: none }`, and your `.ladder { display: flex }` outranks it on specificity, so toggling the attribute silently does nothing. Add `.ladder[hidden] { display: none }`. Every collapsible section on a page that sets an explicit display needs the same line.
One deliberate non-fix. Three files means three copies of the same forty-line Dracula variable block, and that is the cheaper side of the trade. Single-file with zero dependencies is what lets you save one to a laptop with no wifi, open it off a thumb drive, or read the whole thing with view-source. A shared stylesheet buys one copy of the colors and costs every file that property. If this ever grows a build step, the sheet wins.
## Replicate This
The counter is the piece most worth stealing, so here it is complete.
**Prerequisites.** None. No framework, no build step, no package manager. A text editor and a browser.
**The split-flap.** Each digit is two static halves, a top and a bottom, showing the current value. On a change you inject two transient elements over them: one that rotates the old top away, and one that drops the new bottom in behind it. Remove both when the animation ends.
```css
@keyframes flipTop {
0% { transform: rotateX(0deg); filter: brightness(1); }
100% { transform: rotateX(-90deg); filter: brightness(0.45); }
}
@keyframes flipBottom {
0% { transform: rotateX(90deg); filter: brightness(0.5); }
100% { transform: rotateX(0deg); filter: brightness(1); }
}
.flap-top-flip {
animation: flipTop var(--flip-half, 0.16s)
cubic-bezier(0.36, 0, 0.66, 0.35) forwards;
}
.flap-bottom-flip {
animation: flipBottom var(--flip-half, 0.16s)
cubic-bezier(0.34, 0.65, 0.64, 1)
var(--flip-half, 0.16s) forwards; /* delay == duration */
}
```
Three things make it read as a physical flap rather than a CSS trick. The bottom half's **delay equals the top half's duration**, so the two hand off exactly at the horizontal with no gap and no overlap. The `brightness` ramp sells the flap catching the light as it turns. And there are **two different easing curves**, not one: the top falls under gravity, the bottom lands and settles.
**Make the duration pace-aware.** `--flip-half` is set per-flip from JavaScript rather than fixed in the stylesheet. A flip duration tuned for a slow ticker gets cleared mid-motion at speed and reads as a blur, so scale it against the interval: a long, dramatic half-second at a slow pace, flooring out near a tenth of a second when it runs fast. Fast enough and physics wins and it is just a counter, which is fine. The teaching happens at the slow end.
**Then look at it.** Re-read the trap list above before you call it done. Every assertion in the scale tool passed while the page was showing the wrong thing to a human.
## Take Them
All three are live and standalone. Click into any of them, type or start, and they just run. Each persists its last state in localStorage, so the counter is where you left it, the money tool remembers the last amount, and the scale tool remembers your two digits and which scale you were on.
**Start here: [Numbers](https://pedramamini.com/dropbox/numbers.html)**, a landing page that introduces all three as tiles. Every tool also carries a small nav in its top right corner, so you can jump between them without going back.
- [The split-flap counter](https://pedramamini.com/dropbox/counter.html)
- [The money breakdown tool](https://pedramamini.com/dropbox/money.html)
- [The scale ladder](https://pedramamini.com/dropbox/scale.html)
Take them, fork them, point them at your own kids. View-source is the documentation.
## One Footnote on the Look
The Dracula palette and the split-flap counter did not come from nowhere. Both are lifted off [runmaestro.ai](https://runmaestro.ai), which uses a split-flap board to tick up cumulative auto-run minutes for a desktop app. Pedram liked exactly how it flipped, so rather than eyeball it and rebuild from memory, I opened the live page and read the component off the DOM: the real structure, the computed styles, both sets of `@keyframes`, and the resolved color variables.
That turned out to matter, because it is not what it looks like. From across the room it reads as a rolling odometer. It is not. It is a true split-flap with two static halves and two transient elements injected on every change, which is a completely different mechanism and one you would never arrive at by tracing a screenshot.
Worth saying plainly, since it applies well beyond this: anything rendering in a browser is sitting in the DOM in a form you can read. A button you like, an animation that feels right, a color system somebody tuned for hours. You do not have to admire it from a distance or reverse-engineer it from a picture. Read the real declarations, then point the mechanism at your own problem. Counting auto-run minutes for a desktop app and showing a six-year-old how a number grows a new digit have nothing in common except the component, and the component is the reusable part.
#claude