WHY THIS IS A COMPONENT AND NOT A BASE STYLE
Styling h2 and p globally means every card, label and toolbar in the system inherits reading typography and has to opt back out. Most pages are interfaces, not documents. Opting in is one class; opting out is a fight.
The measure is 70ch, not a pixel width, so it survives a change of font size — a 640px column is 70 characters at one size and 45 at another.
🛑 IT STYLES THE TAGS YOU TYPED, AND NOTHING A COMPONENT BROUGHT
Fair question: what happens when a component lands inside an article? Prose styles bare p, a and ul — and a component is built out of those same tags. So a button in a paragraph used to pick up the article underline, and a list module used to pick up the article indent.
Every element rule here now matches unclassed elements only. A tag with a class on it came from a component, and a component owns its own paint. You write Markdown; Markdown emits bare tags; nothing changes for you.
Specificity was tried first and it cannot do this job. The link rule is deliberately weak so a button keeps its own colour — and it does keep its colour. It does not keep its underline, because the button never mentions one and there is no contest to win. Specificity settles who wins a property both rules set. The question was whether the rule should apply at all.
Measured across this book and both sites installing it, before the change:
44 LINKSunderlined that no author typed
1 MODAL TITLEwearing the article h2 — 36px, not 23px
32 FIGURESa lightbox slide laid out by the article
AFTER236 unclassed elements, none moved
One tag is exempt, and it is the interesting one: <pre>. The syntax highlighter puts its own classes on a fenced block, so filtering it would leave every code block unstyled. Its rule lives in code-block instead.
Adding a component to the library adds nothing to that list. What is filtered is HTML tags, and HTML closes that list for us.
THE THEMATIC BREAK WAS FOUR SHADES TOO LOUD, AND IT TOOK A MARKDOWN FILE TO FIND IT
Every other tag on this page was styled and hr was not. Nobody noticed, because nobody types one by hand. A Markdown pipeline does: three hyphens on a line, and you have a divider you never wrote.
Left to the browser under the reset, that divider is a 1px line in currentColor — which inside an article is --ds-ink-read. A site rendering its licence from Markdown drew it at #2a4655 with #e4dfd4 on every other rule beside it. It looked like a rendering fault, and in this system it was one: there is one hairline weight, and that was not it.
It reads --ds-rule now, drawn with the same border-block-start every other divider in the library uses — so a theme that restates that role moves this too. There is no margin on it: .prose is a flex column with a gap, and the space around a break is the gap, like everything else.
.prose-wide DOES NOTHING ON ITS OWN, AND .prose-full IS WHAT MAKES IT WORK
For a long time this page told you a wide child escaped the reading column, and it did not. The cap is max-inline-size on .prose itself — a child setting max-inline-size: none is still 100% of a box that is already capped.
.prose-full moves the cap from the block to its children. The article fills whatever it is given, every direct child still reads at --ds-measure, and .prose-wide on one of them finally does what its name says. That is the second specimen above.
Measured on this page, in an 1100px stage:
.PROSE770px — and its .prose-wide child, 770px
.PROSE-FULL1022px — paragraphs 770px, .prose-wide 1022px
UNDER 770PXidentical — nothing to escape from
It is a modifier rather than a change to .prose on purpose. Moving the cap for everybody changes the width of every prose block in the library and in anything installing it, which is a decision with a screenshot pass attached rather than a tidy-up.
🛑 DO NOT REACH FOR IT IF YOU CONTROL THE MARKUP
When the prose must close, let it close, and let the wide thing stand outside as a sibling. A .prose is a reading column. A grid, a gallery or a run of bands is structure rather than reading, and putting one inside a reading column and then widening it back out is arguing with a component instead of not using it.
That is a trap and not a preference, because a module dropped into a .prose cannot defend itself. feature-band is an <ol> and declares padding: 0. The rule on .prose ol wins anyway — not on specificity but on layer, since ds-components comes after ds-modules and a later layer wins regardless. The bands shipped indented 24px from every other left edge on their page.
The filter above prevents that particular one now — a feature-band is a classed <ol>, so no prose rule reaches it. The advice stands anyway, because paint was only half of it. A run of bands inside a seventy-character column is the wrong shape whatever colour it comes out.
And closing the prose no longer costs you anything. Put measure on the sibling and it keeps the reading width. That primitive exists for this exact sentence — the rule used to cost 350 pixels, which is why nobody followed it.
So what is it for? The case where closing is not available: a Markdown body. render(entry) hands back one sealed component, so the whole article arrives inside a single block and there is no seam to close at. That is a real constraint rather than a preference, and it is the only one this modifier exists to answer.
One edge, and it is a real one: it reaches direct children only. Wrap something in a <div> and the wrapper takes the measure, with everything inside it stuck in that box. Put .prose-wide on the wrapper, or do not wrap.
🛑 THERE WAS NO LIST MARKER AT ALL UNTIL 25 AUGUST 2026
The reset sets list-style: none on every <ul> and <ol>. This file set a left padding and never put a marker back. Measured on this very page: list-style-type: none with 24px of padding — so every list in every article was an unmarked block, indented for no visible reason.
It survived because it looked deliberate. An unstyled list and a styled-flush list are the same picture until you go looking for the bullet that is not there.
The default now is a disc, drawn inside the content box, with no indent at all — so a wrapped line runs back to the same left edge as every paragraph around it. That is a taste, which is why all five parts of it are yours to change:
--prose-markerthe ul glyph. A keyword, or a string
--prose-marker-imagethe ul marker as a picture. A data URI
--prose-marker-orderedthe ol counter: lower-roman, upper-alpha
--prose-marker-positioninside, the default, or outside
--prose-indentthe list’s own padding. Zero by default
Custom properties inherit, so set them wherever suits: on :root for a whole site, on one article, or on a single list.
.prose {
--prose-marker-image: url("data:image/svg+xml;base64,PHN2Zy…");
}
Two things will bite you, and both are about the picture rather than the CSS.
Give the SVG a width and a height before you encode it. A marker image draws at its own intrinsic size and no CSS property resizes one — there is no background-size to reach for, because it is not a background. An SVG carrying only a viewBox has no intrinsic size, so the browser falls back to 300×150 and you get a marker taller than the paragraph next to it. Two attributes on the root element are the whole fix.
A picture cannot follow your theme. The colour is baked into the data URI and no variable reaches inside it. Measured on the specimen above: the default disc moved from rgb(198, 210, 218) to rgb(42, 70, 85) between the two themes, and the chevron stayed exactly the amber it was drawn as. Pick a colour that survives both grounds, or reach for a string marker instead — those paint in the text colour and follow the palette for free.
A FIGURE GETS ITS CAPTION FOR FREE
Write <figure> and <figcaption> and there is no class to remember — which is the same call this component makes about every other tag an author types. The figure stacks its parts; the caption drops to --ds-text-caption and --ds-ink-faint.
It had no rule at all until 22 August 2026, and the failure was the quiet kind: a caption inherited the column's own size and ink, so it measured 17px against 17px in the identical colour and read as the next sentence rather than as a line about the picture. The values are placeholder-caption’s, because that is already this system’s spelling of “a line under a picture” — and --ds-text-caption names figcaptions first in its own declaration. The most literal figcaption in the library was the one not reading it.
A MARKDOWN FILE MAY OPEN WITH ITS OWN TITLE
h1 is styled here as well as h2 to h4, because the most natural thing an author writes at the top of an article is # followed by the title. It was missing, and the failure was a quiet one: an unstyled heading inherits the body family at the base size, so a title rendered smaller than the lead beneath it and read as a deliberate eyebrow rather than a mistake.
It carries no top margin, unlike the other steps: a title opens the flow, and a margin on the first child is the collapse the rhythm exists to avoid.