Skip to content

Prose

The Prose component turns plain HTML into rich, readable prose. Everything you drop inside it, headings, paragraphs, lists, blockquotes, code, tables and images, is styled with a consistent type scale and vertical rhythm, so you can write an article without reaching for custom CSS.

By default Prose renders a div. Set tag to render a different wrapper such as an article, or use as-child to merge the styling onto the single child element without any wrapper at all.

For long-form reading, add the container prop to cap the content at a comfortable measure and center it. The width is driven by the --flux-prose-container custom property (default 90ch), so you can set it to 72ch, 78ch, 84ch or any value you like.

In container mode the content is laid out on a break-out grid, so individual elements can reach past the measure. Mark an element with data-prose-wide to break out to a wider track, or data-prose-full for a full-bleed element that spans the whole container. The gutter and the extra width of the wide track are set through the --flux-prose-gutter (default 24px) and --flux-prose-wide (default 12ch) custom properties.

Because Prose only styles its own HTML elements, you can drop a Flux component straight into an article without a wrapper: it keeps its own styling and still flows in the vertical rhythm.

Use Text instead when you need a single styled piece of inline text, such as a label, a metric or a caption. Prose is for flowing document content, Text is for individual strings.

The art of typesetting

Good typography is invisible. It guides the reader from one idea to the next without ever calling attention to itself, balancing rhythm, contrast and whitespace.

Why it matters

Consistent spacing and a clear hierarchy make long form content effortless to scan. A well set page respects the reader's time.

  • A measured line length keeps the eye from getting lost.
  • Generous leading gives each line room to breathe.
Typography is what language looks like.

TIP

Outside of Prose, Flux keeps only light element defaults (heading sizes, link and monospace styling). The rich prose styling (vertical rhythm, decorated blockquotes, list markers and table borders) applies only within FluxProse.

Props

asChild?: boolean
Merges the prose styling onto the single child element instead of rendering a wrapper. Useful when you already have a semantic root such as an article.

container?: boolean
Constrains the content to a centered reading measure. Set the width through the --flux-prose-container custom property, which defaults to 90ch.

tag?: string
The HTML element that is rendered. Defaults to a div, use article or section for document content.

Slots

default
The prose content, made up of regular HTML elements such as headings, paragraphs, lists and tables.

Examples

Headings

The six heading levels, each with its own size and weight.

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6

<template>
    <FluxProse>
        <h1>Heading 1</h1>
        <h2>Heading 2</h2>
        <h3>Heading 3</h3>
        <h4>Heading 4</h4>
        <h5>Heading 5</h5>
        <h6>Heading 6</h6>
    </FluxProse>
</template>

<script
    lang="ts"
    setup>
    import { FluxProse } from '@flux-ui/components';
</script>

Paragraphs

Body copy flows with a comfortable line height and automatic spacing between blocks.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. A adipisci aut ex excepturi expedita pariatur perspiciatis! Amet aperiam aut commodi fugit harum, id, incidunt necessitatibus, nemo nisi placeat voluptatem voluptatum!

Aspernatur commodi cum debitis doloremque, ea et harum hic illum natus nulla quam quisquam rem rerum similique tempora totam ullam vel. Accusamus consequuntur dicta facilis labore modi mollitia, perspiciatis voluptate?

Assumenda beatae commodi dolor dolore eum ex, excepturi fugiat id, inventore iure maiores maxime minima nesciunt nostrum, quidem sed sequi soluta tempora ullam voluptatem? Atque consequatur non omnis perferendis suscipit.

<template>
    <FluxProse>
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. A adipisci aut ex excepturi expedita pariatur perspiciatis! Amet aperiam aut commodi fugit harum, id, incidunt necessitatibus, nemo nisi placeat voluptatem voluptatum!</p>
        <p>Aspernatur commodi cum debitis doloremque, ea et harum hic illum natus nulla quam quisquam rem rerum similique tempora totam ullam vel. Accusamus consequuntur dicta facilis labore modi mollitia, perspiciatis voluptate?</p>
        <p>Assumenda beatae commodi dolor dolore eum ex, excepturi fugiat id, inventore iure maiores maxime minima nesciunt nostrum, quidem sed sequi soluta tempora ullam voluptatem? Atque consequatur non omnis perferendis suscipit.</p>
    </FluxProse>
</template>

<script
    lang="ts"
    setup>
    import { FluxProse } from '@flux-ui/components';
</script>

Lists

Ordered and unordered lists get markers, indentation and nested styling.

Unordered list

  • Espresso, the concentrated base for most drinks.
    • Ristretto
    • Lungo
  • Filter, brewed slowly for a lighter body.
  • Cold brew, steeped for many hours.

Ordered list

  1. Grind the beans just before brewing.
  2. Rinse the filter and preheat the cup.
  3. Pour in slow, steady circles.

<template>
    <FluxProse>
        <h3>Unordered list</h3>
        <ul>
            <li>Espresso, the concentrated base for most drinks.
                <ul>
                    <li>Ristretto</li>
                    <li>Lungo</li>
                </ul>
            </li>
            <li>Filter, brewed slowly for a lighter body.</li>
            <li>Cold brew, steeped for many hours.</li>
        </ul>

        <h3>Ordered list</h3>
        <ol>
            <li>Grind the beans just before brewing.</li>
            <li>Rinse the filter and preheat the cup.</li>
            <li>Pour in slow, steady circles.</li>
        </ol>
    </FluxProse>
</template>

<script
    lang="ts"
    setup>
    import { FluxProse } from '@flux-ui/components';
</script>

Blockquote

A quote is set off with an accent border and italics.

As Robert Bringhurst reminds us in his classic manual:

Typography exists to honor content. Its purpose is to serve the reader, not to draw attention to itself.

<template>
    <FluxProse>
        <p>As Robert Bringhurst reminds us in his classic manual:</p>
        <blockquote>Typography exists to honor content. Its purpose is to serve the reader, not to draw attention to itself.</blockquote>
    </FluxProse>
</template>

<script
    lang="ts"
    setup>
    import { FluxProse } from '@flux-ui/components';
</script>

Code

Inline code renders as a chip, while a pre block becomes a scrollable code block.

Install the package with your favourite manager, then import FluxProse where you need it.

import { FluxProse } from '@flux-ui/components';

const article = FluxProse;

Press Cmd + K to open the command palette.

<template>
    <FluxProse>
        <p>Install the package with your favourite manager, then import <code>FluxProse</code> where you need it.</p>
        <pre><code>import { FluxProse } from '@flux-ui/components';

const article = FluxProse;</code></pre>
        <p>Press <kbd>Cmd</kbd> + <kbd>K</kbd> to open the command palette.</p>
    </FluxProse>
</template>

<script
    lang="ts"
    setup>
    import { FluxProse } from '@flux-ui/components';
</script>

Table

Tables get borders, padding and a subtle header background.

RoastBodyAcidity
LightDelicateHigh
MediumBalancedMedium
DarkFullLow

<template>
    <FluxProse>
        <table>
            <thead>
                <tr>
                    <th>Roast</th>
                    <th>Body</th>
                    <th>Acidity</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>Light</td>
                    <td>Delicate</td>
                    <td>High</td>
                </tr>
                <tr>
                    <td>Medium</td>
                    <td>Balanced</td>
                    <td>Medium</td>
                </tr>
                <tr>
                    <td>Dark</td>
                    <td>Full</td>
                    <td>Low</td>
                </tr>
            </tbody>
        </table>
    </FluxProse>
</template>

<script
    lang="ts"
    setup>
    import { FluxProse } from '@flux-ui/components';
</script>

Break-out

In container mode, mark an element with data-prose-wide or data-prose-full to break it out of the reading measure.

Reading measure with break-out

Body text stays at a comfortable measure, so lines never get too long to scan. Individual elements can opt out and break into wider tracks without any wrapper.

Mark an element with data-prose-wide and it reaches past the measure on both sides, while the paragraphs around it keep their reading width.

A full-bleed quote, marked with data-prose-full, spans the entire width of the container.

After the break-out, the text returns to the measure on its own, so long-form reading stays comfortable throughout.

<template>
    <FluxProse
        container
        style="width: 100%"
        tag="article">
        <h2>Reading measure with break-out</h2>
        <p>Body text stays at a comfortable measure, so lines never get too long to scan. Individual elements can opt out and break into wider tracks without any wrapper.</p>

        <img
            alt=""
            data-prose-wide
            src="https://images.pexels.com/photos/33688/delicate-arch-night-stars-landscape.jpg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"/>

        <p>Mark an element with <code>data-prose-wide</code> and it reaches past the measure on both sides, while the paragraphs around it keep their reading width.</p>

        <blockquote data-prose-full>A full-bleed quote, marked with data-prose-full, spans the entire width of the container.</blockquote>

        <p>After the break-out, the text returns to the measure on its own, so long-form reading stays comfortable throughout.</p>
    </FluxProse>
</template>

<script
    lang="ts"
    setup>
    import { FluxProse } from '@flux-ui/components';
</script>

Embedded component

Drop a Flux component straight into an article. Prose only styles its own HTML elements, so the component keeps its own styling while flowing in the vertical rhythm.

Embedding a component

Prose only styles its own HTML elements, so you can drop a Flux component straight into an article. It keeps its own styling and still flows in the vertical rhythm.

Embedded componentDropped straight into the prose
No wrapper needed. The pane is left untouched by the prose rules, yet it is spaced from the paragraph above just like any other block.

After the component, the text continues in the same rhythm.

<template>
    <FluxProse>
        <h3>Embedding a component</h3>
        <p>Prose only styles its own HTML elements, so you can drop a Flux component straight into an article. It keeps its own styling and still flows in the vertical rhythm.</p>

        <FluxPane>
            <FluxPaneHeader
                icon="circle-info"
                subtitle="Dropped straight into the prose"
                title="Embedded component"/>
            <FluxPaneBody>
                No wrapper needed. The pane is left untouched by the prose rules, yet it is spaced from the paragraph above just like any other block.
            </FluxPaneBody>
        </FluxPane>

        <p>After the component, the text continues in the same rhythm.</p>
    </FluxProse>
</template>

<script
    lang="ts"
    setup>
    import { FluxPane, FluxPaneBody, FluxPaneHeader, FluxProse } from '@flux-ui/components';
</script>

Full article

Everything together: headings, body copy, lists, a blockquote, inline and block code, a keyboard shortcut, a table, an image, and an embedded Flux pane.

Designing with type

Typography is the craft of arranging text so it is legible, readable and appealing when displayed. Good type sets a rhythm the reader can follow without conscious effort, and it is the single biggest lever you have over how trustworthy an interface feels.

The vertical rhythm

Every block of content, whether a paragraph, a list, an image or a table, sits on a consistent baseline. Spacing scales with the weight of the element: a new section gets more room above it than the next paragraph does.

Typography is the detail and the presentation of a story. It represents the voice of an atmosphere, or a historical setting of some kind.

Working with lists

Lists break dense information into scannable chunks. Reach for an unordered list when order does not matter:

  • Keep the measure between 45 and 75 characters.
    • Narrower for captions.
    • Wider for long form reading.
  • Set a generous line height for body copy.
  • Limit the number of distinct type sizes.

Use an ordered list when the sequence itself is the point:

  1. Establish a base font size.
  2. Derive a modular scale from it.
  3. Assign each level a single, clear role.

Code and keys

Wrap identifiers such as --font-sans in inline code, and show a full snippet in a block:

:root {
    --font-sans: inter-variable, sans-serif;
}

Document shortcuts with the keyboard element: press Cmd + B to toggle bold.

Embedded componentDropped straight into the article
This pane keeps its own styling. Prose only styles its own HTML elements, so an embedded component is left untouched, yet it still flows in the article rhythm just like a paragraph or a heading.

A quick reference

The scale below summarises the default roles.

ElementSizeRole
Heading 127pxPage title
Heading 221pxSection heading
Body15pxParagraphs and lists

Finally, imagery breaks up long stretches of text and gives the eye a place to rest.

<template>
    <FluxProse tag="article">
        <h1>Designing with type</h1>
        <p>Typography is the craft of arranging text so it is <a href="#">legible, readable and appealing</a> when displayed. Good type sets a rhythm the reader can follow without conscious effort, and it is the single biggest lever you have over how <strong>trustworthy</strong> an interface feels.</p>

        <h2>The vertical rhythm</h2>
        <p>Every block of content, whether a paragraph, a list, an image or a table, sits on a consistent baseline. Spacing scales with the weight of the element: a new section gets more room above it than the next paragraph does.</p>

        <blockquote>Typography is the detail and the presentation of a story. It represents the voice of an atmosphere, or a historical setting of some kind.</blockquote>

        <h3>Working with lists</h3>
        <p>Lists break dense information into scannable chunks. Reach for an unordered list when order does not matter:</p>
        <ul>
            <li>Keep the measure between 45 and 75 characters.
                <ul>
                    <li>Narrower for captions.</li>
                    <li>Wider for long form reading.</li>
                </ul>
            </li>
            <li>Set a generous line height for body copy.</li>
            <li>Limit the number of distinct type sizes.</li>
        </ul>

        <p>Use an ordered list when the sequence itself is the point:</p>
        <ol>
            <li>Establish a base font size.</li>
            <li>Derive a modular scale from it.</li>
            <li>Assign each level a single, clear role.</li>
        </ol>

        <h3>Code and keys</h3>
        <p>Wrap identifiers such as <code>--font-sans</code> in inline code, and show a full snippet in a block:</p>
        <pre><code>:root {
    --font-sans: inter-variable, sans-serif;
}</code></pre>
        <p>Document shortcuts with the keyboard element: press <kbd>Cmd</kbd> + <kbd>B</kbd> to toggle bold.</p>

        <FluxPane>
            <FluxPaneHeader
                icon="circle-info"
                subtitle="Dropped straight into the article"
                title="Embedded component"/>
            <FluxPaneBody>
                This pane keeps its own styling. Prose only styles its own HTML elements, so an embedded component is left untouched, yet it still flows in the article rhythm just like a paragraph or a heading.
            </FluxPaneBody>
        </FluxPane>

        <h2>A quick reference</h2>
        <p>The scale below summarises the default roles.</p>
        <table>
            <thead>
                <tr>
                    <th>Element</th>
                    <th>Size</th>
                    <th>Role</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>Heading 1</td>
                    <td>27px</td>
                    <td>Page title</td>
                </tr>
                <tr>
                    <td>Heading 2</td>
                    <td>21px</td>
                    <td>Section heading</td>
                </tr>
                <tr>
                    <td>Body</td>
                    <td>15px</td>
                    <td>Paragraphs and lists</td>
                </tr>
            </tbody>
        </table>

        <hr/>

        <p>Finally, imagery breaks up long stretches of text and gives the eye a place to rest.</p>
        <img src="https://images.pexels.com/photos/33688/delicate-arch-night-stars-landscape.jpg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
            alt=""/>
    </FluxProse>
</template>

<script
    lang="ts"
    setup>
    import { FluxPane, FluxPaneBody, FluxPaneHeader, FluxProse } from '@flux-ui/components';
</script>

Used components