Prose
Typography styling for a block of rich, CMS, or markdown HTML you don't control element by element.
Last updated
Getting started
Prose styles any block of rich or CMS-authored HTML you don’t hand-style element by element — token-driven, so it themes with everything else.
- Works with
set:htmlor plain children - Links, lists, quotes, and code all get consistent spacing
Show codeHide code
<Prose>
<h2>Getting started</h2>
<p>Prose styles any block of rich or CMS-authored HTML you don't hand-style
element by element — token-driven, so it themes with everything else.</p>
<ul>
<li>Works with <code>set:html</code> or plain children</li>
<li>Links, lists, quotes, and code all get consistent spacing</li>
</ul>
</Prose>Add
npx @ava-ui/apogee add prose
No Apogee component dependencies, no icons. Zero JS — a global,
.apogee-prose-scoped CSS layer.
Usage
---
import Prose from '@/components/apogee/Prose/Prose.astro'
---
<Prose set:html={article.body} />
API
@composable — exposed to Content Editors in AVA. Accepts any other attribute
(e.g. set:html, id), which passes through to the wrapper <div>.
| Prop | Type | Default | Description |
|---|---|---|---|
class |
string |
— | Extra classes on the wrapper, merged last. |
States
Static: no interactive states. Links inside thicken their underline on hover.
Examples
Every element a page can contain, styled by Prose:
Heading one
A lead paragraph that introduces the article in a slightly larger, softer voice.
Heading two
Body copy with strong, emphasis, a link, inline code, ⌘K, a highlight and an ARIA abbreviation.
Small print under a paragraph.
Heading three
- Unordered item
- Another item
- Nested item
- First step
- Second step
- Sub-step
Heading four
The best way to predict the future is to invent it.
Alan Kay
npm install @ava-ui/apogee
npx @ava-ui/apogee add proseHeading five
Heading six
| Token | Value |
|---|---|
| –radius-md | 6px |
| –radius-lg | 10px |
- Term
- Its definition.
- Another term
- Another definition.
More details
Hidden until opened.
After a horizontal rule.
Show codeHide code
<Prose>…every element…</Prose>Trusted/sanitized CMS or markdown HTML:
<Prose set:html={renderedMarkdown} />
Authored inline as children instead:
<Prose>
<h2>Release notes</h2>
<p>What changed in this version.</p>
</Prose>
The styled element catalogue — headings, links, emphasis, lists, quotes, code:
Heading two
Heading three
A paragraph with a link and bold text.
- Unordered item
- Another item
- Ordered item
- Another item
A quoted aside.
const x = 1Show codeHide code
<Prose>
<h2>Heading two</h2>
<h3>Heading three</h3>
<p>A paragraph with a <a href="#">link</a> and <strong>bold text</strong>.</p>
<ul>
<li>Unordered item</li>
<li>Another item</li>
</ul>
<ol>
<li>Ordered item</li>
<li>Another item</li>
</ol>
<blockquote>A quoted aside.</blockquote>
<pre><code>const x = 1</code></pre>
<hr />
</Prose>Opting a block out
Wrap a callout or a card in .not-prose and Prose leaves it alone; style it with utilities:
Body text.
Conclusion
Kept out of the prose styles.
Show codeHide code
<Prose>
<p>Body text.</p>
<div class="not-prose mt-8 rounded-2xl bg-neutral-faded p-6">
<h2 class="text-xl font-semibold">Conclusion</h2>
<p class="mt-2 text-neutral-faded">Kept out of the prose styles.</p>
</div>
</Prose>Accessibility
- Native semantics, no extra ARIA needed — Prose is a CSS layer over your own markup; semantics come entirely from the HTML you put inside (or the CMS HTML you render).
- Using
set:html: sanitize the source yourself — Prose only styles, it never sanitizes.
Tokens
| Token | Used for |
|---|---|
--foreground-neutral |
Body + heading text |
--foreground-primary |
Link colour and hover |
--foreground-neutral-faded |
List marker colour, blockquote text |
--border-primary |
Blockquote rule |
--border-neutral |
<hr>, table rules, kbd outline |
--background-neutral-faded |
Inline code and pre background |
--background-elevation-raised |
kbd background |
--background-warning-faded |
mark highlight |
--font-serif / --font-mono |
Blockquote / code and kbd |
--radius-sm |
Inline code, kbd and mark corners |
--radius-lg / --radius-md |
pre / img, video and iframe corners |
Do / Don’t
- Do use it for any block of HTML you don’t style element by element — CMS output, rendered markdown, long-form copy.
- Do sanitize untrusted HTML before
set:html— Prose doesn’t clean input, it only styles it. - Don’t reach for Prose to style one isolated element (a lone
<p>or<a>) — style it directly instead. - Don’t nest interactive app UI (buttons, forms) inside — it’s a typography layer, not a layout system.
Content
- No wording rules of its own — copy follows whatever produced it (CMS,
markdown, hand-authored). Keep headings short enough that the built-in
text-wrap: balancereads well.