Prose
tier 2 — classes
Long-form text the app did not author — a release note, a knowledge-base article, an
e-mail body — and a value the reader is not allowed to see. Rendered Markdown has its own
class, .markdown-body, and looks identical.
Prose
.prose normalises headings, paragraphs, lists, quotes, code and links onto the
tokens, for HTML that did not come from Markdown. The 72-character measure sits on
the element carrying the class, so where the prose is a panel —
class="card-body prose" — set --prose-measure: none on the
box and put the class on an inner wrapper.
Release 0.4.0
This release adds the overlay family and the collection classes. Nothing was renamed, so an upgrade needs no app changes.
Every class name added is listed below, so you can grep your own stylesheets before bumping.
.drawerand.sheet.palette,.spotlight-hole
An image inside prose is capped at the measure and rounded by
.prose img, so an article body cannot be widened by whatever the author
pasted into it.
<div class="prose">
<h2>Release 0.4.0</h2>
<p>
This release adds the overlay family and the collection classes. Nothing was renamed,
so an upgrade needs no app changes.
</p>
<blockquote>Every class name added is listed below, so you can grep your own
stylesheets before bumping.</blockquote>
<ul>
<li><code>.drawer</code> and <code>.sheet</code></li>
<li><code>.palette</code>, <code>.spotlight-hole</code></li>
</ul>
<figure class="media-figure">
<div class="media-frame media-frame--wide media-frame--cover">
<img src="https://images.unsplash.com/photo-1627309366653-2dedc084cdf1?w=840&h=360&fit=crop&q=70&auto=format"
width="840" height="360" loading="lazy"
alt="A warehouse interior: orange and blue racking down one side of an open floor" />
</div>
<figcaption>The floor the new overlay classes were built for. Photo by Jacques Dillies on Unsplash.</figcaption>
</figure>
<p>
An image inside prose is capped at the measure and rounded by
<code>.prose img</code>, so an article body cannot be widened by whatever the author
pasted into it.
</p>
</div>
HTML from somewhere else
.prose--foreign adds containment on top of
.prose's typography: what a 3000px-wide table, an unbroken 400-character URL or
a 4000px image does to the card it landed in. Add it to anything sanitized rather than
authored — a ticketing system's journal entry, a forwarded mail body, a CMS field — and note
that it contains but does not sanitize: the same server-side pass has to add
tabindex="0" to a table that scrolls, or a keyboard cannot reach the off-screen
columns, and role="table" to put back the role
display: block drops.
Reply received from the customer, forwarded from https://helpdesk.example.gov/ticket/4182/comments?thread=a7f3c1e9b2&expand=attachments&from=notification
| Host | Mailbox | Database | Quota | Used | Last logon |
|---|---|---|---|---|---|
| EXCH-02 | service-desk | DB-PRIMARY-01 | 50 GB | 49.8 GB | 2026-08-28 07:41 |
| EXCH-02 | invoices | DB-PRIMARY-01 | 50 GB | 31.2 GB | 2026-08-28 06:12 |
Set-Mailbox -Identity service-desk -ProhibitSendQuota 75GB -ProhibitSendReceiveQuota 80GB -IssueWarningQuota 70GB
<div class="card" style="max-width:420px">
<div class="card-body">
<div class="prose prose--foreign">
<p>
Reply received from the customer, forwarded from
https://helpdesk.example.gov/ticket/4182/comments?thread=a7f3c1e9b2&expand=attachments&from=notification
</p>
<!-- tabindex and role are on the table because the CONTAINMENT costs both:
a scroll container needs keyboard access, and display: block drops a
table's own role. Foreign HTML arrives without either, so the same
sanitizer that cleans it adds them. -->
<table tabindex="0" role="table" aria-label="Mailbox quotas on EXCH-02">
<thead>
<tr><th>Host</th><th>Mailbox</th><th>Database</th><th>Quota</th><th>Used</th><th>Last logon</th></tr>
</thead>
<tbody>
<tr><td>EXCH-02</td><td>service-desk</td><td>DB-PRIMARY-01</td><td>50 GB</td><td>49.8 GB</td><td>2026-08-28 07:41</td></tr>
<tr><td>EXCH-02</td><td>invoices</td><td>DB-PRIMARY-01</td><td>50 GB</td><td>31.2 GB</td><td>2026-08-28 06:12</td></tr>
</tbody>
</table>
<pre>Set-Mailbox -Identity service-desk -ProhibitSendQuota 75GB -ProhibitSendReceiveQuota 80GB -IssueWarningQuota 70GB</pre>
</div>
</div>
</div>
What changed
<ins> and <del> mark the words added and removed
between two versions of a text — a policy, a manual page, a contract — in prose
or in any running text, with no class. The tint is said again by an underline and a
strike-through, so the change survives forced colours, print and a reader who cannot tell
green from red; a change that wraps keeps both ends on every line, and the line height does
not move. What a screen reader says is yours: a .visually-hidden "added:" or
"removed:" inside the element, in your language.
Returns policy — version 4 against version 3
Items can be returned within
removed: 14 days
added: 30 days
of delivery. Refunds are paid to the original payment method
added: within five working days of the item reaching the warehouse, and the customer is told by e-mail when it is sent.
removed: Opened software cannot be returned.
<div class="card" style="max-width:560px">
<div class="card-head">
<h3>Returns policy — version 4 against version 3</h3>
</div>
<div class="card-body prose">
<p>
Items can be returned within
<del><span class="visually-hidden">removed: </span>14 days</del>
<ins><span class="visually-hidden">added: </span>30 days</ins>
of delivery. Refunds are paid to the original payment method
<ins><span class="visually-hidden">added: </span>within five working days of the item reaching the warehouse, and the customer is told by e-mail when it is sent</ins>.
</p>
<p>
<del><span class="visually-hidden">removed: </span>Opened software cannot be returned.</del>
</p>
</div>
</div>
Redacted values
A value deliberately withheld — a secret, a masked identifier, a field the reader's role cannot see. It must not merely look hidden: this class marks a placeholder the server sent in place of the value, so if the real text is in the HTML it is not redacted, whatever it looks like.
<div class="card" style="max-width:420px">
<div class="card-body">
<div class="kv"><span class="k">Client ID</span><span class="v">orders-console-01</span></div>
<div class="kv">
<span class="k">Client secret</span>
<span class="redacted"><i class="ri-eye-off-line"></i> <span>not shown</span></span>
</div>
<div class="kv">
<span class="k">Salary band</span>
<span class="redacted"><i class="ri-lock-line"></i> <span>requires HR role</span></span>
</div>
</div>
</div>