typescale
This is a standalone composable skill. It works two ways:
- Standalone — user runs
/gsp:typescale "Inter" --ratio 1.25directly, gets a production-ready type system - As a building block — identity phase detects existing typography files and reuses them
Visual companion: https://typescale.com/ — users can preview ratios interactively there, then feed the values here.
Input: Font family, ratio, and options (args or interactive)
Output: typography.md foundation chunk + tailwind.typography.css (Tailwind/shadcn) or typescale.css (vanilla) in the target directory
Agent: None — mathematical scale generation, handled inline
<execution_context> @${CLAUDE_SKILL_DIR}/../../references/chunk-format.md @${CLAUDE_SKILL_DIR}/../../references/typography-scales.md </execution_context>
Read the user's input to determine the mode:
| Input | Mode |
|---|---|
/gsp:typescale "Inter" --ratio 1.25 |
Direct — font and ratio from args |
/gsp:typescale --from-style cyberpunk |
From style — extract typography from a preset |
/gsp:typescale |
Interactive — ask for inputs |
/gsp:typescale --list-ratios |
List — show available ratios |
/gsp:typescale --preview "Inter" --ratio 1.25 |
Preview — show scale without writing files |
Additional flags (combinable with any mode):
- --vanilla — output plain CSS custom properties instead of Tailwind format
- --fluid — use clamp()-based fluid sizing instead of breakpoint steps (default: fluid)
- --no-fluid — use breakpoint steps only, no clamp()
- --grid 4 — vertical rhythm grid unit in px (default: 4)
Step 1: List ratios mode (--list-ratios)
If --list-ratios, display the built-in ratios with practical context:
/gsp:typescale — ratios
═══════════════════════════════════════
Name Ratio Character Best for
──────────────────────────────────────────────────────────────────
minor-second 1.067 Nearly invisible steps Dense data UIs, admin panels
major-second 1.125 Gentle, functional Documentation, dashboards
minor-third 1.200 Balanced, versatile Most product UIs (Polaris uses this)
major-third 1.250 Clear hierarchy Marketing + product hybrid
perfect-fourth 1.333 Strong contrast Content-heavy sites, blogs
augmented-fourth 1.414 Dramatic Editorial, magazine layouts
perfect-fifth 1.500 Very dramatic Landing pages, hero sections
golden-ratio 1.618 Maximum drama Art, luxury, display-heavy
──────────────────────────────────────────────────────────────────
Usage: /gsp:typescale "Inter" --ratio 1.25
Preview interactively: https://typescale.com/
Stop here. Do not write any files.
Step 2: Collect inputs
Direct mode (args provided)
Parse from the invocation:
- Font family — quoted string (e.g.,
"Inter") - --ratio — scale ratio (e.g.,
1.25) - --secondary — optional secondary font (e.g.,
--secondary "Merriweather") - --mono — optional monospace font (e.g.,
--mono "Geist Mono") - --base — optional base size in px (default:
16) - --weights — optional weight list (e.g.,
--weights 400,500,700) - --line-height — optional base line-height override (default:
1.5) - --letter-spacing — optional base letter-spacing override (default:
0)
From-style mode (--from-style)
Read the style preset YAML from ../gsp-style/styles/{name}.yml. Extract:
typography.font-family-primary→ primary fonttypography.font-family-mono→ mono fonttypography.font-size-base→ base sizetypography.font-weight-heading→ heading weighttypography.font-weight-body→ body weighttypography.line-height-base→ base line height
Calculate the implied ratio from the preset's type scale if present, or default to major-third (1.25).
Interactive mode (no args)
Use AskUserQuestion for each input:
-
Primary font — "What's your primary font family?" with options:
- Inter — "clean geometric sans-serif, great all-rounder"
- Geist Sans — "Vercel's modern sans — pairs with Geist Mono"
- Plus Jakarta Sans — "modern geometric with personality"
- DM Sans — "low-contrast geometric, contemporary"
- Space Grotesk — "technical, monospaced-inspired sans"
- Instrument Serif — "elegant serif with sharp details"
- Custom — "enter your own font family"
-
Scale ratio — "What scale ratio? Preview interactively at https://typescale.com/" with options:
- minor-third (1.2) — "balanced, safe default — used by Shopify Polaris"
- major-third (1.25) — "clear hierarchy, marketing-friendly"
- perfect-fourth (1.333) — "strong contrast, bold headlines"
- augmented-fourth (1.414) — "dramatic, editorial feel"
- Custom — "enter a custom ratio"
-
Base size — default to 16px unless user specifies otherwise
Step 3: Calculate type scale
Generate a 9-level scale from the base size and ratio. Each level is calculated as:
size = base × ratio^n
Where n is the level's exponent:
| Level | Exponent | Tailwind class | shadcn equivalent | Purpose |
|---|---|---|---|---|
| Display | 5 | text-6xl–text-7xl |
— | Hero headlines, splash screens |
| H1 | 4 | text-4xl |
scroll-m-20 text-4xl font-extrabold tracking-tight |
Page titles |
| H2 | 3 | text-3xl |
scroll-m-20 text-3xl font-semibold tracking-tight |
Section headings |
| H3 | 2 | text-2xl |
scroll-m-20 text-2xl font-semibold tracking-tight |
Subsection headings |
| H4 | 1 | text-xl |
scroll-m-20 text-xl font-semibold tracking-tight |
Minor headings |
| body-large | 0.5* | text-lg |
Lead: text-xl text-muted-foreground |
Lead paragraphs, intros |
| body | 0 | text-base |
leading-7 [&:not(:first-child)]:mt-6 |
Default body text (= base size) |
| body-small | -1 | text-sm |
Small: text-sm font-medium leading-none |
Secondary text, metadata |
| caption | -2 | text-xs |
Muted: text-sm text-muted-foreground |
Labels, helper text |
| overline | -2 | text-xs |
— | All-caps category labels (same size as caption) |
*body-large uses exponent 0.5 (half-step) to avoid an awkward gap between body and H4.
Round px values to nearest 0.5px. Calculate rem as px / 16.
Line height per level (snapped to 4px grid)
Line height is calculated as: ceil(fontSize * ratio / gridUnit) * gridUnit
Where ratio is the target unitless line-height and gridUnit is 4px.
This ensures vertical rhythm — every line-height is a multiple of 4px.
| Level | Target ratio | Grid-snapped example (16px base, 1.25 ratio) |
|---|---|---|
| Display | 1.1 | Round to nearest 4px multiple |
| H1 | 1.15 | |
| H2 | 1.2 | |
| H3 | 1.25 | |
| H4 | 1.3 | |
| body-large | 1.5 | |
| body | 1.5 | 24px (6 × 4px) — anchor for spacing scale |
| body-small | 1.5 | 20px (5 × 4px) |
| caption | 1.4 | 16px (4 × 4px) |
| overline | 1.5 | 16px (4 × 4px) |
If user provided --line-height, use it as the body target ratio and adjust proportionally.
Letter spacing per level (size-dependent curve)
Based on the principle: small text needs more space, large text needs less. Reference: Apple SF Pro tracking, Tailwind defaults.
| Level | Letter spacing | Tailwind token | Rationale |
|---|---|---|---|
| Display | -0.025em | tracking-tighter |
Tighten large type |
| H1 | -0.025em | tracking-tight |
|
| H2 | -0.025em | tracking-tight |
|
| H3 | -0.015em | tracking-tight |
|
| H4 | -0.01em | tracking-tight |
|
| body-large | 0 | tracking-normal |
Neutral |
| body | 0 | tracking-normal |
|
| body-small | 0.01em | tracking-normal |
Slightly open small text |
| caption | 0.015em | tracking-wide |
|
| overline | 0.1em | tracking-wider |
Wide-tracked for all-caps |
Step 3.5: Calculate fluid type values
For each heading level (Display through H4), generate a clamp() value that fluidly scales between mobile and desktop viewports.
Parameters:
- Min viewport: 375px (mobile)
- Max viewport: 1280px (desktop)
- Mobile ratio: step down two named ratios from chosen ratio
- Desktop ratio: the chosen ratio
clamp() formula:
slope = (maxSize - minSize) / (maxViewport - minViewport)
intercept = minSize - slope * minViewport
clamp(minSize_rem, intercept_rem + slope * 100vw, maxSize_rem)
Ratio step-down map (each entry steps to the next lower named ratio):
golden-ratio → perfect-fifth → augmented-fourth
perfect-fifth → augmented-fourth → perfect-fourth
augmented-fourth → perfect-fourth → major-third
perfect-fourth → major-third → minor-third
major-third → minor-third → major-second
minor-third → major-second → minor-second
major-second → minor-second → minor-second
minor-second → minor-second → minor-second (floor)
Mobile size = base × mobileRatio^exponent
Desktop size = base × desktopRatio^exponent
Body and below don't scale — they stay at the base size across all viewports.
If --no-fluid is passed, skip clamp() and use breakpoint-only @media rules instead.
Step 4: Preview mode (--preview)
If --preview, display the full scale without writing files:
/gsp:typescale preview — {font family}
═══════════════════════════════════════
Base: {base}px | Ratio: {ratio} ({ratio name})
Fluid: 375px → 1280px | Mobile ratio: {mobile ratio name}
Level Mobile Desktop Fluid clamp() Weight LH LS
───────────────────────────────────────────────────────────────────────────────────────────────────
Display {px}px {px}px clamp({min}, {pref}, {max}) {wt} {lh} -0.025em
H1 {px}px {px}px clamp({min}, {pref}, {max}) {wt} {lh} -0.025em
H2 {px}px {px}px clamp({min}, {pref}, {max}) {wt} {lh} -0.025em
H3 {px}px {px}px clamp({min}, {pref}, {max}) {wt} {lh} -0.015em
H4 {px}px {px}px clamp({min}, {pref}, {max}) {wt} {lh} -0.01em
body-large {px}px — — {wt} {lh} 0
body {base}px — — {wt} {lh} 0
body-small {px}px — — {wt} {lh} 0.01em
caption {px}px — — {wt} {lh} 0.015em
overline {px}px — — 600 {lh} 0.1em
───────────────────────────────────────────────────────────────────────────────────────────────────
Vertical rhythm grid: {grid}px
Body line-height: {lh}px ({lh/base} unitless) — spacing anchor
Run /gsp:typescale "{font}" --ratio {ratio} to write files.
Stop here. Do not write any files.
Step 5: Resolve output path
Determine where to write the typography output:
Within a brand identity
If a brand context exists (.design/branding/{brand}/):
- Write to
{BRAND_PATH}/identity/ - Typography files sit alongside other identity artifacts
Standalone (no brand context)
- Write to
.design/branding/_typescale/ - Create minimal directory structure
Step 6: Write typography.md
Write {OUTPUT_PATH}/typography.md as a foundation chunk per references/chunk-format.md:
# Typography
> Phase: identity | Brand: {name} | Generated: {DATE}
---
## Font Families
| Role | Family | Variable | Source | Fallback stack |
|------|--------|----------|--------|----------------|
| Primary | {family} | {yes/no} | Google Fonts | {system fallbacks} |
| Secondary | {family or "—"} | {yes/no} | {source} | {system fallbacks} |
| Monospace | {family or "Geist Mono"} | {yes/no} | Google Fonts | ui-monospace, monospace |
**Google Fonts URL:**
`https://fonts.googleapis.com/css2?family={primary}:wght@{weights}&family={mono}:wght@400;700&display=swap`
**Self-hosting recommended** for production — eliminates third-party DNS lookup, improves GDPR compliance, and allows precise subsetting. Use WOFF2 only.
**Performance budget:** < 100KB total font weight, 2-3 weights max. If using a variable font, a single file replaces all static weights (< 150KB).
## Type Scale
Base: {base}px | Ratio: {ratio} ({ratio name}) | Grid: {grid}px
| Level | px | rem | Fluid | Weight | Line height | Letter spacing | Tailwind |
|-------|----|-----|-------|--------|-------------|----------------|----------|
| Display | {px} | {rem} | clamp({...}) | {heading wt} | {lh}px / {unitless} | -0.025em | text-[{rem}rem] tracking-tighter |
| H1 | {px} | {rem} | clamp({...}) | {heading wt} | {lh}px / {unitless} | -0.025em | text-4xl tracking-tight font-extrabold |
| H2 | {px} | {rem} | clamp({...}) | {heading wt} | {lh}px / {unitless} | -0.025em | text-3xl tracking-tight font-semibold |
| H3 | {px} | {rem} | clamp({...}) | {heading wt} | {lh}px / {unitless} | -0.015em | text-2xl tracking-tight font-semibold |
| H4 | {px} | {rem} | clamp({...}) | {heading wt} | {lh}px / {unitless} | -0.01em | text-xl tracking-tight font-semibold |
| body-large | {px} | {rem} | — | {body wt} | {lh}px / {unitless} | 0 | text-lg |
| body | {base}px | 1rem | — | {body wt} | {lh}px / {unitless} | 0 | text-base leading-7 |
| body-small | {px} | {rem} | — | {body wt} | {lh}px / {unitless} | 0.01em | text-sm |
| caption | {px} | {rem} | — | {body wt} | {lh}px / {unitless} | 0.015em | text-xs |
| overline | {px} | {rem} | — | 600 | {lh}px / {unitless} | 0.1em | text-xs uppercase tracking-wider |
## shadcn/ui Typography Classes
Ready-to-use className strings for shadcn projects. These match the calculated scale above:
```tsx
// Headings
<h1 className="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl">
<h2 className="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0">
<h3 className="scroll-m-20 text-2xl font-semibold tracking-tight">
<h4 className="scroll-m-20 text-xl font-semibold tracking-tight">
// Body
<p className="leading-7 [&:not(:first-child)]:mt-6">
<p className="text-xl text-muted-foreground"> {/* Lead */}
<p className="text-lg font-semibold"> {/* Large */}
<p className="text-sm font-medium leading-none"> {/* Small */}
<p className="text-sm text-muted-foreground"> {/* Muted */}
// Special
<blockquote className="mt-6 border-l-2 pl-6 italic">
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold">
<span className="text-xs uppercase tracking-wider font-semibold"> {/* Overline */}
To customize these for your exact scale, extend Tailwind with the CSS file below.
Vertical Rhythm
Grid unit: {grid}px | Body line-height: {body lh}px ({unitless})
All line-heights are snapped to {grid}px multiples. The body line-height ({body lh}px) serves as the spacing anchor — use multiples of it for consistent vertical spacing:
| Token | Value | Lines | Usage |
|---|---|---|---|
| space-xs | {grid}px | {fraction} | Inline element gaps |
| space-sm | {grid*2}px | {fraction} | Related element gaps |
| space-md | {body lh / 2}px | 0.5 | Compact section spacing |
| space-lg | {body lh}px | 1 | Default paragraph spacing |
| space-xl | {body lh * 1.5}px | 1.5 | Section breaks |
| space-2xl | {body lh * 2}px | 2 | Major section breaks |
CSS lh and rlh units (94%+ browser support) can reference line-height directly:
p + p { margin-block: 1lh; } /* one line of spacing */
section { padding-block: 2rlh; } /* two root-line-heights */
Weights
| Name | Value | Usage |
|---|---|---|
| Regular | {body weight} | Body text, descriptions |
| Medium | 500 | Emphasized body, nav items, labels |
| Semibold | 600 | shadcn heading default (H2-H4), overlines |
| Bold / Extrabold | {heading weight} | H1, page titles, CTAs |
Accessibility
This scale meets WCAG 2.2 AA requirements:
- Body line-height ≥ 1.5× font size (SC 1.4.12) ✓
- Layout survives 200% zoom (SC 1.4.4) — fluid clamp() uses rem bounds ✓
- Minimum text size: {caption px}px (practical floor 12px) ✓
- Max line length recommendation: 60–75 characters (
max-w-prosein Tailwind = 65ch)
SC 1.4.12 resilience: Layout must not break when users override to: line-height ≥ 1.5×, letter-spacing ≥ 0.12×, word-spacing ≥ 0.16×, paragraph-spacing ≥ 2× font size. Avoid fixed-height text containers.
Variable Font Notes
{If the primary font is a variable font:}
- Enable optical sizing:
font-optical-sizing: auto(browser maps opsz to font-size automatically) - Dark mode grade adjustment:
@media (prefers-color-scheme: dark) { body { font-variation-settings: 'GRAD' -25; } }— reduces apparent weight of light-on-dark text - Use
font-variation-settings: 'wght' {value}for precise weight control beyond standard keywords {End if}
Modern CSS Enhancements
h1, h2, h3, h4 { text-wrap: balance; } /* Auto-balance heading line lengths */
p { text-wrap: pretty; } /* Avoid orphans in paragraphs */
Related
## Step 7: Write CSS output
### Tailwind / shadcn mode (default)
Write `{OUTPUT_PATH}/tailwind.typography.css` — extends Tailwind v4 via `@theme` with the calculated scale. Drop into your project and import in `globals.css`:
```css
/* Typography — {font family} @ {ratio} ({ratio name})
Generated by /gsp:typescale | {DATE}
Import in globals.css: @import './tailwind.typography.css';
Preview: https://typescale.com/ */
/* ─── Font imports ─── */
@import url('https://fonts.googleapis.com/css2?family={primary}:wght@{weights}&family={mono}:wght@400;700&display=swap');
/* ─── Tailwind v4 theme extension ─── */
@theme {
/* Font families */
--font-sans: '{primary}', ui-sans-serif, system-ui, sans-serif;
--font-mono: '{mono}', ui-monospace, 'SFMono-Regular', monospace;
/* Custom font size tokens (extend Tailwind's built-in scale) */
--text-display: {display rem}rem;
--text-display--line-height: {display lh};
--text-display--letter-spacing: -0.025em;
--text-display--font-weight: {heading weight};
--text-h1: {h1 rem}rem;
--text-h1--line-height: {h1 lh};
--text-h1--letter-spacing: -0.025em;
--text-h1--font-weight: {heading weight};
--text-h2: {h2 rem}rem;
--text-h2--line-height: {h2 lh};
--text-h2--letter-spacing: -0.025em;
--text-h2--font-weight: {heading weight};
--text-h3: {h3 rem}rem;
--text-h3--line-height: {h3 lh};
--text-h3--letter-spacing: -0.015em;
--text-h3--font-weight: 600;
--text-h4: {h4 rem}rem;
--text-h4--line-height: {h4 lh};
--text-h4--letter-spacing: -0.01em;
--text-h4--font-weight: 600;
--text-body-large: {body-large rem}rem;
--text-body-large--line-height: {body-large lh};
--text-overline: {overline rem}rem;
--text-overline--line-height: {overline lh};
--text-overline--letter-spacing: 0.1em;
--text-overline--font-weight: 600;
}
/* ─── Fluid type (clamp) ─── */
:root {
--fs-display: clamp({display min}rem, {display intercept}rem + {display slope}vw, {display max}rem);
--fs-h1: clamp({h1 min}rem, {h1 intercept}rem + {h1 slope}vw, {h1 max}rem);
--fs-h2: clamp({h2 min}rem, {h2 intercept}rem + {h2 slope}vw, {h2 max}rem);
--fs-h3: clamp({h3 min}rem, {h3 intercept}rem + {h3 slope}vw, {h3 max}rem);
--fs-h4: clamp({h4 min}rem, {h4 intercept}rem + {h4 slope}vw, {h4 max}rem);
}
/* ─── Typography utility classes ─── */
/* Use these or compose with Tailwind's built-in classes */
.text-display {
font-size: var(--fs-display);
line-height: {display lh};
letter-spacing: -0.025em;
font-weight: {heading weight};
text-wrap: balance;
}
.prose-headings h1, .text-h1 {
font-size: var(--fs-h1);
line-height: {h1 lh};
letter-spacing: -0.025em;
font-weight: {heading weight};
text-wrap: balance;
}
.prose-headings h2, .text-h2 {
font-size: var(--fs-h2);
line-height: {h2 lh};
letter-spacing: -0.025em;
font-weight: 600;
text-wrap: balance;
}
.prose-headings h3, .text-h3 {
font-size: var(--fs-h3);
line-height: {h3 lh};
letter-spacing: -0.015em;
font-weight: 600;
text-wrap: balance;
}
.prose-headings h4, .text-h4 {
font-size: var(--fs-h4);
line-height: {h4 lh};
letter-spacing: -0.01em;
font-weight: 600;
text-wrap: balance;
}
.text-body-large {
font-size: {body-large rem}rem;
line-height: {body-large lh};
}
.text-overline {
font-size: {overline rem}rem;
line-height: {overline lh};
letter-spacing: 0.1em;
font-weight: 600;
text-transform: uppercase;
}
/* ─── Optical sizing + dark mode (variable fonts) ─── */
body {
font-optical-sizing: auto;
}
@media (prefers-color-scheme: dark) {
body {
/* Reduce apparent weight for light-on-dark text */
-webkit-font-smoothing: antialiased;
}
}
/* ─── Modern CSS enhancements ─── */
h1, h2, h3, h4, h5, h6 { text-wrap: balance; }
p { text-wrap: pretty; }
Vanilla mode (--vanilla)
If --vanilla flag is set, write {OUTPUT_PATH}/typescale.css instead — plain CSS custom properties without Tailwind-specific syntax:
/* Typography — {font family} @ {ratio} ({ratio name})
Generated by /gsp:typescale | {DATE} */
@import url('https://fonts.googleapis.com/css2?family={primary}:wght@{weights}&family={mono}:wght@400;700&display=swap');
:root {
/* Font families */
--font-primary: '{primary}', {fallback stack};
--font-secondary: '{secondary or primary}', {fallback stack};
--font-mono: '{mono}', ui-monospace, monospace;
/* Font weights */
--fw-regular: {body weight};
--fw-medium: 500;
--fw-semibold: 600;
--fw-bold: {heading weight};
/* Type scale — fluid */
--fs-display: clamp({...});
--fs-h1: clamp({...});
--fs-h2: clamp({...});
--fs-h3: clamp({...});
--fs-h4: clamp({...});
--fs-body-large: {rem}rem;
--fs-body: {base}rem;
--fs-body-small: {rem}rem;
--fs-caption: {rem}rem;
--fs-overline: {rem}rem;
/* Line heights (4px grid-snapped) */
--lh-display: {value};
--lh-h1: {value};
--lh-h2: {value};
--lh-h3: {value};
--lh-h4: {value};
--lh-body-large: {value};
--lh-body: {value};
--lh-body-small: {value};
--lh-caption: {value};
--lh-overline: {value};
/* Letter spacing */
--ls-display: -0.025em;
--ls-h1: -0.025em;
--ls-h2: -0.025em;
--ls-h3: -0.015em;
--ls-h4: -0.01em;
--ls-body-large: 0;
--ls-body: 0;
--ls-body-small: 0.01em;
--ls-caption: 0.015em;
--ls-overline: 0.1em;
/* Vertical rhythm */
--grid-unit: {grid}px;
--space-line: {body lh}px;
}
Step 8: Completion output
Display the result:
/gsp:typescale — {font family} @ {ratio}
═══════════════════════════════════════
{OUTPUT_PATH}/
├── typography.md Foundation chunk
└── tailwind.typography.css Tailwind v4 / shadcn ready
Scale: {ratio name} ({ratio})
Range: {caption px}px → {display px}px
Levels: 10 (Display → Overline)
Fluid: 375px → 1280px (clamp)
Grid: {grid}px vertical rhythm
─────────────────────────────────────
Then use AskUserQuestion with routing options:
- Generate palette — "pair this type scale with color palettes" → route to
/gsp:palette - Apply a full style — "use a style preset for the complete system" → route to
/gsp:style - Continue to identity — "use this type scale in the branding diamond" → route to
/gsp:brand-identity - Done — "that's all for now"
More from jubscodes/get-shit-pretty
get-shit-pretty
Design engineering for AI coding tools. Full pipeline: brand research, strategy, identity, guidelines, UI design, critique, accessibility audit, build, and review. Expertise skills (color, typography, visuals) serve the entire pipeline. 14 specialized agents with Apple HIG, Nielsen's heuristics, WCAG 2.2 AA, and design token standards.
15gsp-visuals
Define visual direction — imagery, 3D, video, textures, and surface treatments
14gsp-accessibility
Quick contrast checks and token WCAG audits — inline, no agent
14gsp-help
Show all skills
14gsp-color
Design color systems — palettes, contrast, semantic mapping, dark mode
14gsp-typography
Design type systems — scale, pairing, fluid type, vertical rhythm
14