Text
Atombeta@macaw-ds/ui · since 0.1.0Renders text in a named typographic role and tone. Every string on screen goes through it; nothing else sets font size or colour.
Import
import { Text, textVariants } from '@macaw-ds/ui';Props
| Prop | Type | Default | Description |
|---|---|---|---|
| as | "p" | "span" | "div" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "label" | "dt" | "dd" | "legend" | "strong" | "em" | "time" | p | Rendered element. Choose for meaning: h2 for a section title, label for a field, time for a date. Accessibility: Heading levels must not skip (h2 then h3). A label must point at a control with htmlFor. |
| variant | "display-lg" | "display-md" | "heading-lg" | "heading-md" | "heading-sm" | "body-lg" | "body-md" | "body-sm" | "label-md" | "label-sm" | "caption" | "numeric-lg" | "numeric-md" | "numeric-sm" | "code" | body-md | Typographic variant from the scale. Independent of the element, so an h2 can use heading-sm in a dense panel. |
| tone | "primary" | "secondary" | "muted" | "inverse" | "numeric" | "success" | "warning" | "critical" | "info" | "accent" | primary | Colour. primary and numeric meet 7:1; the rest meet 4.5:1 on every surface in both themes. |
| align | "start" | "center" | "end" | start | Text alignment. Money columns align end. |
| numeric | boolean | false | Tabular numerals plus the numeric colour (unless tone is set). Use for every number that will be compared or summed. Accessibility: Numerals at 7:1 contrast (DECISIONS.md #8). |
| truncate | boolean | false | One line with an ellipsis. Accessibility: Truncated text must be available in full elsewhere (a title attribute or a tooltip). |
| children | ReactNode | Available balance | The text. |
Slots
Named parts you pass content into.
- children
Tokens used
Every value this component renders comes from one of these tokens.
| Token | Light | Dark |
|---|---|---|
| text.size.body-md semantic · dimension | 0.875rem | 0.875rem |
| text.leading.body-md semantic · dimension | 1.25rem | 1.25rem |
| text.weight.body-md semantic · fontWeight | 400 | 400 |
| font.family.sans primitive · fontFamily | Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif | Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif |
| font.family.mono primitive · fontFamily | 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace | 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace |
| color.fg.primary semantic · color | oklch(0.15 0.012 250) | oklch(0.985 0.002 250) |
| color.fg.secondary semantic · color | oklch(0.38 0.012 250) | oklch(0.87 0.007 250) |
| color.fg.muted semantic · color | oklch(0.48 0.012 250) | oklch(0.66 0.01 250) |
| color.fg.numeric semantic · color | oklch(0.11 0.01 250) | oklch(1 0 0) |
| color.fg.inverse semantic · color | oklch(1 0 0) | oklch(0.15 0.012 250) |
| color.status.success-fg semantic · color | oklch(0.37 0.1 152) | oklch(0.88 0.1 152) |
| color.status.warning-fg semantic · color | oklch(0.41 0.11 50) | oklch(0.9 0.12 80) |
| color.status.critical-fg semantic · color | oklch(0.39 0.14 27) | oklch(0.88 0.08 27) |
| color.status.info-fg semantic · color | oklch(0.38 0.09 230) | oklch(0.88 0.07 230) |
| color.status.accent-fg semantic · color | oklch(0.38 0.15 262) | oklch(0.88 0.08 262) |
Eject
Ejecting copies the source into your repo and records it in .macaw/ejected.json, so the fork stays visible. The same file is served as a shadcn-compatible registry item.
npx @macaw-ds/cli add text --ejectnpx shadcn@latest add https://macaw-design-system.vercel.app/r/text.jsonBundle budget: 2,000 bytes gzipped, enforced by size-limit in CI.
Manifest
registry/manifests/text.json, the contract the playground, CLI, plugin and MCP server read.
{
"name": "Text",
"slug": "text",
"package": "@macaw-ds/ui",
"status": "beta",
"since": "0.1.0",
"level": "atom",
"category": "typography",
"purpose": "Renders text in a named typographic role and tone. Every string on screen goes through it; nothing else sets font size or colour.",
"description": "The typography primitive. `variant` picks size, line height, weight and tracking from the type scale; `tone` picks the colour; `as` picks the HTML element for semantics. `numeric` switches on tabular numerals and the 7:1 numeric colour for money, counts and dates.",
"exports": [
"Text",
"textVariants"
],
"props": {
"as": {
"type": "enum",
"values": [
"p",
"span",
"div",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"label",
"dt",
"dd",
"legend",
"strong",
"em",
"time"
],
"default": "p",
"required": false,
"description": "Rendered element. Choose for meaning: h2 for a section title, label for a field, time for a date.",
"a11y": "Heading levels must not skip (h2 then h3). A label must point at a control with htmlFor.",
"control": "select"
},
"variant": {
"type": "enum",
"values": [
"display-lg",
"display-md",
"heading-lg",
"heading-md",
"heading-sm",
"body-lg",
"body-md",
"body-sm",
"label-md",
"label-sm",
"caption",
"numeric-lg",
"numeric-md",
"numeric-sm",
"code"
],
"default": "body-md",
"required": false,
"description": "Typographic variant from the scale. Independent of the element, so an h2 can use heading-sm in a dense panel.",
"control": "select"
},
"tone": {
"type": "enum",
"values": [
"primary",
"secondary",
"muted",
"inverse",
"numeric",
"success",
"warning",
"critical",
"info",
"accent"
],
"default": "primary",
"required": false,
"description": "Colour. primary and numeric meet 7:1; the rest meet 4.5:1 on every surface in both themes.",
"control": "select"
},
"align": {
"type": "enum",
"values": [
"start",
"center",
"end"
],
"default": "start",
"required": false,
"description": "Text alignment. Money columns align end."
},
"numeric": {
"type": "boolean",
"default": false,
"required": false,
"description": "Tabular numerals plus the numeric colour (unless tone is set). Use for every number that will be compared or summed.",
"a11y": "Numerals at 7:1 contrast (DECISIONS.md #8)."
},
"truncate": {
"type": "boolean",
"default": false,
"required": false,
"description": "One line with an ellipsis.",
"a11y": "Truncated text must be available in full elsewhere (a title attribute or a tooltip)."
},
"children": {
"type": "node",
"default": "Available balance",
"required": false,
"description": "The text."
}
},
"slots": [
"children"
],
"tokens": [
"text.size.body-md",
"text.leading.body-md",
"text.weight.body-md",
"font.family.sans",
"font.family.mono",
"color.fg.primary",
"color.fg.secondary",
"color.fg.muted",
"color.fg.numeric",
"color.fg.inverse",
"color.status.success-fg",
"color.status.warning-fg",
"color.status.critical-fg",
"color.status.info-fg",
"color.status.accent-fg"
],
"a11y": {
"role": "none (inherits the element's role: heading, paragraph, label, time). The HTML role attribute passes through.",
"focusable": false,
"keyboard": [],
"name": "Not applicable; Text is content. For label, the name is the text and it names the control via htmlFor.",
"notes": [
"Pick `as` for document structure first; use `role` for size. Screen readers navigate by heading level, not by font size.",
"Do not convey status by tone alone; pair a tone with words or an icon with a label."
]
},
"budget": {
"gzip": 2000
},
"rules": [
"All text goes through Text. Never apply text-* or font-* utilities to raw elements.",
"Money, counts and dates use numeric (or a numeric-* role) so columns line up.",
"Heading levels descend without skipping.",
"label elements use htmlFor and point at the control's id."
],
"dos": [
"Use heading-sm for card titles and panel headers.",
"Use caption with tone=\"muted\" for helper text under a field."
],
"donts": [
"Do not use display roles inside cards or tables.",
"Do not use tone=\"muted\" for anything the user must read to act (errors, amounts)."
],
"examples": [
{
"name": "Default",
"props": {
"children": "Available balance"
}
},
{
"name": "Heading",
"props": {
"as": "h2",
"children": "Invoices",
"variant": "heading-md"
}
},
{
"name": "Money",
"description": "Tabular numerals, end aligned, numeric colour.",
"props": {
"numeric": true,
"align": "end",
"children": "$12,480.00",
"variant": "numeric-md"
}
},
{
"name": "Status",
"props": {
"tone": "critical",
"children": "Overdue by 12 days",
"variant": "label-sm"
}
},
{
"name": "Helper",
"props": {
"tone": "muted",
"children": "Updated 2 minutes ago",
"variant": "caption"
}
}
],
"registryDependencies": [],
"dependencies": [
"class-variance-authority"
],
"related": [
"badge",
"card"
]
}