Axis Specimen
Text AnimationsA live variable-font specimen playground: a giant editable specimen line driven through font-variation-settings by labelled range sliders generated from an axes prop, plus size and tracking controls, preset chips, and a copy-ready CSS readout. Bring your own variable font. Idle axis "breathe" never starts under prefers-reduced-motion and presets jump instantly.
Inspired by Extracted from the Glyphica template — independently built, not affiliated. Attribution policy
Fraunces
font-variation-settings: "opsz" 96, "wght" 620, "SOFT" 0, "WONK" 1;
96
620
0
1
72 px
-0.010 em
Installation
npx shadcn@latest add https://parable-three.vercel.app/r/axis-specimen.jsonRuns the shadcn CLI — the component source is copied intocomponents/parable/axis-specimen.tsx. You own it from there.
Usage
The exact code rendering the preview above.
<div style={{ width: "100%" }}>
<style>{"@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght,SOFT,WONK@9..144,100..900,0..100,0..1&display=swap');"}</style>
<AxisSpecimen text="Fraunces" defaultSize={72} />
</div>Props
AxisSpecimenProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">
| Prop | Type | Default | Description |
|---|---|---|---|
| fontFamily | string | '"Fraunces", Georgia, serif' | CSS `font-family` the specimen renders in. Loading the font is the consumer's job (`next/font`, `@font-face`, or a Google Fonts link). |
| axes | AxisDef[] | FRAUNCES_AXES | Variable axes exposed as sliders. Defaults to Fraunces' opsz/wght/SOFT/WONK. |
| text | string | "Hamburgefonstiv" | Initial specimen text. When `editable`, typing replaces it in place. |
| editable | boolean | true | Render the specimen as a `plaintext-only` contentEditable line. |
| presets | AxisPreset[] | — | Preset chips. Defaults to four Fraunces presets when `axes` is default, else none. |
| showSize | boolean | true | Show the font-size slider. |
| showTracking | boolean | true | Show the tracking (letter-spacing) slider. |
| defaultSize | number | 112 | Initial specimen size in px (clamped to the viewport at render). |
| sizeRange | [number, number] | [28, 220] | [min, max] px for the size slider. |
| defaultTracking | number | -1 | Initial tracking in hundredths of an em (−1 → −0.010em). |
| trackingRange | [number, number] | [-6, 12] | [min, max] for the tracking slider, in hundredths of an em. |
| breathe | boolean | true | Idle "auto specimen": until any control is touched, the first two wide axes breathe through a slow sine loop (paused off-screen, stopped forever on first interaction, never started under prefers-reduced-motion). |
| onChange | (values: Record<string, number>) => void | — | Fires with the full axis map (tag → value) on every user-driven change. |
| accentColor | string | "var(--pb-violet, #8b5cf6)" | Accent for slider thumbs, active presets, focus rings, axis tags. |
| textColor | string | "var(--foreground, #17171a)" | Specimen text colour. Defaults to the `--foreground` token. |
| specimenLabel | string | "Specimen text — type to replace it" | aria-label for the editable specimen line. |