Skip to content

Typography

Use consistent headings, body text, quotes, code and lists.

import { Eyebrow, TypographyBlockquote, TypographyH1, TypographyH2, TypographyH3, TypographyH4, TypographyInlineCode, TypographyLarge, TypographyLead, TypographyList, TypographyMuted, TypographyP, TypographySmall } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use the typography set for text you write into a page by hand: a changelog entry, an empty state's explanation, a settings section's intro, a marketing page. It follows shadcn's typography, but as components you import rather than class lists you copy, so every page picks up the theme's type and a fix lands everywhere.

For long-form content that comes from Markdown or a CMS, style the container instead of wrapping every element. The docs site does that with a .prose stylesheet built on the same values.

import { TypographyH1, TypographyLead, TypographyList, TypographyP } from '@youj/react'

export function Intro() {
  return (
    <>
      <TypographyH1>Deploys that explain themselves</TypographyH1>
      <TypographyLead>Every deploy now carries its own story.</TypographyLead>
      <TypographyP>
        Read the <a href="/guide">migration guide</a> if you pinned the old layout.
      </TypographyP>
      <TypographyList>
        <li>Build output streams as it happens.</li>
        <li>Rollbacks keep their environment.</li>
      </TypographyList>
    </>
  )
}

Each piece renders the element it names (h1 to h4, p, blockquote, code, ul or ol) and takes that element's props, so id, className and ARIA pass straight through. Pick the heading for the outline, not the size: restyle it with className if the page needs a different look.

Reference

Eyebrowthe short label over a figure or section

Eyebrow

Eyebrow is the short label over a figure, a group or a section: "Month-to-date spend", "Included usage", "Status". It's a span of small muted text, and your classes win, so a label you move to it keeps the look it had. Naming it is what lets a look give every label the same voice: in Bezel they're mono capitals. Pass render for another element, such as a section's heading.

An Eyebrow is inline and carries no margin, and a heading that opens a container sits flush, so the two touch until you space them. Stack them with a gap, as a hero does:

<div className="flex flex-col gap-2">
  <Eyebrow>New in 2.0</Eyebrow>
  <TypographyH1>Deploys that explain themselves</TypographyH1>
</div>
import { Eyebrow } from '@youj/react'

<Eyebrow>Month-to-date spend</Eyebrow>
<Eyebrow render={<h2 id="usage" />} className="text-ui-lg font-semibold text-fg">
  Included usage
</Eyebrow>
Propsordered (1)

Props

TypographyList

TypographyList props
PropType
orderedfalse–

Every other piece takes the props of its element.

MotionTypography doesn't move · reduced motion

Motion

Typography doesn't move. The one exception is a link's underline color, which eases to the accent on hover over fast with the standard ease.

Reduced motion

Nothing changes: a link's underline color change isn't motion.

AccessibilityARIA, focus, announcements

Accessibility

  • The headings are real heading elements, so the page outline and screen-reader heading navigation follow them. Don't skip levels for looks; restyle instead.
  • TypographyList is a real ul or ol, so most screen readers announce it as a list with its item count. Safari with VoiceOver drops list semantics from lists without visible markers, so pass role="list" where that matters. The square markers and mono numbers are drawn with CSS and aren't read.
  • Links keep the focus ring every control has.
  • Body and muted text meet WCAG AA contrast on the theme's backgrounds, in both modes.