RoadieRoadie
  • Home
  • Brand
  • Foundations
  • Content
  • Tokens
  • Components
  • Charts
  • Widgets
  • Appearance
HomeBrandFoundations
Appearance

Foundations

  • Visual

    • Colors
    • Elevation
    • Iconography
    • Shape
    • Theming
  • Content

    • Date and time
    • Layout
    • Prose styles
    • Typography
  • Behaviour

    • Accessibility
    • Interactions
    • Motion
    • View transitions
  • Building apps

    • Forms
    • Linking
    • Navigation
    • Performance
    • Records modelAlpha
    • Tables

Prose styles

Docs updated
20 Oct 2026

ReferenceTypography tokens

Rich text from a CMS, Markdown, or MDX arrives as bare HTML elements with no classes. Add .prose to the element that holds it, and the headings, paragraphs, lists, tables, code, quotes, rules, figures, and keys inside are styled from Roadie tokens, and em, i, and cite show in Intermission's italic face. The class ships in @oztix/roadie-core/css, so it needs no import of its own. In React, the Prose component adds the class and sets its size.

<article className='prose'>
<h2>Ochre Kite Weekender 2026</h2>
<p>
Gates open at <strong>11am</strong> on Saturday. Bring a printed or{' '}
<a href='#'>mobile ticket</a>, and press <kbd>Ctrl</kbd> + <kbd>P</kbd> to
print yours.

Element rules sit inside :where() in the components layer. A utility on a child, such as text-subtle on a paragraph, always wins over the sheet.

Rhythm

Space only goes above an element, never below, so a block's first child sits flush with the top of its container. Every gap is a multiple of --prose-flow, which is 1.25em by default. The property is registered, so the em resolves once, at the prose body size, and a larger heading doesn't bring a larger gap. Space above a heading runs from 1.2 flows for h5 and h6 to 3 flows for h1, and the block after it sits half a flow below, so each heading reads as part of the section it opens.

Measure

Each direct child of .prose is capped at --prose-measure, which is 65ch by default. The property is registered, so ch resolves once, at the prose body size, rather than at each heading's size. Set it to another length to change the cap, or to none to remove it.

Add .prose-bleed to a direct child that should span the full width, such as a wide image or a table.

<div className='prose [--prose-measure:32ch]'>
<p>
This paragraph stops at the measure, which this example sets to 32ch so the
cap shows in a narrow preview.
</p>
<p className='prose-bleed'>

Tables

Tables shrink to fit their content. Wrap a table that might be wider than its container in .prose-scroll, which scrolls sideways instead of overflowing the page. Each column in a scrolling table stays at least 9em wide, so when the columns can't all fit, the table scrolls rather than squeezing text to a word or two a line. A long cell wraps at the prose measure. Inline code in a scrolling table stays on one line, so a token such as rounded-sm never splits. Outside .prose-scroll, inline code still breaks where it must, so the table fits its column.

The Prose component wraps bare tables in .prose-scroll for you, and makes each one reachable with the keyboard while it overflows. With the class alone, use useScrollRegion for that.

<div className='prose'>
<div className='prose-scroll'>
<table>
<thead>
<tr>
<th>Session</th>

Opting out

Add .not-prose or data-not-prose to an element inside .prose to keep the sheet away from it and everything inside it. Use it around components, previews, and anything else that brings its own styles. An escaped direct child still takes its place in the rhythm and is still capped at --prose-measure. Add .prose-bleed as well to let it run wider.

<div className='prose'>
<p>This paragraph is typeset.</p>
<div className='not-prose grid gap-1 rounded-xl emphasis-raised p-4'>
<p className='text-display-ui-6 text-strong'>A card keeps its own styles</p>
<ul>
<li>No bullets or indent here</li>

A .prose inside an escaped element stays unstyled, so put the escape around the parts that need it, not around a whole region that also holds prose.

Variables

Set these on the .prose element to adjust the type without new classes.

VariableDefaultSets
--prose-sizevar(--text-base)Body font size
--prose-leadingvar(--leading-prose)Body line height
--prose-flow1.25emThe gap every other gap derives from
--prose-measure65chMaximum width of each direct child
--prose-h1-size to --prose-h6-sizeThe text-display-ui-1 to -6 sizesHeading font sizes
--prose-h1-weight to --prose-h6-weightThe text-display-ui-1 to -6 weightsHeading font weights

Colour and dark mode

Colours come from the intent tokens, so .prose follows the intent and the light or dark scheme it sits in, with no rules of its own for dark mode. Links inherit the text colour with an accent underline, whatever intent they sit in, and grow it into a tint on hover, the same as Link at its default emphasis. Under forced colours, inline code and code blocks get a border.

The sheet borrows its approach from typeset.css by shadcn, under the MIT licence.

Previous page← LayoutNext pageTypography →