ch, ex, cap & other type units
CSS has a set of units that are relative to the font's own metrics — the actual dimensions of glyphs in the current typeface. These units are invaluable for typography-aware sizing: making a container exactly wide enough for a comfortable line length, sizing elements relative to the x-height of the font, or scaling decorations to match uppercase letters. They're niche compared to rem and em, but knowing them gives you precision that's hard to achieve otherwise.
The type-relative units
Unit | Based on | Browser support |
|---|---|---|
| The advance width of the | Excellent — all modern browsers |
| The x-height of the font — height of a lowercase | Excellent |
| The cap height — height of an uppercase letter | Good — Chrome/Edge/Firefox, partial Safari |
| The advance width of the CJK water ideograph | Growing — intended for CJK text |
| The computed | Growing — Chrome 109+, Firefox 120+ |
| The computed | Growing |
ch — character width unit
1ch is the width of the 0 (zero) glyph in the current font. Because 0 is a representative average-width character in most fonts, ch is a great proxy for "one character width". This makes it extremely useful for setting optimal line lengths and sizing form inputs:
/* Optimal reading line length: 60–80 characters */
.prose {
max-width: 65ch;
/* This adapts to the font — a wider font gets a physically wider max-width */
}
/* A password input that fits exactly 8 characters */
input[type="password"] {
width: 8ch; /* plus a bit for borders — approximate, not pixel-perfect */
}
/* A fixed-width code editor column guide */
.code-editor {
max-width: 80ch;
font-family: 'Fira Code', monospace;
/* In a monospace font, 80ch is exactly 80 characters wide */
}
/* Note: in proportional fonts, ch is based on '0' width,
not the average width of all characters — vary by font */ex — x-height unit
1ex equals the x-height — the height of the lowercase letter x in the current font. Different fonts have dramatically different x-heights (the ratio of x-height to cap height is called the x-height ratio), so ex adapts to the specific typeface:
/* Align an icon to the visual centre of text using ex */
.icon {
height: 1ex;
width: 1ex;
vertical-align: middle; /* combined with ex-based height, optically centres */
}
/* A slightly raised small detail — relative to text height */
.badge {
font-size: 0.75em;
vertical-align: 0.5ex; /* raises by half an x-height */
}
/* Approximate relationship: 1ex ≈ 0.5em in many fonts */cap — cap height unit
1cap equals the height of uppercase letters (the cap height) in the current font. Useful for sizing decorative elements that should visually align with headings:
/* A vertical rule that matches the exact height of a heading */
.section-header {
display: flex;
align-items: center;
gap: 1rem;
}
.section-header::before {
content: "";
display: block;
width: 4px;
height: 1cap; /* exactly as tall as the capitalised letters */
background: #0066cc;
border-radius: 2px;
}
/* Icon that matches the visual size of adjacent uppercase text */
.label-icon {
width: 1cap;
height: 1cap;
}lh and rlh — line height units
/* lh = computed line-height of the current element */
.pull-quote {
margin-block: 1lh; /* vertical margin equals exactly one text line */
}
/* rlh = computed line-height of the root element */
:root { line-height: 1.5; font-size: 16px; }
/* rlh = 24px */
.section {
padding-block: 2rlh; /* top/bottom padding = 2 root line-heights = 48px */
}
/* Keeps spacing grid-aligned — everything is a multiple of the line height */Practical use: the perfect prose column
Combining ch for line length with ex for vertical rhythm creates typography that adapts to the typeface:
/* A typography block that adapts to any serif or sans-serif font */
.prose {
max-width: 65ch; /* comfortable reading width in any font */
font-size: 1.125rem;
line-height: 1.7;
margin-inline: auto;
}
.prose h2 {
margin-top: 2lh; /* two lines of space before each heading */
margin-bottom: 0.5lh; /* half a line after */
}
.prose p + p {
margin-top: 0; /* no extra gap between consecutive paragraphs */
}
.prose p {
margin-bottom: 1lh; /* one full line between paragraphs */
}