demo · v138
Unit converter playground
Before typed arithmetic, calc(200px / 1px) was a syntax error — type mismatch. With typed arithmetic it's a unitless number. This playground exercises four conversions that previously needed the tan(atan2(x, 1px)) hack — and drives a dial, a ruler, a colour, and a CSS counter from the result.
Behind a flag in earlier builds: typed arithmetic ships unflagged in Chrome 138 for length / time / angle / percentage. The dial and the counters below run entirely in CSS — no JavaScript reads or writes any computed style.
probing typed arithmetic…
↓ all four numbers below come from CSS calcs alone — JS only writes the two slider values.
length → number
calc(var(--px) / 1px)
em → percentage
calc(var(--em) / 1em * 100%)
%
length → angle
calc(var(--px) / 1px * 1deg)
0deg
px → percentage (÷4)
calc((var(--px) / 1px / 4) * 1%)
dial — rotation = calc(var(--px) / 1px * 1deg)
colour — red channel = calc(var(--px) / 1px), mapped via @property
interactive: drag the px slider
calc(var(--px) / 1px) → unitless number → drives needle rotation, swatch red channel, counters above
The four canonical conversions
/* OLD — pre-v138 you had to do this nonsense */
.legacy {
--unitless: tan(atan2(var(--px), 1px)); /* the "atan2 trick" */
}
/* NEW — typed arithmetic */
.modern {
--unitless: calc(var(--px) / 1px); /* <length> / <length> = <number> */
--as-percentage: calc(var(--em) / 1em * 100%); /* multiply unitless back into a unit */
--as-angle: calc(var(--px) / 1px * 1deg); /* cast from px space to deg space */
--double-percent: calc(20% / 0.5em * 1px); /* percentage + em → length */
}
What's happening
- Each slider drives a CSS custom property (
--px,--em). These are typed, registered with@property. - The four conversion calcs each cast or compose units. The result feeds
transform: rotate(…),rgb(),counter-reset, andwidth. - CSS counters (which only accept integer) are the readout trick:
counter-reset: emToPx calc((var(--em) / 1em) * 100)gives us a live numeric display straight from CSS arithmetic — no JavaScript needed to render the converted value. - The dial's needle rotation comes from
--px / 1px * 1deg— a length cast into an angle. Before v138 this was a hard syntax error.