v0.5

Typography

One type system, seven token families, applied with a single utility class.

Create UI ships a single, unified type system. Every text style is a token, and each token is applied with one text-{token} utility class that carries its font size, line height, letter spacing, and weight together. You name the role the text plays (text-heading-h1, text-paragraph-md, text-numeric-xl) instead of tuning four separate properties by hand.

One system, many roles: every screen is typed once against these tokens, and a token already knows its metrics at every breakpoint. Retuning the scale or shifting a heading down a size never touches markup.

Why one class

The same heading, written two ways:

// Raw values: four coupled properties, hand-managed at every breakpoint.
<h1 className="text-[36px] leading-[1.2] font-medium tracking-[-1.4px] md:text-[48px] md:tracking-[-1.6px] xl:text-[56px] xl:tracking-[-1.8px]">
  Ship faster with Create UI
</h1>
 
// Semantic token: name the role, every metric and breakpoint is baked in.
<h1 className="text-heading-h1">Ship faster with Create UI</h1>

The payoff:

  • One decision, not four. Size, line height, letter spacing, and weight travel together, so they can never drift out of sync on a stray element.
  • Responsive for free. The three breakpoints live inside the token, not in your markup. There is nothing to remember and nothing to copy-paste.
  • Consistent by construction. Two text-heading-h2 elements are identical everywhere, because there is one definition, not dozens of arbitrary values.
  • One place to change. Retuning the scale happens in a single data file, not a search-and-replace across every text-[…] in the app.

Usage

Apply a token class to any element. The class sets everything the style needs.

<h1 className="text-heading-h1">Ship faster with Create UI</h1>
<p className="text-paragraph-md">
  A unified type system keeps every screen in rhythm.
</p>
<span className="text-numeric-xl">$4,299</span>

Tokens compose. A real section is just a few of them stacked, each one naming its role and pairing with a color token:

<article>
  <span className="text-ui-overline-sm text-placeholder">Pricing</span>
  <h2 className="text-heading-h2 text-strongest">Team plan</h2>
  <p className="text-paragraph-md text-body">
    Everything your whole team needs to ship a consistent product, billed once
    per seat.
  </p>
  <span className="text-numeric-xl text-strongest">$4,299</span>
</article>

That snippet rendered, with each token doing its job:

Light
Pricing

Team plan

Everything your whole team needs to ship a consistent product, billed once per seat.

$4,299
Dark
Pricing

Team plan

Everything your whole team needs to ship a consistent product, billed once per seat.

$4,299

How it works

A token flows through three layers before it reaches your markup.

1. Token data is the source of truth. Each token is defined as plain data: a category plus its responsive metrics, written [mobile, tablet, desktop].

lib/createui-typography.ts
"heading-h1": {
  fontSize: r(36, 48, 56),
  lineHeight: pct(120),
  letterSpacing: r(-1.4, -1.6, -1.8),
},

The category (heading) supplies the font family and default weight; the size step (h1) supplies the metrics.

2. CSS custom properties. The build expands each token into four --text-* variables on :root, then overrides the responsive ones in two max-width media queries:

styles/globals.css
--text-heading-h1: 56px; /* desktop, ≥ 1280px */
--text-heading-h1--line-height: 120%;
--text-heading-h1--letter-spacing: -1.8px;
--text-heading-h1--font-weight: 500;
 
@media (max-width: 1279px) {
  --text-heading-h1: 48px; /* tablet */
  --text-heading-h1--letter-spacing: -1.6px;
}
@media (max-width: 767px) {
  --text-heading-h1: 36px; /* mobile */
  --text-heading-h1--letter-spacing: -1.4px;
}

3. Tailwind utilities. Those variables are registered in @theme inline, so Tailwind v4 generates the text-* utility from them:

<h1 className="text-heading-h1" />

The result is a single class that already knows its four metrics at every breakpoint. When a project runs createui init, the CLI emits the same variables into the consumer's stylesheet, so the type scale travels with the components.

Anatomy of a token

Token names follow a category-size shape:

heading-h1   →  category: heading   size: h1
numeric-xl   →  category: numeric   size: xl
ui-overline-sm  →  category: ui / overline   size: sm

The category decides the font family and default weight. The size decides the metrics. Pick a token and you have picked the right typeface, the right weight, and the right scale step in one move.

Categories

Seven families cover every role, from marketing display type down to dense numeric and code data. Reach for the family that matches the job, not the one that happens to look the right size.

CategoryFontDefault weightUse for
displayGeist500Marketing hero type, the largest statements
headingGeist500Page and section titles (h1h6)
bodyGeist500Dense, functional UI copy
paragraphGeist400Long-form reading, generous line height
uiGeist500Buttons, inputs, badges, tabs, navigation
numericJetBrains Mono500Prices, stats, counters, tabular figures
codeJetBrains Mono500Inline code and code previews

Each family has its own personality. body is tight and functional for UI copy; paragraph opens up its line height for comfortable reading; numeric switches to a monospace face for figures that line up in columns:

Light

Body: dense, functional copy for product UI.

Paragraph keeps the same family but opens up its line height, so a long block of running text stays comfortable to read across several lines.

1,284 · $4,299 · 99.9%createUI()
Dark

Body: dense, functional copy for product UI.

Paragraph keeps the same family but opens up its line height, so a long block of running text stays comfortable to read across several lines.

1,284 · $4,299 · 99.9%createUI()

Live specimen

Every token in the system, rendered at its real responsive metrics with its desktop size, line height, tracking, and weight printed alongside.

displaydisplay
display-xl96px · 120% · -2px · 500
Create UI
display-lg72px · 120% · -1.8px · 500
Create UI
headingdisplay
heading-h156px · 120% · -1.8px · 500
Design at scale
heading-h248px · 130% · -1.6px · 500
Design at scale
heading-h336px · 130% · -1.4px · 500
Design at scale
heading-h432px · 140% · -1.2px · 500
Design at scale
heading-h524px · 140% · -0.8px · 500
Design at scale
heading-h620px · 150% · -0.6px · 500
Design at scale
bodybody
body-xl20px · 28px · -0.5px · 500
The quick brown fox jumps over the lazy dog.
body-lg18px · 26px · 0px · 500
The quick brown fox jumps over the lazy dog.
body-md16px · 24px · 0px · 500
The quick brown fox jumps over the lazy dog.
body-sm14px · 20px · 0px · 500
The quick brown fox jumps over the lazy dog.
body-xs12px · 18px · 0px · 500
The quick brown fox jumps over the lazy dog.
paragraphbody
paragraph-xl24px · 34px · -0.6px · 400
The quick brown fox jumps over the lazy dog.
paragraph-lg20px · 30px · -0.2px · 400
The quick brown fox jumps over the lazy dog.
paragraph-md18px · 28px · 0px · 400
The quick brown fox jumps over the lazy dog.
paragraph-sm16px · 26px · 0px · 400
The quick brown fox jumps over the lazy dog.
paragraph-xs14px · 22px · 0px · 400
The quick brown fox jumps over the lazy dog.
uibody
ui-control-xl18px · 24px · -0.5px · 500
Action label
ui-control-lg16px · 22px · -0.5px · 500
Action label
ui-control-md14px · 20px · -0.3px · 500
Action label
ui-control-sm12px · 16px · -0.2px · 500
Action label
ui-control-xs11px · 12px · -0.1px · 500
Action label
ui-overline-md14px · 20px · 1px · 500
Action label
ui-overline-sm12px · 18px · 1px · 500
Action label
ui-overline-xs11px · 16px · 1px · 500
Action label
ui-caption-md14px · 20px · 0px · 500
Action label
ui-caption-xs11px · 16px · 0px · 500
Action label
numericnumeric
numeric-xl32px · 36px · -1px · 500
$4,299
numeric-lg24px · 28px · 0px · 500
$4,299
numeric-md18px · 22px · 1px · 500
$4,299
numeric-sm14px · 18px · 1px · 500
$4,299
numeric-xs12px · 16px · 1px · 500
$4,299
codenumeric
code-lg24px · 150% · 0px · 500
createUI()
code-md18px · 150% · 0px · 500
createUI()
code-sm14px · 150% · 0px · 500
createUI()
code-xs12px · 150% · 0px · 500
createUI()

Font families

Three font variables back the categories, each defined with next/font in lib/fonts.ts and exposed as a CSS variable on the document root:

  • --font-display and --font-body are both Geist (sans-serif). Display, heading, body, paragraph, and ui all read from these.
  • --font-numeric is JetBrains Mono (monospace), giving prices, stats, and code consistent tabular width. Both numeric and code use it.

You never set the family directly. Choosing a token in a category applies its font automatically.

Responsive scale

Token values are defined per breakpoint as [mobile, tablet, desktop]. The generated CSS sets the desktop value on :root and overrides it with two max-width media queries:

BreakpointRangeSource
Desktop≥ 1280px:root default
Tablet768px – 1279px@media (max-width: 1279px)
Mobile≤ 767px@media (max-width: 767px)

Large type scales down on smaller screens, while interface, numeric, and code type stay fixed so controls and data never shift size:

  • Display and heading scale their font size and letter spacing. Line height is a percentage, so it tracks the font size automatically at every breakpoint.
  • Body and paragraph keep their font size fixed and open up line height slightly on smaller screens for readability.
  • ui, numeric, and code are fixed at every breakpoint.

For example, heading-h1 steps down as the viewport narrows:

/* :root (desktop, ≥ 1280px) */
--text-heading-h1: 56px;
 
/* tablet, @media (max-width: 1279px) */
--text-heading-h1: 48px;
 
/* mobile, @media (max-width: 767px) */
--text-heading-h1: 36px;

Guidelines

// Do
<h2 className="text-heading-h2 text-strongest">Section title</h2>
 
// Avoid: arbitrary values, metrics that can drift apart, no shared meaning
<h2 className="text-[48px] leading-[1.3] tracking-[-1.6px] font-medium">
  Section title
</h2>
  • Use heading and display for titles, body for UI copy, paragraph for long-form reading, ui for controls, numeric for figures, code for code.
  • Let the token own its metrics. If you find yourself adding leading-* or tracking-* next to a text-{token}, the wrong size step is almost always the real fix.
  • Reserve arbitrary text-[…] values for the rare one-off that genuinely has no role in the scale.
  • Pair a type token with a color token (text-strongest, text-body, text-placeholder) rather than baking color into the type choice.

Token reference

Every token in the system, grouped by category. Responsive values are written desktop / tablet / mobile; a single value is fixed across all breakpoints. Sizes and pixel line heights are in pixels. Display, heading, and code line heights are percentages, so they stay proportional as the font size changes.

Display

TokenSizeLine heightTracking
display-xl96 / 80 / 64120%-2 / -1.8 / -1.6
display-lg72 / 60 / 52120%-1.8 / -1.6 / -1.4

Heading

TokenSizeLine heightTracking
heading-h156 / 48 / 36120%-1.8 / -1.6 / -1.4
heading-h248 / 36 / 32130%-1.6 / -1.4 / -1.2
heading-h336 / 32 / 24130%-1.4 / -1.2 / -0.8
heading-h432 / 24 / 20140%-1.2 / -0.8 / -0.6
heading-h524 / 18 / 16140%-0.8 / -0.6 / -0.4
heading-h620 / 16 / 16150%-0.6 / -0.4 / -0.2

Body

Font size is fixed; line height opens up slightly on mobile for readability.

TokenSizeLine heightTracking
body-xl2028 / 28 / 30-0.5
body-lg1826 / 26 / 280
body-md1624 / 24 / 260
body-sm14200
body-xs12180

Paragraph

TokenSizeLine heightTracking
paragraph-xl2434 / 36 / 38-0.6
paragraph-lg2030 / 32 / 34-0.2
paragraph-md1828 / 30 / 320
paragraph-sm1626 / 28 / 280
paragraph-xs1422 / 24 / 240

UI

Fixed at every breakpoint so controls never resize.

TokenSizeLine heightTracking
ui-control-xl1824-0.5
ui-control-lg1622-0.5
ui-control-md1420-0.3
ui-control-sm1216-0.2
ui-control-xs1112-0.1

UI / Overline

Uppercase labels with widened tracking, for eyebrows and section kickers.

TokenSizeLine heightTrackingTransform
ui-overline-md14201uppercase
ui-overline-sm12181uppercase
ui-overline-xs11161uppercase

UI / Caption

TokenSizeLine heightTracking
ui-caption-md14200
ui-caption-xs11160

Numeric

JetBrains Mono, fixed at every breakpoint.

TokenSizeLine heightTracking
numeric-xl3236-1
numeric-lg24280
numeric-md18221
numeric-sm14181
numeric-xs12161

Code

JetBrains Mono, fixed at every breakpoint.

TokenSizeLine heightTracking
code-lg24150%0
code-md18150%0
code-sm14150%0
code-xs12150%0