Typography
The typography components set prose and labels in the global type scale: headings, inline text, paragraphs, links, quotes, lists, separators, keycaps and eyebrow labels. Each one sets its own size, color and spacing from theme tokens, so text built from them reads the same in Brutal and Elegant, wherever it is placed.
import {
Blockquote,
Eyebrow,
Heading,
Kbd,
Link,
List,
ListItem,
Paragraph,
Separator,
Text,
} from "raft-ui";Block components (Heading, Paragraph, Blockquote, List, Separator) carry their own vertical margins and drop them at the start and end of their container. Inline components (Text, Link, Kbd, Eyebrow) have none.
Heading
Heading renders a section heading, h1 to h6. Its size comes only from level, taken from the global type scale, and it carries its own vertical margins.
Basic
level picks the element and its size: h1 24px, h2 20px, h3 18px, h4 to h6 16px. The default is 2.
Prefix bar
prefix="bar" draws a bar before the text. type colors the bar: default, primary, info, success, warning or error. The text keeps the primary label color.
Align text
With alignText, the bar moves into the left gutter so the heading text lines up with the body text below it.
Spacing
A heading has a margin above and below it, larger for h1 to h3. The first child of its container drops its top margin and the last child its bottom margin.
Anchor
id and other attributes go to the heading element, so a link can point at it.
Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
level | 1 | 2 | 3 | 4 | 5 | 6 | 2 | Renders h1 to h6, and sets the size. | Unreleased |
prefix | bar | undefined | bar draws a bar before the text. | Unreleased |
type | default | primary | info | success | warning | error | default | The bar’s color. | Unreleased |
alignText | boolean | false | With prefix="bar", moves the bar into the left gutter so the text lines up with the body. | Unreleased |
Other h1 attributes (id, className, aria-*, ref …) go to the heading element.
Pass level to pick the heading. Sizes follow the raft-ui type scale, so h1 is 24px; use a display size of your own for a hero title.
Text
Text renders a run of text in the body size (--r-font-size-4 / --r-line-height-4). It always sets its own color, the primary label color unless type or depth says otherwise, so it reads the same wherever it is placed. There is no size prop.
Type
type picks a color role: default, primary, info, success, warning or error.
Depth
depth steps the default color down to the secondary and tertiary label colors. It only applies when type is default.
Strong, italic, underline, delete
delete wraps the content in <del>, so assistive tech announces it as removed.
Code
code renders the content as inline code in <code>. It keeps the color of type and depth, and combines with delete.
Element
as picks the element, from a fixed set of text elements. The element’s meaning stays; its size and color come from Text.
Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
as | TextElement | span | The element to render: span, div, p, label, strong, em, b, i, small, time, abbr, cite, q, mark, sub, sup, dfn, legend, figcaption, dt or dd. | Unreleased |
code | boolean | false | Renders the content as inline code. | Unreleased |
delete | boolean | false | Strikes the content through with <del>. | Unreleased |
depth | 1 | 2 | 3 | undefined | Primary, secondary or tertiary label color. Only with type="default". | Unreleased |
italic | boolean | false | Italic text. | Unreleased |
strong | boolean | false | The theme’s strong weight. | Unreleased |
type | default | primary | info | success | warning | error | default | Color role. | Unreleased |
underline | boolean | false | Underlined text. | Unreleased |
Other props, including ref, go to the root element, which is always the as element. <code> or <del> sit inside it, so as and the type of ref always match.
Paragraph
Paragraph renders a <p> of prose: the body font size (step 4 of the type scale) with the prose line height, --r-line-height-prose (1.6), rather than the fixed 20px line height of dense UI text.
Basic
Paragraphs keep 16px above and below. The first paragraph in its container has no space above and the last has none below.
Depth
depth sets the text emphasis: 1 is the primary text color and the default, 2 and 3 are quieter.
Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
depth | 1 | 2 | 3 | 1 | Text emphasis: primary, secondary or tertiary label color. | Unreleased |
Other props, including ref, go to the <p>. A paragraph without depth uses the primary text color.
Link
Link is an inline link inside text. It keeps the font and size of the text around it, takes its color from type, and lets the theme decide the underline.
Basic
A link reads as part of the sentence: only its color and underline change.
Type
primary is the brand link color. neutral is a quiet gray link for meta lines and footers, where a colored link would be too loud.
Underline
auto follows the theme: Elegant underlines on hover only, Brutal always draws a 2px underline that thickens on hover. hover, always and none fix it in every theme.
External links
target="_blank" adds rel="noopener noreferrer"; tokens you pass in rel are kept.
Router links
as renders another component instead of <a>, such as your router’s link. Link accepts that component’s props (here to) and passes its class, rel and ref on, so the component must render an <a> that takes them.
Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
as | ElementType | 'a' | The element or component to render, e.g. a router’s Link. | Unreleased |
type | primary | neutral | primary | Color role. | Unreleased |
underline | auto | hover | always | none | auto | When the underline shows; auto follows the theme. | Unreleased |
Other props go to the rendered element. type is the color role, so the anchor’s own type attribute is not available. type and underline defaults can be set for a subtree with ConfigProvider’s componentOptions={{ Link: { … } }}.
A visited link keeps its type’s color.
Blockquote
Blockquote renders a <blockquote> with a bar on its left, in secondary text. Like the other typography components it carries its own vertical margins and drops them at the start and end of its container.
Basic
cite and the other native attributes pass through to the <blockquote>.
Paragraphs
Paragraphs inside a quote sit closer together than on the page, and the first and last lose their outer margins so the bar fits the text.
Align text
alignText hangs the bar and its gap in the left gutter, so the quoted text lines up with the text around it.
Nested
A quote inside a quote gets its own bar.
Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
alignText | boolean | false | Hangs the bar in the left gutter so the text lines up with the surrounding text. | Unreleased |
Every other <blockquote> attribute, including cite and ref, passes through. The text color is fixed to the secondary label color. Elegant draws a thin neutral bar; Brutal draws a 2px ink bar and sets the text in italic.
List
List renders a prose list: a <ul>, or an <ol> when ordered. Each ListItem is an <li>; put another List inside an item to nest. The list carries its own vertical margin, dropped when it is the first or last thing in its container, and each item after the first sits a little below the one before.
This is the typography list. A virtualized list for long feeds is a separate, planned component.
Basic
Bullets by default, numbers with ordered. Brutal draws square bullets and bold numbers in ink; Elegant keeps them quiet in the tertiary label color.
Ordered
start sets the first number, reversed counts down and type picks the numbering (1, a, A, i, I). All three go to the <ol> and only apply with ordered.
Nested
A List inside a ListItem is a sub-list. Nested bullet lists switch to a second bullet style.
Align text
alignText removes the indent: the markers hang in the left gutter and the item text lines up with the text around the list.
Task list
A task item drops its marker so the leading checkbox stands in for it, as in a GFM task list (- [x] …). An item whose first child is a checkbox (a native <input type="checkbox"> or a Checkbox) does this on its own in browsers with :has(); task does it explicitly, and also works when the checkbox is wrapped.
Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
ordered | boolean | false | Renders an <ol> instead of a <ul>. | Unreleased |
start | number | undefined | The number of the first item. Only with ordered. | Unreleased |
reversed | boolean | undefined | Counts down. Only with ordered. | Unreleased |
type | 1 | a | A | i | I | undefined | Numbering style. Only with ordered. | Unreleased |
alignText | boolean | false | Hangs the markers in the gutter so the item text aligns with the surrounding text. | Unreleased |
Other props go to the <ul> / <ol>.
ListItem Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
task | boolean | false | Drops the marker so a leading checkbox replaces it. | Unreleased |
Other props, such as value, go to the <li>.
Theme tokens
| Token | Elegant | Brutal |
|---|---|---|
--r-list-font-size / --r-list-line-height | --r-font-size-4 / --r-line-height-4 | same |
--r-list-margin | 16px 0 | same |
--r-list-padding | 2em | same |
--r-list-item-margin | 0.25em 0 0 | same |
--r-list-bullet-type / --r-list-nested-bullet-type | disc / circle | square / square |
--r-list-marker-font-weight | regular | strong |
--r-list-marker-color | initial (tertiary label) | primary label |
Separator
Separator draws a line between content: a rule between blocks, a rule with a label in it, or a short vertical rule between inline items.
Basic
With no children it renders an <hr> with space above and below. As the first or last child of its container it drops that outer space.
Label
Children put a label in the line. labelPlacement moves it to the left, center or right. The label also names the separator for assistive tech.
Dashed
Vertical
orientation="vertical" renders an inline line, 1em tall, between inline items.
Decorative
decorative marks a line as purely visual: it gets role="none" and is hidden from assistive tech. A label stays readable as plain text.
Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
children | ReactNode | undefined | A label in the line. Ignored when vertical. | Unreleased |
dashed | boolean | false | Draws the line dashed. | Unreleased |
decorative | boolean | false | Purely visual: no separator role. | Unreleased |
labelPlacement | left | center | right | center | Where the label sits on the line. | Unreleased |
orientation | horizontal | vertical | horizontal | A block rule or an inline rule. | Unreleased |
Other props go to the root element: <hr>, the labeled <div role="separator">, or the vertical <span role="separator">.
Kbd
Kbd marks up keyboard input as a keycap, using the native <kbd> element. It is sized in em, so it follows the text it sits in.
Basic
Put the key’s label inside.
Key combinations
keys renders one keycap per key, joined by separator (+ by default). As the HTML spec suggests for combinations, the keycaps are wrapped in an outer <kbd>.
Size
medium is 0.875em of the surrounding text and small is 0.75em, so keycaps scale with headings and captions alike.
Modifier symbols
Kbd does not detect the platform or map key names. Write the symbol you want, and give symbols a title so the name shows on hover.
Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
children | ReactNode | undefined | The key’s label. Ignored when keys is set. | Unreleased |
keys | readonly ReactNode[] | undefined | Renders a combination: one keycap per entry inside an outer <kbd>. | Unreleased |
separator | ReactNode | "+" | What goes between the keys of a combination. | Unreleased |
size | small | medium | medium | Relative to the surrounding text: 0.75em or 0.875em. | Unreleased |
Kbd also accepts native kbd attributes and a ref, which go to the outer element of a combination.
Eyebrow
Eyebrow is the small label above or beside a section: a list group, a panel block, a form field. It is bold, uppercase and widely spaced, always in the secondary label color, so every section label reads the same wherever it sits. Chinese text renders as is.
Basic
12px, the strong weight and the widest letter spacing. The color is set, not inherited.
Mono
mono switches to the monospace meta voice, one step smaller at 11px, for runtime details, versions and counters.
Section heading
When the eyebrow is the only title of a section, render it as a heading with as="h2" to "h6" so it shows up in the document outline. It looks the same; browser heading margins are reset.
Form label
With as="label" and htmlFor, the eyebrow labels a form control.
Before and after
before and after put an icon or a count next to the label. Counts use tabular figures.
Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
as | span | div | label | h2 | h3 | h4 | h5 | h6 | span | The element to render. div and headings are blocks; span and label are inline. | Unreleased |
htmlFor | string | undefined | The id of the control the label belongs to. Only used with as="label". | Unreleased |
mono | boolean | false | Monospace at 11px instead of the sans face at 12px. | Unreleased |
before | ReactNode | undefined | Content before the label, such as an icon. | Unreleased |
after | ReactNode | undefined | Content after the label, such as a count. | Unreleased |
children | ReactNode | undefined | The label text. | Unreleased |
Eyebrow also accepts native attributes and a ref. It has no margins; space it from the layout around it. Decorative icons in before should carry aria-hidden.