Prose
The bare reset from elements.css
grounds raw HTML in the design system, but it deliberately ships zero vertical rhythm —
no heading margins, no paragraph spacing, no list padding. Drop
class="hp-prose" on any container to opt into rhythm for long-form content.
When to reach for it. Article bodies, README renders, MDX output,
docs prose, changelog summaries — anywhere a block of HTML wants to read like a
document. Not for app chrome (login forms, dashboards, settings panes).
Side-by-side
Same HTML, with and without the wrapper. Both screens use the same elements.css
baseline; .hp-prose only adds the rhythm.
The hex stencil
Every hex has a bounding box of w × (w · 2/√3). Axial
coordinates project to pixels as x = w · (q + r/2),
y = w · √3/2 · r.
The grid uses an effective cell width of w − hex-stroke so
adjacent hexes' stroke bands coincide into a single shared edge.
- Pointy-top is the default orientation.
- Flat-top kicks in at
size="md". - Bond detection is opt-in via the
bondable attribute.
<body>
<div>
<h3>The hex stencil</h3>
<p>Every hex has a bounding box of <code>w × (w · 2/√3)</code>...</p>
<p>The grid uses an effective cell width...</p>
<ul>
<li>Pointy-top is the default orientation.</li>
<li>Flat-top kicks in at <code>size="md"</code>.</li>
</ul>
</div>
</body> The hex stencil
Every hex has a bounding box of w × (w · 2/√3). Axial
coordinates project to pixels as x = w · (q + r/2),
y = w · √3/2 · r.
The grid uses an effective cell width of w − hex-stroke so
adjacent hexes' stroke bands coincide into a single shared edge.
- Pointy-top is the default orientation.
- Flat-top kicks in at
size="md". - Bond detection is opt-in via the
bondable attribute.
<body>
<div class="hp-prose">
<h3>The hex stencil</h3>
<p>Every hex has a bounding box of <code>w × (w · 2/√3)</code>...</p>
<p>The grid uses an effective cell width...</p>
<ul>
<li>Pointy-top is the default orientation.</li>
<li>Flat-top kicks in at <code>size="md"</code>.</li>
</ul>
</div>
</body> What .hp-prose adds
The rhythm rules in plain English:
- Block rhythm.
> * + * sets a top margin of
--hp-md between siblings, so paragraphs, lists, and blocks of code
breathe.
- Heading margins. Headings get
--hp-xl top + --hp-sm bottom,
so a new section visibly separates from the prose above it. The first child resets
to zero so a heading at the top of the wrapper doesn't push everything down.
- List indentation.
ul / ol get
padding-left: --hp-lg + bullet marker tint. Nested lists tighten so
they hug their parent item.
- Blockquote + table + figure + pre margins. Each block-level
content surface gets matching
--hp-md outer margins for consistent
cadence.
- Max-width: 70ch. Caps the line length for readability. Override
on the wrapper itself if you need wider content.
Heading rhythm in action
The Foundation
Subsection right under
Two headings stacked back-to-back don't get the full
--hp-xl gap — they tighten so a subtitle hugs its parent. Below the
second heading, paragraphs resume normal rhythm.
A follow-up paragraph spaces normally — --hp-md from the previous
block.
The next subsection
And a new section starts again.
<body>
<article class="hp-prose">
<h2>The Foundation</h2>
<h3>Subsection right under</h3>
<p>Two headings stacked back-to-back tighten...</p>
<p>A follow-up paragraph spaces normally...</p>
<h3>The next subsection</h3>
<p>And a new section starts again.</p>
</article>
</body> When NOT to use it
- App chrome. A settings pane with a grid of toggles, a dashboard
KPI row, a form layout — these are bespoke per-screen and don't want forced
70ch caps or sibling margins.
- Component internals.
<hp-*> elements own their
shadow DOM; .hp-prose can't and shouldn't reach into them.
- Marketing surfaces with bespoke layout. A landing-page hero with a
hex-grid backdrop and centred CTA isn't prose — author it as page chrome and
reach for hp-* components.
Wrap the content surface, not the page shell. A typical MDX layout sets
<article class="hp-prose"> around the rendered body and leaves
nav / sidebar / footer outside the wrapper.
Contents