Browse docs
Browse docs
The foundation of every readable surface. <Typography> collapses the visual scale, the semantic HTML tag, and the intent color into a single typed prop set — so every heading and every paragraph in your app reads from the same source of truth.
import { Typography } from '@dashforge/tw';
<Typography variant="h1">Workspace settings</Typography>
<Typography variant="body1" color="muted">Control access and notifications.</Typography><Typography variant="h2" gutterBottom>Profile</Typography>
<Typography variant="body1">
Update your display name, email, and notification preferences below.
</Typography>
<Typography variant="caption" color="muted">
Last updated 2 days ago
</Typography>The default variant is body1 → renders a <p>. Default color is inherit so Typography painted inside a <Box variant="solid" color="primary"> reads white without configuration.
Subtitle 1 — emphasized lead
Body 1 — primary prose paragraph for long-form reading.
Body 2 — dense secondary text for helper lines.
OVERLINE — SECTION KICKERimport { Typography, Stack } from '@dashforge/tw';
<Stack gap={2}>
<Typography variant="h2">Heading 2 — section</Typography>
<Typography variant="h4">Heading 4 — subsection</Typography>
<Typography variant="subtitle1">Subtitle 1 — emphasized lead</Typography>
<Typography variant="body1">
Body 1 — primary prose paragraph for long-form reading.
</Typography>
<Typography variant="body2" color="muted">
Body 2 — dense secondary text for helper lines.
</Typography>
<Typography variant="overline">OVERLINE — SECTION KICKER</Typography>
</Stack>Headings get font-bold (h1, h2) or font-semibold (h3–h6) by default; body and subtitle stay normal/medium; overline gets uppercase + wide tracking automatically.
Primary — calls-to-action accent
Success — saved · just now
Warning — 3 days until your trial ends
Danger — required field
Muted — last seen 4 hours ago
import { Typography, Stack } from '@dashforge/tw';
<Stack gap={1}>
<Typography color="primary">Primary — calls-to-action accent</Typography>
<Typography color="success">Success — saved · just now</Typography>
<Typography color="warning">Warning — 3 days until your trial ends</Typography>
<Typography color="danger">Danger — required field</Typography>
<Typography color="muted">Muted — last seen 4 hours ago</Typography>
</Stack>muted is the workhorse for secondary text — helper lines, captions, "last updated" labels. inherit is the escape: inside any container that paints its own color, leave Typography untouched and it reads cleanly.
The variant baked-in weight is just a default. When you want "an h2 styled like h2 but lighter":
<Typography variant="h2" weight="normal">
Subtle heading
</Typography>tailwind-merge resolves the conflict — the font-normal from weight wins over the variant's font-bold.
<div className="w-48">
<Typography truncate>
This is a very long string that will collapse to a one-line ellipsis…
</Typography>
</div>truncate enables text-ellipsis + overflow-hidden + whitespace-nowrap in one prop. For one-line WITHOUT ellipsis (rare — usually for status badges), use noWrap instead.
<Typography variant="h2" align="center" gutterBottom>
Welcome back
</Typography>
<Typography variant="body1" align="center" color="muted">
Pick up where you left off.
</Typography>gutterBottom adds mb-3 — the canonical bottom margin between a heading and its following paragraph. Mirror of MUI's same flag, familiar muscle memory.
Sometimes the heading hierarchy of the page should differ from the visual hierarchy of the section. The as prop swaps the tag while keeping the variant's visuals:
<Typography variant="h1" as="h2">
Looks like h1, but in the page outline this is an h2.
</Typography>Useful for hero sections embedded inside an article that already has its own <h1>.
asChild — paint onto a router LinkFor a styled <Link> with no extra wrapper element:
import { Link } from 'react-router-dom';
<Typography variant="h3" color="primary" asChild>
<Link to="/dashboard">Open dashboard →</Link>
</Typography>Radix Slot merges our className + ref onto the child. The rendered DOM is exactly <a> — no extra <h3> wrapping it. When both as and asChild are passed, asChild wins.
<Box variant="solid"><Box variant="solid" color="primary" rounded="2xl" p={12}>
<Typography variant="h1" color="inherit" gutterBottom>
Get started today
</Typography>
<Typography variant="body1" color="inherit">
Two minutes from install to your first component on screen.
</Typography>
</Box>color="inherit" is the magic: Box paints the surface, Typography reads the resulting color from its parent. No prop drilling, no CSS variables to remember.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'subtitle1' | 'subtitle2' | 'body1' | 'body2' | 'caption' | 'overline' | 'body1' | Type scale. Drives the default HTML tag (overridable via as). |
color | 'inherit' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral' | 'muted' | 'inherit' | Intent. Resolves to the @dashforge/tw-theme color vars with dark-mode variants pre-wired. |
weight | 'normal' | 'medium' | 'semibold' | 'bold' | 'extrabold' | — | Overrides the variant's default font-weight. |
align | 'left' | 'center' | 'right' | 'justify' | 'left' | Text-align. |
truncate | boolean | false | One-line ellipsis (text-ellipsis + overflow-hidden + whitespace-nowrap). |
noWrap | boolean | false | One-line without ellipsis. |
gutterBottom | boolean | false | Adds mb-3 for the heading→paragraph rhythm. |
as | ElementType | inferred from variant | Override the rendered HTML tag. |
asChild | boolean | false | Render via Radix Slot onto the single child. Wins over as. |
sx | string | — | Utility classes merged via tailwind-merge — wins over variant defaults. |
| ...rest | React.HTMLAttributes<HTMLElement> | — | All native HTML attributes pass through (id, data-*, aria-*, etc.). |
| Variant | Tag | Rationale |
|---|---|---|
h1–h6 | h1–h6 | Semantic heading level matches visual level by default |
subtitle1, subtitle2, body1, body2 | p | Block-level prose |
caption, overline | span | Inline annotation, no implicit block break |
import { typographyVariants } from '@dashforge/tw';
// typographyVariants({ variant: 'h2', color: 'primary' }) → className stringUseful when extending the catalogue downstream (e.g. an app-level "display" variant that wraps Typography).
asChild vs as precedence: when both are passed, asChild wins. We considered making this a compile-time discriminated union, but the props type already has 8 axes — one more dimension would inflate IntelliSense suggestions for marginal benefit. Documented + tested.color="inherit" is the right default for nested usage. Typography painted inside <Box variant="solid" color="primary"> reads white because the Box sets the inherited color on its container.weight always wins over variant default — <Typography variant="h1" weight="normal"> produces a thin display heading. tailwind-merge resolves the conflicting font-* classes to the last one.lineHeight prop in v1 — the per-variant leading is opinionated (leading-[1.05] for h1 down to leading-relaxed for body). Override via sx="leading-loose" when needed. We'll consider promoting this to a prop if it becomes the most-overridden axis.<Typography>: nesting <p> inside <p> is invalid HTML. Wrap the inner text in <Typography as="span"> if you really need that.direction="row" and put icon + Typography side by side. Embedding an SVG inside Typography breaks truncate and confuses screen readers.