Skip to content
Primitiv home
Framework
Consumption mode

Prose

stableSource

A flow-rhythm container — applies the .primitiv-flow context so every direct child gets density-scoped vertical spacing via a one-directional owl. Zero behaviour; renders a semantic element, composing the consumer's own element via asChild.

Playground

Density

Preview

Flow rhythm

A flow container spaces its direct children with a one-directional owl: each child gets margin above it, scaled to the density around it.

Nothing here sets a margin of its own — the rhythm comes from the context, so headings, paragraphs, lists and media all share one vertical scale.

import { Prose } from "@/components/ui/prose";
<Prose>  <h3>Flow rhythm</h3>  <p>{/* ... */}</p>  <p>{/* ... */}</p></Prose>

Density is set by a data-density ancestor — the Context system, not a Prose prop.

Installation

npx primitiv add prose

Import

import { Prose } from "@/components/ui/prose";

No headless primitive — this one ships only as a copied styled surface, so primitiv add is the only way in, whichever mode you are reading.

Props

Prose

Extends HTMLDivElement — every native attribute of that element is accepted and forwarded.

PropTypeDefaultDescription
asChildbooleanRender the single child element instead of a wrapping <div>, merging the flow class onto it — e.g. <Prose asChild><article>...</article></Prose>.
measure"false" | "true"falseCaps the column at a comfortable reading line length (~68 characters) via --primitiv-prose-measure. Opt-in: a flow context is often a whole region containing grids and media, which a reading-width cap would break.

Styling contract

--primitiv-prose-measure--primitiv-flow-tight--primitiv-flow-normal--primitiv-flow-section--primitiv-flow-region

Accessibility

  • Prose renders a plain <div> with no semantics — it is a spacing context, so a screen reader reads straight through to the content. Use asChild to make it a real <article>/<section> when the region is a landmark.
  • Capping the line length with measure is itself an accessibility win: over-long lines are hard to track back to the next line, so a reading column helps low-vision and dyslexic readers.
  • It adds no interactive or ARIA surface; the meaning is entirely in the content it wraps.

Examples

Vertical rhythm

Wrap a run of content and every direct child gets vertical spacing from the context — no margin on the elements themselves. It is a one-directional owl (* + * { margin-block-start }), so the first child has no stray top margin, and the gaps scale with the nearest data-density. Change the density above and the whole rhythm shifts.

Density

Flow rhythm

A flow container spaces its direct children with a one-directional owl: each child gets margin above it, scaled to the density around it.

Nothing here sets a margin of its own — the rhythm comes from the context, so headings, paragraphs, lists and media all share one vertical scale.

import { Prose } from "@/components/ui/prose";
<Prose>  <h3>Flow rhythm</h3>  <p>{/* ... */}</p>  <p>{/* ... */}</p></Prose>

Reading measure

measure caps the column at a comfortable reading line length (~68 characters), which keeps long-form text scannable. It is opt-in: a flow context is often a whole region holding grids and media, and a reading-width cap would break those — so you add measure only on a genuine reading column.

Density

Flow rhythm

A flow container spaces its direct children with a one-directional owl: each child gets margin above it, scaled to the density around it.

Nothing here sets a margin of its own — the rhythm comes from the context, so headings, paragraphs, lists and media all share one vertical scale.

import { Prose } from "@/components/ui/prose";
<Prose measure>  <h3>A reading column</h3>  <p>{/* long-form text, capped at the measure */}</p></Prose>

As a semantic element

asChild renders your own element with the flow context applied — an <article> or <section> that also spaces its children — rather than wrapping one in an anonymous <div>. The rhythm still lands on the direct children of that element.

Density

Flow rhythm

A flow container spaces its direct children with a one-directional owl: each child gets margin above it, scaled to the density around it.

Nothing here sets a margin of its own — the rhythm comes from the context, so headings, paragraphs, lists and media all share one vertical scale.

import { Prose } from "@/components/ui/prose";
<Prose asChild measure>  <article>    <h3>Title</h3>    <p>{/* ... */}</p>  </article></Prose>