GitHub

prose

Typography wrapper for rich text you don't control: CMS output, markdown-rendered HTML, database content, blog posts. Styles all descendant elements — headings, paragraphs, lists, blockquotes, code, tables — using --ui-* tokens. No classes needed on individual elements.

Basic

Pass any HTML string to content. All elements inside are styled automatically.

Why great coffee starts with water

Most home baristas obsess over beans and grinders, but water quality is the single biggest variable in your cup. Tap water that is too hard leaves bitter, chalky espresso. Too soft and your shots taste flat and lifeless.

The ideal mineral balance

Speciality roasters recommend water with a total dissolved solids (TDS) between 75–150 mg/L and a magnesium content of at least 10 mg/L. Magnesium is the mineral most responsible for extracting the fruity, floral notes from light roasts.

What to do if your tap water is off

  • Use a filter jug — Brita Maxtra+ reduces hardness and chlorine
  • Try third-wave water sachets — add to distilled water for precise control
  • Blend tap with still mineral water to hit the right TDS range
Water is the ingredient you can control most precisely, yet almost nobody does.

Once your water is dialled in, even a modest grinder will produce noticeably better results. Read our water guide for a full breakdown by region.

import { prose } from '@invisibleloop/pulse/ui'

// CMS rich text field — output directly, fully styled
prose({ content: server.article.bodyHtml })

// Markdown rendered to HTML
prose({ content: renderMarkdown(server.post.body) })

Size

Scale the base font size for different contexts.

sm — footnotes, sidebars

The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.

  • Smaller text
  • Tighter line height

base (default)

The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.

  • Default size
  • Standard line height

lg — hero intro, feature descriptions

The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.

  • Larger text
  • More generous spacing
prose({ content: cms.body, size: 'sm' })   // footnotes, sidebars
prose({ content: cms.body })               // default
prose({ content: cms.intro, size: 'lg' }) // hero intro

Styled elements

Every common HTML element rendered inside prose() is styled using --ui-* tokens:

h2 heading

h3 heading

A paragraph with bold, italic, and a link. Also inline code.

  • Unordered item one
  • Unordered item two
  1. Ordered item one
  2. Ordered item two
A blockquote with an accent left border.
// A code block
const x = 1 + 2

End of content.

prose({ content: `
  <h2>Section heading</h2>
  <p>Paragraph with <strong>bold</strong> and <a href="#">a link</a>.</p>
  <ul><li>List item</li></ul>
  <blockquote>A quote.</blockquote>
  <pre><code>const x = 1</code></pre>
` })
Do not escape the content prop. prose() renders raw HTML — it is designed for trusted server-side content only. Never pass unescaped user input directly. Sanitise CMS output before rendering if your CMS allows arbitrary HTML.
PropTypeDefault
contentstringRaw HTML string — rendered as-is, not escaped. Use for server-side content only.
sizesm | base | lgbaseBase font size scale. sm=0.875rem, base=1rem, lg=1.125rem
classstringExtra classes on the wrapper <div>