5

Prose

Add .prose to any container to give headings and paragraphs consistent vertical spacing.

Standalone

<link rel="stylesheet" href="https://esm.sh/@erikt/ui/prose.css" />

Headings

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6
<div class="prose">
  <h1>Heading 1</h1>
  <h2>Heading 2</h2>
  <h3>Heading 3</h3>
  <h4>Heading 4</h4>
  <h5>Heading 5</h5>
  <h6>Heading 6</h6>
</div>

Lead

An <hgroup> pairs a heading with a muted subtitle. Any <p> inside is automatically dimmed.

Tasks

Here's a list of your tasks for this month.

<hgroup>
  <h2>Tasks</h2>
  <p>Here's a list of your tasks for this month.</p>
</hgroup>

Paragraphs

@erikt/ui is a minimal CSS design system. It styles native HTML elements directly, with no class names for most components.

Drop in the stylesheet and start writing semantic HTML. The defaults are sensible, and everything is customizable via CSS variables.

<div class="prose">
  <p>First paragraph.</p>
  <p>Second paragraph.</p>
</div>

Lists

There are a few points to note regarding this.

  • It is a long established fact that a reader will be distracted by the readable content of a page.
  • There are many variations of passages of Lorem Ipsum available, but most of them read like filler once you look closely. Long items like this one wrap onto several lines, so the spacing between list items matters as much as the spacing within them.
    • Some were changed by injected humour, others by randomised words that don't look even slightly believable.
    • If you use a passage, make sure nothing embarrassing is hidden in the middle of the text.
  • The majority have suffered alteration in some form.
<div class="prose">
  <p>There are a few points to note.</p>
  <ul>
    <li>First item.</li>
    <li>
      Second item.
      <ul>
        <li>Nested item.</li>
        <li>Another nested item.</li>
      </ul>
    </li>
    <li>Third item.</li>
  </ul>
</div>

Follow these steps to get started.

  1. Import the stylesheet into your project.
  2. Write semantic HTML as you normally would.
    1. Pick the element that matches the meaning of the content.
    2. Reach for a class name only when no element fits.
  3. Override CSS variables to match your brand.
<div class="prose">
  <p>Follow these steps to get started.</p>
  <ol>
    <li>Import the stylesheet into your project.</li>
    <li>
      Write semantic HTML as you normally would.
      <ol>
        <li>Pick the element that matches the meaning of the content.</li>
        <li>Reach for a class name only when no element fits.</li>
      </ol>
    </li>
    <li>Override CSS variables to match your brand.</li>
  </ol>
</div>

Blockquote

"Design is a funny word. Some people think design means how it looks. But of course, if you dig deeper, it's really how it works."
— Steve Jobs
<blockquote>
  "Design is a funny word. Some people think design means how it looks. But of
  course, if you dig deeper, it's really how it works."
  <footer>
    <cite>— Steve Jobs</cite>
  </footer>
</blockquote>

Spacing

Vertical spacing between elements is controlled by --ui-typography-spacing, which defaults to .61em.

:root {
  --ui-typography-spacing: 0.61em;
}

Article example

A realistic mix of all prose elements together.

The Case for Plain HTML

Why writing semantic markup still matters in 2025.

There is a quiet movement happening at the edges of the web. Developers, exhausted by ever-growing dependency trees, are rediscovering a simple truth: the browser already knows how to do most things.

Start with the right element

Semantic HTML is not a relic. Elements like <article>, <nav>, and <details> carry meaning that a <div> never can. Screen readers, search engines, and browser devtools all benefit.

  • Use <button> for actions, <a> for navigation.
  • Reach for <details> before writing a toggle component.
  • Let the browser handle focus management wherever possible.

Progressive enhancement

Build a solid HTML foundation first, then layer on styles and behaviour. A form that works without JavaScript is more resilient than one that requires three npm packages to render.

  1. Write the HTML structure.
  2. Add CSS for visual polish.
  3. Sprinkle JavaScript only where interaction cannot be avoided.
"The web is agreement. Every time you use a standard element you are contributing to a shared contract between authors and browsers."
Jeremy Keith

A note on accessibility

Accessible markup is not extra work added at the end. It is the natural result of writing HTML the way it was designed to be written.

<div class="prose">
  <hgroup>
    <h1>The Case for Plain HTML</h1>
    <p>Why writing semantic markup still matters in 2025.</p>
  </hgroup>
  <p>
    There is a quiet movement happening at the edges of the web. Developers,
    exhausted by ever-growing dependency trees, are rediscovering a simple
    truth:
    <a href="#">the browser already knows how to do most things</a>.
  </p>
  <h2>Start with the right element</h2>
  <p>
    Semantic HTML is not a relic. Elements like <code>&lt;article&gt;</code>,
    <code>&lt;nav&gt;</code>, and <code>&lt;details&gt;</code> carry meaning
    that a <code>&lt;div&gt;</code> never can.
  </p>
  <ul>
    <li>Use <code>&lt;button&gt;</code> for actions.</li>
    <li>Reach for <code>&lt;details&gt;</code> before writing a toggle.</li>
    <li>Let the browser handle focus management.</li>
  </ul>
  <h2>Progressive enhancement</h2>
  <ol>
    <li>Write the HTML structure.</li>
    <li>Add CSS for visual polish.</li>
    <li>Sprinkle JavaScript only where necessary.</li>
  </ol>
  <blockquote>
    "The web is agreement."
    <footer><cite>Jeremy Keith</cite></footer>
  </blockquote>
  <h3>A note on accessibility</h3>
  <p>
    Accessible markup is not extra work. It is the natural result of writing
    HTML the way it was designed to be written.
  </p>
</div>

Search 5021 icons

Type a name to find icons from the Tabler icon set.

      Move Open Esc Close