Design token generation
Skill jpoindexter/typography-skills/skills/design-token-generation
Use when turning typographic decisions into tokens, variables, or theme config — naming a type scale, structuring font/size/weight/line-height tokens, exporting to CSS/Tailwind/Style Dictionary/iOS/Android, or fixing a token set that has drifted into arbitrary values. Also use when design and code disagree about what the type scale actually is.From its SKILL.md
npx -y skills add jpoindexter/typography-skills --skill design-token-generationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.8 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
Typographic design token generation
Tokens are how a type scale survives contact with a codebase. A scale that isn't enforced is a suggestion.
1. When to invoke
- Turning a type scale into tokens, CSS variables, or theme config.
- Naming typographic tokens.
- Exporting type across platforms (web, iOS, Android).
- A token set has accumulated arbitrary values, or design and code disagree.
Do not invoke to design the scale (hierarchy-and-scale) or choose the face
(typeface-selection). Tokenize decisions already made.
2. Required context
- The finalised scale and its rationale.
- Target platforms and their unit conventions.
- Whether tokens must support theming (density modes, brand variants).
- Whether the pipeline is generated (Style Dictionary, Figma variables) or hand-maintained.
- Whether fluid/responsive values are in scope.
3. Invariant principles
- Three tiers: primitive → semantic → component.
- Primitive — raw values, no meaning:
font-size-300: 1.125rem. - Semantic — role-based, referencing primitives:
text-body-size. - Component — component-scoped, referencing semantics:
button-label-size. Components consume semantics. Semantics consume primitives. Never let a component reference a primitive directly — that's how a scale erodes.
- Primitive — raw values, no meaning:
- Name by role, never by appearance.
text-body, nottext-16ortext-grey. Appearance names become lies the first time the value changes. - Never name by size number alone.
text-14blocks you from ever changing 14. - Line-height tokens must be unitless.
- A token set is a constraint. If arbitrary values remain possible, tokens are documentation, not enforcement. Lint for off-token values.
- Typography tokens are composite in practice. Size, line-height, weight, tracking, and family travel together; a size alone is rarely a usable token.
4. Context-dependent heuristics
Numeric primitive scales (100, 200, 300…) leave room to insert values.
T-shirt sizes (sm, md, lg) run out fast and force renames. Prefer numeric
for primitives, semantic names above that.
Composite type tokens. The W3C Design Tokens format has a typography type
bundling the properties that must move together:
{
"text": {
"body": {
"$type": "typography",
"$value": {
"fontFamily": "{font.family.sans}",
"fontSize": "{font.size.300}",
"fontWeight": "{font.weight.regular}",
"lineHeight": "1.5",
"letterSpacing": "0"
}
}
}
}
Composite tokens prevent the common bug where someone updates a size and leaves the line-height behind.
Fluid values in tokens. A clamp() expression can be a token value, but it's
opaque to non-CSS platforms. Two options: store min/max/preferred as separate
primitives and compose per platform, or keep a stepped token set as the source of
truth and derive fluid CSS from it. Choose based on whether iOS/Android consume the
same tokens.
Platform units. Web rem; iOS points with Dynamic Type text styles; Android
sp (never dp for text — sp respects the user's font-size setting). A
generator must emit the right unit per platform, not a shared px.
Density themes. A compact mode is a different semantic layer over the same primitives, not a second primitive scale.
Don't tokenize everything. One-off display sizes used once are not tokens. Rule of three: tokenize on the third use.
5. Failure patterns
| Pattern | Cause | Fix |
|---|---|---|
text-16 now renders 18px | Named by value | Rename by role |
| Size changed, line-height didn't | Separate tokens, no composite | Composite typography token |
| Component references a primitive directly | Tier skipped | Route through a semantic |
Arbitrary font-size: 15px in the codebase | No enforcement | Lint off-token values |
| Android text ignores user font-size setting | dp instead of sp | Use sp |
| Token set has 40 sizes | Every one-off tokenized | Tokenize on third use |
| Figma and code disagree | Two hand-maintained sources | One source, generated both ways |
| Dark theme needed a new size scale | Weight/colour conflated with size | Theme the semantic layer only |
lineHeight: "24px" | Fixed unit | Unitless |
6. Evaluation procedure
- Confirm three tiers exist and each references only the tier below.
- Grep component tokens for direct primitive references. Should be zero.
- Grep the codebase for raw font-size/line-height values. Should be zero outside token definitions.
- Confirm every name is role-based, not value- or appearance-based.
- Confirm all line-heights are unitless.
- Build for every target platform; verify units (
rem/ points /sp). - Change one primitive and rebuild — confirm the change propagates everywhere it should and nowhere it shouldn't.
- Verify the generated output against the rendered result, not just the build log.
7. Output format
Tiers: primitive <n> · semantic <n> · component <n>
Naming: role-based <yes/no> — violations: <list>
Composite typography tokens: <yes/no>
Line-heights unitless: <yes/no>
Platforms: web <unit> · ios <unit> · android <unit>
Off-token values in codebase: <n> — <locations>
Propagation test: changed <token> → <n> surfaces updated, <expected?>
Fluid strategy: <clamp-in-token | derived | n/a>
8. Examples
Scale from hierarchy-and-scale (1.2 ratio, body 14, four levels) into tokens.
Primitives carry the raw ramp:
font-size-100: 0.875rem…font-size-400: 1.5rem, numeric so a value can be inserted later without renaming. Semantics name roles:text-body,text-card-title,text-section,text-page-title— each a composite token bundling family, size, weight, line-height, and tracking, so a size change can't leave line-height behind. Components reference only semantics. Android emitssp, iOS maps to Dynamic Type text styles, web emitsrem. Added a lint rule failing the build on any rawfont-sizeoutside the token file — without that, the scale is advisory.
9. Counterexamples
- ❌
--text-16: 16px— names the value; guarantees a lie on first change. - ❌
--heading-blue-large— appearance naming, three ways. - ❌ Component token pointing straight at a primitive. — Skips the semantic layer, so rebranding requires touching components.
- ❌
lineHeight: "1.5rem"— fixed unit; breaks at other sizes. - ❌ Android text in
dp. — Ignores the user's font-size preference. - ❌ "We have tokens" with 200 raw values in the codebase. — Documentation, not enforcement.
- ❌ Maintaining the scale by hand in both Figma and CSS. — They will diverge.
10. Source citations
- Stocks, Universal Principles of Typography — a typographic scale is already a design system; a full system documents how typographic elements interact with other elements and offers a limited set of options via constraints.
- Brown, Flexible Typesetting — the modular scale as a shared system of measurement referenced throughout a composition.
- Santa Maria, On Web Typography — devising a sizing and spacing system; standardising method reproducibly and enabling consistency across a team.
- Latin, Better Web Typography — modular scale and meaningful typography.
- W3C Design Tokens Community Group format —
composite
typographytoken type. - MDN — custom properties.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.