Special · Chapter 7 of 7 · 12 units
Special Units
Not everything CSS measures is a distance.
Everything so far has measured distance: lengths that ultimately resolve to pixels. But CSS has four more families of units that measure other dimensions entirely: angles, time, frequency, and resolution.
angle deg, grad, rad, turn — rotations, gradients, hue
time s, ms — transitions and animations
frequency Hz, kHz — speech CSS (essentially unused)
resolution dpi, dpcm, dppx, x — density media queries, image-set()
They’re not <length> values, so you can’t put them in width or padding. In day-to-day work, angles and time come up constantly, resolution occasionally, and frequency almost never (its properties aren’t implemented). Here’s the honest tour.
Angles — deg, grad, rad, turn
Four units, one circle. They all describe rotation; they just divide the circle differently:
deg degrees — a full circle is 360deg
grad gradians — a full circle is 400grad
rad radians — a full circle is 2π ≈ 6.283rad
turn turns — a full circle is 1turn
deg is the everyday choice. turn reads well for simple fractions (0.5turn = half a rotation). rad shows up when values come from JavaScript math (Math.PI). grad is the odd one out — an eighteenth-century French attempt to decimalize the circle that survives in surveying and on European calculators; valid and unambiguous in CSS, just unfamiliar to most readers.
90deg90deg100grad1.5708rad0.25turnAll four units describe the same rotation:360deg = 400grad = 2π rad ≈ 6.283rad = 1turn. They're interchangeable in rotate(), conic-gradient(), and the hue channel of hsl().
Angles appear in more places than people expect:Hue is an angle, which makes every color picker secretly a protractor — oklch(0.58 0.15 42) points at this site’s terracotta the way a compass points north-east.
.spin { transform: rotate(45deg); }
.pie { background: conic-gradient(red 0turn, blue 1turn); }
.diagonal{ background: linear-gradient(135deg, #f00, #00f); }
.swatch { background: hsl(200deg 80% 50%); } /* hue is an angle */
- Rotations:
transform: rotate(45deg) - Gradients:
linear-gradient(135deg, …),conic-gradient() - Hue channel in
hsl()/oklch():hsl(200deg …) turnfor clean fractions:0.5turn,0.25turnradwhen the value comes straight from JS trig
- Defaulting to
gradfor shared code — preferdeg, which more readers parse at a glance - Mixing angle units within one codebase without reason — pick one and stay consistent
Time — s and ms
Two units, one quantity. Time drives transition and animation durations and delays:
.fade {
transition: opacity 0.3s ease;
}
.slide {
animation: in 600ms ease-out;
}
1s = 1000ms, so they’re fully interchangeable. Teams usually pick one for consistency; ms reads naturally for short UI timings (150ms, 300ms), s for longer ones (1.5s).
The numbers themselves matter more than the unit. The rough bands most interface work settles on: around 100ms reads as instant, 200–300ms as smooth motion, and past half a second an interface starts to feel like it’s ignoring you. These are conventions rather than findings, and worth re-testing on your own product, but they explain why most UI transitions live in a narrow 150–300ms band — and why ms is usually the more natural notation.
600ms600mss 0.6sare the same value1s = 1000ms — pick whichever reads better. One catch: time always needs a unit. transition-duration: 1 is invalid and ignored; it must be 1s or 1000ms.
- Transition and animation durations:
0.2s,200ms - Delays:
transition-delay,animation-delay msfor short UI timings;sfor longer sequences- Negative time on
animation-delayto start mid-animation
- Unitless time —
1is invalid; always write1s/1000ms - Time anywhere outside duration/delay properties — it's not a length
Frequency — Hz and kHz
Frequency units exist in the spec for aural / speech CSS: properties like pitch that were meant to control synthesized speech. 1kHz = 1000Hz.
In practice they have essentially no browser support: the speech CSS properties they were designed for were never broadly implemented, and the aural module was deprecated. Voice characteristics are set by the reader in their screen reader or OS instead, which is the honest answer — the CSS Speech module that replaced the aural one is barely implemented either, and ARIA describes semantics, not delivery.
- Know they exist so the spec's unit list isn't a mystery
- Recognize them if you encounter very old aural stylesheets
- Relying on them — the consuming properties aren't implemented in shipping browsers
- Confusing CSS frequency units with the Web Audio API (unrelated)
Resolution — dpi, dpcm, dppx, x
Resolution units describe pixel density — how many device dots fit in a unit of length:
dpi dots per inch
dpcm dots per centimeter
dppx dots per px unit (1dppx = one device pixel per CSS pixel)
x shorthand for dppx
The key equivalence ties them to the absolute-units anchor:
1x = 1dppx = 96dpi
Because 1in = 96px, “96 dots per inch” is the same as “1 dot per CSS pixel.” A 2× Retina display reports 2dppx / 2x — equivalently 192dpi in media-query terms (not the panel’s physical ppi, which is far higher).
————Resolution units describe pixel density. 1x = 1dppx = 96dpi. They power @media (min-resolution: 2dppx) and theimage-set() function, so a HiDPI display gets a sharper asset.
You won’t write resolution units in width; they’re for two specific places: density media queries and resolution-aware image selection.
/* Serve a sharper background to HiDPI screens */
@media (min-resolution: 2dppx) {
.logo { background-image: url(logo@2x.png); }
}
/* Let the browser pick the right asset by density */
.avatar {
background-image: image-set(
"avatar.png" 1x,
"avatar@2x.png" 2x
);
}
- Density media queries:
@media (min-resolution: 2dppx) - Resolution-aware images:
image-set()with1x/2x - Prefer
dppx/x— they map directly to devicePixelRatio - Print stylesheets that genuinely target a known DPI
- As a length — resolution units can't size boxes
dpifor screens — it works, but screens are described by device-pixel ratio, whichdppx/xmap to directlydpcm— the metric sibling ofdpi, with the same screen caveat and little practical use- Assuming
min-resolutionalone covers all HiDPI cases — test on real devices
Compact reference
| Family | Units | Anchor | Used for |
|---|---|---|---|
| Angles | deg grad rad turn |
360deg = 400grad = 2π rad = 1turn | rotate(), gradients, hsl() hue |
| Time | s ms |
1s = 1000ms — unitless is invalid | transition / animation duration & delay |
| Frequency | Hz kHz |
1kHz = 1000Hz | speech CSS — not implemented |
| Resolution | dpi dpcm dppx x |
1x = 1dppx = 96dpi | resolution media queries, image-set() |