alouette
alouette-typography
Style alouette Text and Paragraph entirely via className: combined font family+weight utilities (font-body, font-body-bold, font-body-extrabold, font-heading*, font-mono*), Tailwind text-* sizes, and semantic color tokens (text-sharp, text-muted, text-accent, text-on-accent). No variant props. Load when rendering any text; avoids font-bold and raw color classes.
This skill builds on alouette-theming. Read it first for the color token model.
alouette — Typography
Text and Paragraph have no variant props — style them entirely with className. Text defaults to font-body + text-sharp, so plain body text needs no family or color class; nested in another Text it inherits instead (see "Nested Text inherits"). Paragraph is Text with role="paragraph" (renders a <p> on web) and selectable text — a block of prose, not a generic text wrapper (see "Text vs Paragraph").
Setup
import { Text, Paragraph } from "alouette";
<Text className="font-heading-extrabold text-4xl">Title</Text>
<Paragraph className="text-base">Body copy.</Paragraph>Core Patterns
Text vs Paragraph
Paragraph means "this is prose": one or more sentences, in a <p>, selectable so the reader can copy them. Everything else is Text — a label, a heading, a stat, a table cell, a single value — even when it stands alone on its own line.
A value with its own component keeps that component: a code fragment is Code, a code listing CodeBlock, a link ExternalLinkText / LinkText, a quotation Blockquote (which already wraps its text in a Paragraph; block content goes in a BlockquoteFrame).
A <p> takes no block content, so a Paragraph's children are inline only — Text, Code, ExternalLinkText. Nesting a Paragraph, a View or a Box inside one is invalid DOM on web.
<Paragraph>
Point the client at <Code>{endpointUrl}</Code>, then{" "}
<ExternalLinkText size="sm" href={docsUrl} text="read the guide" />.
</Paragraph>
<Text className="text-sm text-muted">{endpointUrl}</Text>Family + weight (one combined utility)
Family and weight are a single utility — never a standalone font-bold:
- font-body, font-body-bold, font-body-extrabold
- font-heading, font-heading-bold, font-heading-extrabold
- font-mono, font-mono-bold, font-mono-extrabold
<Text className="font-body-bold">Bold body</Text>
<Text className="font-heading-extrabold text-2xl">Heading</Text>
<Text className="font-mono text-sm text-muted">code()</Text>Size (standard Tailwind, font-size only)
text-xs 12 · text-sm 14 · text-base 16 · text-lg 18 · text-xl 24 · text-2xl 32 · text-3xl 40 · text-4xl 48 · text-5xl 64 · text-6xl 80.
Pair font-heading* with text-xl (24px) or larger — the heading family is tuned for display sizes. For smaller emphasis, use font-body-bold.
An all-caps label centered in a fixed box (initials in a disc) sits ~1px high: the line box keeps room for descenders caps do not have. Add text-trim-cap, which trims it to cap height over baseline — web only, a no-op on native and in browsers without text-box. Avatar's initials use it.
Source: packages/alouette/scripts/build-css.ts, ui/data/Avatar.tsx
Color (semantic tokens)
text-sharp (default) · text-muted · text-accent · text-on-accent (+ -muted variants). Accent color follows the nearest accent scope:
<Text accent="brand" className="text-accent">
Brand-colored
</Text>Nested Text inherits
A Text nested in another Text (or a Paragraph) drops the font-body / text-sharp defaults and inherits the family, weight, color and size it does not set itself, so a run of a rich-text renderer is a plain <Text>:
<Text className="font-heading-bold text-xl text-muted">
Release <Text className="text-accent">24.3</Text> notes
</Text>"24.3" stays heading-bold and text-xl, colored text-accent. A nested Text that must differ sets that class itself (text-sharp, font-body). On native, a View between the two (an inline Badge) breaks inheritance, so a Text inside it starts from the defaults again.
Common Mistakes
MEDIUM Using Paragraph as a generic text wrapper
A URL, a label, a stat or a heading is not prose: role="paragraph" announces a paragraph to a screen reader and emits a <p> that cannot legally hold the View/Box such a value usually sits next to. Keep Paragraph for sentences; a value with its own component (Code, CodeBlock, ExternalLinkText) keeps that component.
Source: packages/alouette/src/ui/primitives/Text.tsx
HIGH Using font-bold instead of font-body-bold
Wrong:
<Text className="font-bold text-base">Title</Text>Correct:
<Text className="font-body-bold text-base">Title</Text>On native, bold/extrabold are separate font files baked into the family utility. A standalone font-bold / font-extrabold / font-normal has no effect and is overridden — it only appears to work on web.
Source: CLAUDE.md (Text styling); src/ui/primitives/Text.stories.tsx (Invalid)
HIGH Using react-native Text instead of alouette Text
Wrong:
import { Text } from "react-native";
<Text className="text-muted">Hi</Text>;Correct:
import { Text } from "alouette";
<Text className="text-muted">Hi</Text>;react-native's Text has no font-body default, no semantic color, and no accent prop, so it doesn't participate in theming. alouette's Text applies font-body + text-sharp and re-themes via accent.
Source: packages/alouette/src/ui/primitives/Text.tsx
MEDIUM Using a heading font below text-xl
Wrong:
<Text className="font-heading-bold text-base">Section</Text>Correct:
<Text className="font-heading-bold text-xl">Section</Text>The heading family is tuned for display sizes (text-xl / 24px and up). Below that it looks off — use font-body-bold for small emphasized text instead.
Source: scripts/build-css.ts (type scale); src/ui/primitives/Text.stories.tsx
MEDIUM Coloring text with the raw Tailwind palette
Wrong:
<Text className="text-black">Body</Text>Correct:
<Text className="text-sharp">Body</Text>text-black / text-gray-500 ignore the theme and break dark mode; semantic tokens resolve per mode and accent.
Source: CLAUDE.md (Color tokens)