Typography & Semantic HTML
Oat CSS styles semantic HTML elements directly — no extra classes needed. Just write proper HTML and it looks great. This page shows every styled element along with the design tokens that power them.
Headings
All six heading levels use fluid clamp() sizing, text-wrap: balance for even line breaks, and tight letter-spacing for a polished feel.
Heading 1 var(--text-1)
Heading 2 var(--text-2)
Heading 3 var(--text-3)
Heading 4 var(--text-4)
Heading 5 var(--text-5)
Heading 6 var(--text-regular)
<h1>Heading 1</h1> <!-- var(--text-1) — largest -->
<h2>Heading 2</h2> <!-- var(--text-2) -->
<h3>Heading 3</h3> <!-- var(--text-3) -->
<h4>Heading 4</h4> <!-- var(--text-4) -->
<h5>Heading 5</h5> <!-- var(--text-5) -->
<h6>Heading 6</h6> <!-- var(--text-regular) — smallest -->Typography scale tokens
Every size is fluid — it scales smoothly between mobile and desktop.
| Token | Clamp value | Used by |
|---|---|---|
--text-1 | clamp(1.5rem, 1.318rem + 0.91vw, 2rem) | h1 |
--text-2 | clamp(1.25rem, 1.114rem + 0.68vw, 1.625rem) | h2 |
--text-3 | clamp(1.125rem, 1.034rem + 0.45vw, 1.375rem) | h3 |
--text-4 | clamp(1rem, 0.955rem + 0.23vw, 1.125rem) | h4 |
--text-5 | clamp(0.875rem, 0.83rem + 0.23vw, 1rem) | h5 |
--text-6 | 0.875rem | — |
--text-7 | clamp(0.75rem, 0.705rem + 0.23vw, 0.875rem) | labels, captions |
--text-8 | clamp(0.6875rem, 0.665rem + 0.11vw, 0.75rem) | badges, chips |
--text-regular | clamp(0.875rem, 0.83rem + 0.23vw, 1rem) | p, h6, body |
Inline text elements
Regular paragraph with a link, bold, italic, and inline code.
Small text uses var(--text-7).
Highlighted / marked text uses the warning color at 30% opacity.
Strikethrough text for deprecated content.
CSS abbreviation with native tooltip.
<p>
Regular paragraph with a <a href="#">link</a>,
<strong>bold</strong>, <em>italic</em>,
and <code>inline code</code>.
</p>
<small>Small text</small>
<mark>Highlighted text</mark>Blockquote
"Oat styles this automatically — write semantic HTML and things just look right."
<blockquote>
"Styled with a left border, padding, italic, and muted color."
</blockquote>Code blocks
const greeting = 'Hello, Oat!';
console.log(greeting);<pre><code> blocks get monospace font, muted background, and horizontal scroll for overflow.
Lists
Unordered
- First item
- Second item
- Third item
Ordered
- First item
- Second item
- Third item
Add .unstyled to remove markers: <ul class="unstyled">
Horizontal rule
Content above
Content below
Prose class
For long-form content (blog posts, docs, articles), wrap your content in a .prose container. It sets optimal line-length (65ch), relaxed line-height, and consistent vertical rhythm.
Article title
This is a prose block. Notice how the max-width is capped at 65 characters for optimal readability. Line-height is relaxed and headings have extra margin above them for clear visual hierarchy.
Paragraphs use text-wrap: pretty for better line-break decisions. Links have subtle underline offsets that become solid on hover.
Blockquotes inside prose are indented and italicized.
- Lists have comfortable spacing
- Each item has a small margin below
<!-- Wrap long-form content in .prose for optimal readability -->
<div class="prose">
<h2>Article title</h2>
<p>Body text is capped at 65ch width with relaxed line-height...</p>
<blockquote>Quotes are indented automatically.</blockquote>
<ul>
<li>Lists have comfortable spacing</li>
</ul>
</div>
<!-- Variants -->
<div class="prose-sm">Narrower, smaller font</div>
<div class="prose-lg">Wider for dense content</div>
<div class="prose-full">No max-width constraint</div>Prose variants
| Class | Max-width | Notes |
|---|---|---|
.prose | 65ch | Default — optimal for reading |
.prose-sm | 45ch | Narrower, smaller font |
.prose-lg | 75ch | Wider for dense content |
.prose-full | none | No max-width constraint |
Line-height tokens
| Token | Value | Utility class |
|---|---|---|
--leading-none | 1 | .leading-none |
--leading-tight | 1.25 | .leading-tight |
--leading-snug | 1.375 | .leading-snug |
--leading-normal | clamp(1.5 … 1.6) | .leading-normal |
--leading-relaxed | 1.625 | .leading-relaxed |
--leading-loose | 2 | .leading-loose |
Letter-spacing tokens
| Token | Value | Utility class |
|---|---|---|
--tracking-tighter | -0.05em | .tracking-tighter |
--tracking-tight | -0.025em | .tracking-tight |
--tracking-normal | 0em | .tracking-normal |
--tracking-wide | 0.025em | .tracking-wide |
--tracking-wider | 0.05em | .tracking-wider |
--tracking-widest | 0.1em | .tracking-widest |
Font weight tokens
| Token | Value | Utility class |
|---|---|---|
--font-normal | 400 | .font-normal |
--font-medium | 500 | .font-medium |
--font-semibold | 600 | .font-semibold |
--font-bold | 700 | .font-bold |
font-normal (400) — Regular body text
font-medium (500) — Labels and captions
font-semibold (600) — Headings default
font-bold (700) — Strong emphasis
Font family
| Token | Value | Utility |
|---|---|---|
--font-sans | system-ui, sans-serif | .font-sans |
--font-mono | ui-monospace, Consolas, monospace | .font-mono |
Typography utility classes
All utility classes for text styling at a glance:
Text size
| Class | Maps to |
|---|---|
.text-1 … .text-8 | var(--text-1) … var(--text-8) |
.text-small | var(--text-7) |
.text-xs | var(--text-8) |
.text-regular | var(--text-regular) |
Text color
| Class | Color |
|---|---|
.text-light | var(--muted-foreground) |
.text-lighter | var(--faint-foreground) |
.text-danger | Danger red |
.text-success | Success green |
.text-warning | Warning amber |
.text-primary | var(--primary) |
.text-muted | var(--muted-foreground) |
Text alignment
| Class | Responsive |
|---|---|
.text-left / .text-center / .text-right | Always |
.sm:text-left etc. | 640px+ |
.md:text-left etc. | 768px+ |
.lg:text-left etc. | 1024px+ |
Text transform & decoration
| Class | CSS |
|---|---|
.uppercase | text-transform: uppercase |
.lowercase | text-transform: lowercase |
.capitalize | text-transform: capitalize |
.normal-case | text-transform: none |
.underline | text-decoration-line: underline |
.line-through | text-decoration-line: line-through |
.no-underline | text-decoration-line: none |
Text wrapping & overflow
| Class | What it does |
|---|---|
.text-balance | Even line lengths for headings |
.text-pretty | Better line-break decisions |
.text-nowrap | Prevent text wrapping |
.truncate | Single-line ellipsis overflow |
.line-clamp-1 … .line-clamp-4 | Multi-line clamp (1–4 lines) |
.line-clamp-none | Remove line clamp |
.break-words | Break long words |
.break-all | Break at any character |
White-space
| Class | CSS |
|---|---|
.whitespace-normal | white-space: normal |
.whitespace-nowrap | white-space: nowrap |
.whitespace-pre | white-space: pre |
.whitespace-pre-line | white-space: pre-line |
.whitespace-pre-wrap | white-space: pre-wrap |
Responsive text sizes
Override font-size at specific breakpoints:
| Prefix | Breakpoint | Example |
|---|---|---|
sm: | 640px+ | .sm:text-3 |
md: | 768px+ | .md:text-2 |
lg: | 1024px+ | .lg:text-1 |
<!-- Start small, scale up at breakpoints -->
<h1 class="text-3 sm:text-2 lg:text-1">
Responsive heading
</h1>
<!-- Center on mobile, left-align on desktop -->
<p class="text-center md:text-left">
Responsive alignment
</p>Card typography helpers
Cards have optional semantic sub-classes for structured content:
Card title
A short description of this card's content.
Main card body text with regular sizing and normal line-height.
<div class="card">
<div class="card-header">
<h3 class="card-title">Project name</h3>
<p class="card-description">Brief description here.</p>
</div>
<div class="card-content">
<p>Main content goes here.</p>
</div>
<div class="card-footer">
<button class="small">Save</button>
<button class="small outline">Cancel</button>
</div>
</div>| Class | Font size | Notes |
|---|---|---|
.card-title | var(--text-4) | Semibold, tight leading |
.card-description | var(--text-7) | Muted color |
.card-content | var(--text-regular) | Normal body text |
.card-footer | — | Flex row with top border |
Spacing tokens
Fluid spacing values used across all components:
| Token | Value |
|---|---|
--space-1 | 0.25rem (4px) |
--space-2 | 0.5rem (8px) |
--space-3 | clamp(0.5rem, 1.5vw, 0.75rem) |
--space-4 | clamp(0.5rem, 2vw, 1rem) |
--space-5 | clamp(0.75rem, 2.5vw, 1.25rem) |
--space-6 | clamp(0.75rem, 3vw, 1.5rem) |
--space-8 | clamp(1rem, 4vw, 2rem) |
--space-10 | clamp(1.5rem, 5vw, 2.5rem) |
--space-12 | 3rem (48px) |
--space-14 | 3.5rem (56px) |
--space-16 | 4rem (64px) |
--space-18 | 4.5rem (72px) |
Border radius tokens
| Token | Value | Used by |
|---|---|---|
--radius-small | 0.125rem | Checkboxes, code |
--radius-medium | clamp(0.25rem, 0.8vw, 0.375rem) | Cards, inputs, buttons |
--radius-large | clamp(0.5rem, 1.5vw, 0.75rem) | Dialog, carousel |
--radius-full | 9999px | Badges, avatars, pills |
Shadow tokens
--shadow-small--shadow-medium--shadow-largeColor tokens
All colors use light-dark() for automatic dark mode support.
| Token | Light | Purpose |
|---|---|---|
--background | #fff | Page background |
--foreground | #09090b | Default text color |
--primary | #574747 | Primary accent / buttons |
--secondary | #f4f4f5 | Secondary surfaces |
--muted | #f4f4f5 | Muted backgrounds |
--muted-foreground | #71717a | Secondary text |
--faint | #fafafa | Code backgrounds |
--accent | #f4f4f5 | Hover states |
--danger | #d32f2f | Error / danger states |
--success | #008032 | Success states |
--warning | #a65b00 | Warning states |
--border | #d4d4d8 | Border color |
--ring | #574747 | Focus ring color |
--card | #fff | Card background |
--card-foreground | #09090b | Card text color |
--primary-foreground | #fafafa | Text on primary backgrounds |
--secondary-foreground | #574747 | Text on secondary backgrounds |
--faint-foreground | #a1a1aa | Placeholder / disabled text |
--danger-foreground | #fafafa | Text on danger backgrounds |
--success-foreground | #fafafa | Text on success backgrounds |
--warning-foreground | #09090b | Text on warning backgrounds |
--input | #d4d4d8 | Input border color |
Z-index tokens
A consistent 8-level stacking scale ensures layers never conflict across components.
| Token | Value | Used by |
|---|---|---|
--z-base | 0 | Default document flow |
--z-sticky | 10 | Sticky elements (sidebar overlay) |
--z-fixed | 20 | Fixed positioned elements (sidebar panel) |
--z-dropdown | 50 | Dropdowns, select menus |
--z-toast | 100 | Toast notifications |
--z-modal | 200 | Dialog / modal overlays |
--z-popover | 300 | Popovers |
--z-tooltip | 400 | Tooltips (always on top) |