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

Formula
360deg = 400grad = 2π rad = 1turn
Relative to
A full circle
Support
Universal

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.

Demo — One angle, four units
angle90deg
deg90deg
grad100grad
rad1.5708rad
turn0.25turn

All 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 */
Use when
  • Rotations: transform: rotate(45deg)
  • Gradients: linear-gradient(135deg, …), conic-gradient()
  • Hue channel in hsl() / oklch(): hsl(200deg …)
  • turn for clean fractions: 0.5turn, 0.25turn
  • rad when the value comes straight from JS trig
Avoid when
  • Defaulting to grad for shared code — prefer deg, which more readers parse at a glance
  • Mixing angle units within one codebase without reason — pick one and stay consistent

Time — s and ms

Formula
1s = 1000ms
Relative to
Seconds
Support
Universal

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.

Demo — Transition duration in s and ms
duration600ms
ms 600mss 0.6sare the same value

1s = 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.

Use when
  • Transition and animation durations: 0.2s, 200ms
  • Delays: transition-delay, animation-delay
  • ms for short UI timings; s for longer sequences
  • Negative time on animation-delay to start mid-animation
Avoid when
  • Unitless time — 1 is invalid; always write 1s/1000ms
  • Time anywhere outside duration/delay properties — it's not a length

Frequency — Hz and kHz

Formula
1kHz = 1000Hz
Relative to
Cycles per second
Support
Not implemented

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.

Use when
  • Know they exist so the spec's unit list isn't a mystery
  • Recognize them if you encounter very old aural stylesheets
Avoid when
  • 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

Formula
1x = 1dppx = 96dpi
Relative to
Pixel density of the output device
Support
Universal

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).

Demo — Your display's resolution, in CSS units
devicePixelRatio
= dppx
= x
≈ dpi
1 CSS px on a 1× display
1 device pixel
1 CSS px on a 2× display
4 device pixels (2 × 2)

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
  );
}
Use when
  • Density media queries: @media (min-resolution: 2dppx)
  • Resolution-aware images: image-set() with 1x / 2x
  • Prefer dppx / x — they map directly to devicePixelRatio
  • Print stylesheets that genuinely target a known DPI
Avoid when
  • As a length — resolution units can't size boxes
  • dpi for screens — it works, but screens are described by device-pixel ratio, which dppx/x map to directly
  • dpcm — the metric sibling of dpi, with the same screen caveat and little practical use
  • Assuming min-resolution alone 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()

Sources