Media and prose
tier 2 — classesContent the app did not author: an attachment, an article body, or a value the reader is not allowed to see.
Media frame
A bounded box for an image or embed whose size you do not control.
aspect-ratio plus object-fit:contain is what stops the page
reflowing as images arrive — the box has its height before the image loads.
contain rather than cover, because these are documents and
cropping a screenshot removes the part somebody needed.
.media-frame--cover is there for a decorative banner, where cropping is the
right answer. Use <figure> with .media-figure when there is
a caption — an image whose meaning is in its caption still needs a real
alt.
<div class="gallery" style="max-width:520px">
<figure class="media-figure">
<div class="media-frame media-frame--square">
<i class="ri-image-line" style="font-size:28px; color:var(--border-hover)"></i>
</div>
<figcaption>Square — a thumbnail</figcaption>
</figure>
<figure class="media-figure">
<div class="media-frame">
<i class="ri-image-line" style="font-size:28px; color:var(--border-hover)"></i>
</div>
<figcaption>16:10 — the default</figcaption>
</figure>
<figure class="media-figure">
<div class="media-frame media-frame--wide">
<i class="ri-image-line" style="font-size:28px; color:var(--border-hover)"></i>
</div>
<figcaption>21:9 — a banner</figcaption>
</figure>
</div>
Prose
Long-form text rendered from a source the app does not control — a release note, a
knowledge-base article, an e-mail body.
.markdown-body already covers rendered Markdown;
.prose is the same job when the HTML did not come from Markdown. They look
identical, so an app has one typographic voice rather than two.
The measure is capped at 72 characters. Past roughly that the eye loses the line it is
returning to, which no amount of line-height fixes. A table inside untrusted prose gets
display:block; overflow-x:auto, because its own content sets its minimum width
and it is the one thing that will otherwise break the page.
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
<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>
</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. A blur or a low opacity leaves the real text in the DOM, where it is readable by view-source, by a screen reader, and by anyone who selects it. This class marks a placeholder the server sent in place of the value. The value never reaches the browser. If it 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> not shown</span>
</div>
<div class="kv">
<span class="k">Salary band</span>
<span class="redacted"><i class="ri-lock-line"></i> requires HR role</span>
</div>
</div>
</div>
A gallery of attachments
.gallery is a breakpoint-free grid of thumbnails. Each tile is a real
<button> or <a> directly inside it — never a
<div> with a click handler — and the stylesheet gives the tile's hover and
focus ring to the .media-frame inside it.
Inside the gallery every frame is square whatever aspect modifier it carries, so a row of mixed attachments lines up. The file name is part of the tile, because an icon does not say which document it is.
<div class="gallery" style="max-width:520px">
<button class="btn-ghost" type="button" style="padding:0; text-align:start">
<span class="media-frame">
<i class="ri-file-pdf-2-line" style="font-size:26px; color:var(--border-hover)"></i>
</span>
<span class="text-sm text-muted text-truncate dr-mt-1">pod-ord-4209.pdf</span>
</button>
<button class="btn-ghost" type="button" style="padding:0; text-align:start">
<span class="media-frame">
<i class="ri-image-line" style="font-size:26px; color:var(--border-hover)"></i>
</span>
<span class="text-sm text-muted text-truncate dr-mt-1">pallet-label.png</span>
</button>
<button class="btn-ghost" type="button" style="padding:0; text-align:start">
<span class="media-frame">
<i class="ri-mail-line" style="font-size:26px; color:var(--border-hover)"></i>
</span>
<span class="text-sm text-muted text-truncate dr-mt-1">customer-reply.eml</span>
</button>
<button class="btn-ghost" type="button" style="padding:0; text-align:start">
<span class="media-frame">
<i class="ri-file-excel-2-line" style="font-size:26px; color:var(--border-hover)"></i>
</span>
<span class="text-sm text-muted text-truncate dr-mt-1">stock-check.xlsx</span>
</button>
</div>