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:html or plain children
  • Links, lists, quotes, and code all get consistent spacing
Show 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
  1. First step
  2. Second step
    1. 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 prose
Heading five
A landscape placeholder
A figure with a caption.
Heading six
TokenValue
–radius-md6px
–radius-lg10px
Term
Its definition.
Another term
Another definition.
More details

Hidden until opened.


After a horizontal rule.

Show 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
  1. Ordered item
  2. Another item
A quoted aside.
const x = 1

Show 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 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: balance reads well.