Skip to content

Typography

Renders text on the library's type scale, so headings, body copy and captions all share the same size steps.

tsx
import { Typography } from 'neba';

<Typography level="h2">A sheet of cut acrylic</Typography>
<Typography>Every surface is the same material at a different opacity.</Typography>;

Props

PropTypeDefaultDescription
level'h1'…'h6' | 'lead' | 'body' | 'caption' | 'overline''body'The type scale, and the element that carries it. Not called `variant`: that word already means the weight of a surface here
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info'Semantic colour role. Unlike every other component this has no default: unset means the page’s own colour
weight'regular' | 'medium' | 'semibold' | 'bold'Overrides the weight the level would pick
align'start' | 'center' | 'end' | 'justify'Text alignment
linesnumberClamps to this many lines with an ellipsis. 1 is a single-line truncation
gutterbooleanfalseAdds the space below. Off by default: a component that injects margins is one a layout has to fight
renderuseRender.RenderPropChanges the element without changing the type scale. Base UI's own escape hatch
childrenReactNodeThe text
transitionsharedNebaTransitionAn entrance animation, run once on mount (transition="fade"). Wrap it in an Animate* component for a trigger or a replay

Two props differ from the rest of the library. The type scale is chosen with level rather than variant, since variant means the weight of a surface everywhere else. And color has no default: leave it unset and the text inherits the surrounding colour.

The root carries the class neba-typography. It is the hook a stylesheet uses to reach the text from outside React — .neba-typography { text-wrap: balance } — since the utilities a level resolves to are not a contract.

Examples

level

level sets both the type scale and the element rendered. body matches the body step of an md Card, so a paragraph inside a card and one outside it are the same size. The heading steps tighten their leading as they grow.

color

lines

lines={1} truncates to one line with an ellipsis. 2 or more is a line clamp at that many lines.

render

Use render when the element level implies is not the element you need: a subheading that should stay out of the document outline, or a <p> that has to look like a heading.

tsx
<Typography level="h3" render={<p />}>
  Looks like a heading, is not one
</Typography>

gutter

gutter is off by default, so there are no vertical margins. Turn it on for a run of prose; leave it off inside a flex container that already owns its spacing.

Overriding the size

Each level's leading is a ratio, not a length, so a size set through className keeps a line box in proportion to it:

tsx
<Typography level="h2" className="text-[2.75rem]!">
  42
</Typography>

The ! is required, and it is the way to override the weight, the ink and the gutter too. The scale is written at two-class strength so that it clears a host stylesheet — .prose h2 and VitePress's .vp-doc h2 both set font-size, line-height, letter-spacing and font-weight on the tag, at a specificity a single utility cannot reach, and without this a Neba heading inside rendered Markdown took the article's type instead of its own. There is no specificity above that rule and below a plain utility, so ! is the way in. Add a leading-* beside it when the proportion is what you want to change.

align and lines are deliberately left as plain utilities: nothing styles text-align or a line clamp by tag name, so className reaches them without !.

Released under the MIT License