tidy-typography.css is a reset that only touches the text
Iris Calderón
You drop a heading into a 300px sidebar. Your fluid type scale runs on vw, so the heading sizes itself for the whole window. It comes out huge. It wraps onto four lines, and the last line is one word. Then the webfont arrives and the block jumps again.
Three bugs. All typography. None of them is fixed by the margin-and-box-sizing reset you already ship.
Stu Robson's new CSS-Tricks article goes after exactly that layer. He has collected the useful modern type declarations into one default stylesheet, tidy-typography.css, covering rendering and fallback rules, fluid sizing, vertical rhythm, wrapping, hyphenation and OpenType detail. Take the whole file or take the one declaration that fixes today's irritation. He is upfront that some of it is experimental and that support is uneven, so it needs guardrails rather than blind inclusion.
Size against the column
Robson's point is simple: viewport-based fluid type has no idea how wide its element is. Swap vw for cqi and the scale reads the container instead.
.article-container {
container-type: inline-size;
}
:root {
--step-0: clamp(1rem, 0.9rem + 1cqi, 1.125rem);
--step-3: clamp(2rem, 1.6rem + 3cqi, 2.75rem);
}
The same heading now fits an article column and a 300px sidebar, without a media query per component.
One thing to check in devtools. A cqi length resolves against the nearest container above the element that uses it, and with no container it falls back to the small viewport size. The finished file sets font-size: var(--step-0) on body, which sits above any .article-container. So the headings inside the column get container math. Body copy gets viewport math inside a narrow clamp. That is fine. Just know which one you are looking at.
Trim the line box
line-height adds half-leading above the cap height and below the baseline. You never notice until text has to line up tightly against an icon or a box edge, and then you reach for a negative margin tuned to one font. Robson's replacement measures from the font metrics:
:is(h1, h2, h3, h4, h5, h6), p, blockquote, figcaption, li {
text-box: trim-both cap alphabetic;
}
The article also shows the proposed :heading pseudo-class from Selectors Level 5. Robson says it has not landed in browsers and belongs behind @supports selector(:heading). Take that seriously. A plain selector list is all-or-nothing, so one selector the parser doesn't know drops the whole rule, p and li included. The finished file spells the headings out with :is(), which is the version to copy.
Wrapping, handed back to CSS
p, li, dd {
text-wrap: pretty;
max-width: 65ch;
}
[contenteditable="true"] {
text-wrap: stable;
}
Headings, blockquotes and figcaptions get text-wrap: balance, which Robson says works best on blocks of up to about six lines. pretty avoids a stranded last word without rebalancing the whole paragraph. stable stops editable text from reshuffling the paragraph on every new word. Both replace the JavaScript orphan fixers. Delete them.
Hyphenation gets the same tuning. hyphens: auto needs a correct lang attribute, because without a dictionary the browser can't hyphenate anything. On top of it Robson sets hyphenate-limit-chars: 6 3 3, hyphenate-limit-lines: 2, hyphenate-limit-zone: 8% and hyphenate-limit-last: always, and adds the -webkit- equivalents because Safari needs them. Code, kbd, samp, var and headings go back to hyphens: manual. A broken code word can change its meaning.
Let the font do the fine work
This is the part I'd steal first.
font-synthesis: nonestops the browser inventing a bold or italic the family doesn't have.font-size-adjust: from-fontuses font metrics to shrink the visible jump when the fallback swaps for the webfont.size-adjust: 105%inside an@font-facescales a monospace face to match the x-height of the prose around it, without touchingfont-size.- A
--vf-gradcustom property drops to-50underprefers-color-scheme: darkand feedsfont-variation-settings: "GRAD" var(--vf-grad, 0). Light text on a dark background reads heavier, and the grade axis thins it without changing glyph width. Fonts without the axis ignore it. font-variant-position: subandsuper, wrapped in@supports, use the font's real sub and superscript glyphs. Robson warns that most typefaces only include numerals for these, so check your font first.
The locale quote rules sit inside :where(:lang(en)) and friends. That's zero specificity, so a project overrides them with one class. :is() keeps the specificity of its most specific argument. That's the trade between the two.
Where to draw the line
Robson's closing caveat is the honest one. Unsupported declarations are ignored, so the file can go in gradually, but that only helps when the unenhanced text already looks acceptable. He expects support to keep moving, especially for text-wrap and text-box-trim. And a news article, a marketing page and an editable product UI don't ask the same things of their text.
So don't paste all of it. Start with font-synthesis: none and text-wrap: pretty on your next project. Then open the sidebar again and see whether that heading still needs its media query.
Source: CSS-Tricks (css-tricks.com)