feat: extract typography tokens into dedicated file with semantic aliases and a11y fallbacks
Create typography.tokens.css as single source of truth for the type system: - Extract all primitive tokens from variables.base.css (--text-*, --leading-*, --font-*, --tracking-*) - Add semantic aliases (--text-body, --text-label, --text-caption, --text-title, --text-page-title) - Add --text-md (15px) escape hatch and --tracking-wider (0.05em) token - Add prefers-reduced-motion fallbacks locking fluid headings to fixed midpoints - Document scale ratio (1.25x major third, 14px base) with reference table - Migrate h1/h2 in app-shell.css to semantic aliases - Migrate 4 raw letter-spacing values to --tracking-* tokens - Extend TypographyTokenContractTest with 3 new tests (letter-spacing, aliases, reduced-motion) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,9 @@
|
||||
*/
|
||||
@layer tokens, vendor, layout, vendor-overrides, components, pages, utilities;
|
||||
|
||||
/* Load typography tokens first — consumed by variables.base.css and all downstream layers. */
|
||||
@import url("base/typography.tokens.css") layer(tokens);
|
||||
|
||||
/* Load third-party styles into the vendor layer so app layers can override them predictably. */
|
||||
@import url("../vendor/bootstrap-icons/bootstrap-icons.css") layer(vendor);
|
||||
@import url("../vendor/gridjs/mermaid.min.css") layer(vendor);
|
||||
|
||||
87
web/css/base/typography.tokens.css
Normal file
87
web/css/base/typography.tokens.css
Normal file
@@ -0,0 +1,87 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
/*
|
||||
* Typography tokens — single source of truth for the type system.
|
||||
*
|
||||
* ┌─────────┬──────────┬────────────────────────────────────────────┐
|
||||
* │ Token │ Size │ Notes │
|
||||
* ├─────────┼──────────┼────────────────────────────────────────────┤
|
||||
* │ --text-2xs │ 11px │ base ÷ 1.25²·⁵ (rounded) │
|
||||
* │ --text-xs │ 12px │ base ÷ 1.25² │
|
||||
* │ --text-sm │ 13px │ base ÷ 1.25 │
|
||||
* │ --text-base│ 14px │ ← anchor (enterprise admin density) │
|
||||
* │ --text-md │ 15px │ escape hatch between base and lg │
|
||||
* │ --text-lg │ 16px │ base × 1.25 (rounded to whole px) │
|
||||
* │ --text-xl │ 18px │ base × 1.25² │
|
||||
* │ --text-2xl │ 18–22px│ fluid via clamp() │
|
||||
* │ --text-3xl │ 22–28px│ fluid via clamp() │
|
||||
* │ --text-4xl │ 26–34px│ fluid via clamp() │
|
||||
* │ --text-5xl │ 32–44px│ fluid via clamp() │
|
||||
* └─────────┴──────────┴────────────────────────────────────────────┘
|
||||
*
|
||||
* Scale ratio: ~1.25× (major third) from a 14px base.
|
||||
* Fluid tokens (2xl+) use clamp(min, preferred, max) with a vw component.
|
||||
*
|
||||
* Semantic aliases:
|
||||
* Use semantic aliases for role-based sizing (e.g. "this is a label").
|
||||
* Use primitives when the size is context-independent (e.g. "make it 12px").
|
||||
*/
|
||||
|
||||
@layer tokens {
|
||||
:host,
|
||||
:root {
|
||||
/* ── Size scale (primitives) ──────────────────────────────────── */
|
||||
--text-2xs: 0.6875rem; /* 11px */
|
||||
--text-xs: 0.75rem; /* 12px */
|
||||
--text-sm: 0.8125rem; /* 13px */
|
||||
--text-base: 0.875rem; /* 14px — enterprise admin baseline */
|
||||
--text-md: 0.9375rem; /* 15px — escape hatch */
|
||||
--text-lg: 1rem; /* 16px */
|
||||
--text-xl: 1.125rem; /* 18px */
|
||||
--text-2xl: clamp(1.125rem, 1rem + 0.5vw, 1.375rem); /* 18–22px */
|
||||
--text-3xl: clamp(1.375rem, 1.2rem + 0.75vw, 1.75rem); /* 22–28px */
|
||||
--text-4xl: clamp(1.625rem, 1.4rem + 1vw, 2.125rem); /* 26–34px */
|
||||
--text-5xl: clamp(2rem, 1.7rem + 1.5vw, 2.75rem); /* 32–44px */
|
||||
|
||||
/* ── Semantic aliases ─────────────────────────────────────────── */
|
||||
--text-caption: var(--text-xs); /* small annotations, hints */
|
||||
--text-label: var(--text-sm); /* form labels, nav items */
|
||||
--text-body: var(--text-base); /* default body text */
|
||||
--text-title: var(--text-xl); /* section headings (h2) */
|
||||
--text-page-title: var(--text-2xl); /* page headings (h1) */
|
||||
|
||||
/* ── Line-height tokens ─────────────────────────────────────── */
|
||||
--leading-none: 1;
|
||||
--leading-tight: 1.2;
|
||||
--leading-snug: 1.375;
|
||||
--leading-normal: 1.5;
|
||||
--leading-relaxed: 1.625;
|
||||
--leading-loose: 1.75;
|
||||
|
||||
/* ── Font-weight tokens ─────────────────────────────────────── */
|
||||
--font-regular: 400;
|
||||
--font-medium: 500;
|
||||
--font-semibold: 600;
|
||||
--font-bold: 700;
|
||||
|
||||
/* ── Letter-spacing tokens ──────────────────────────────────── */
|
||||
--tracking-tight: -0.01em;
|
||||
--tracking-normal: 0;
|
||||
--tracking-wide: 0.025em;
|
||||
--tracking-wider: 0.05em;
|
||||
}
|
||||
|
||||
/* ── Reduced-motion fallback ──────────────────────────────────── */
|
||||
/* Lock fluid heading sizes to fixed midpoints for users who */
|
||||
/* prefer reduced motion — viewport-responsive text can feel */
|
||||
/* jarring when resizing. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:host,
|
||||
:root {
|
||||
--text-2xl: 1.25rem; /* midpoint of 1.125–1.375rem */
|
||||
--text-3xl: 1.5625rem; /* midpoint of 1.375–1.75rem */
|
||||
--text-4xl: 1.875rem; /* midpoint of 1.625–2.125rem */
|
||||
--text-5xl: 2.375rem; /* midpoint of 2–2.75rem */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,36 +75,7 @@
|
||||
--app-empty-text: var(--app-color);
|
||||
--app-empty-hint: var(--app-muted-color);
|
||||
|
||||
/* ── Typography scale (enterprise-density, 14px base, approximate 1.25× ratio) ── */
|
||||
--text-2xs: 0.6875rem; /* 11px */
|
||||
--text-xs: 0.75rem; /* 12px */
|
||||
--text-sm: 0.8125rem; /* 13px */
|
||||
--text-base: 0.875rem; /* 14px — enterprise admin baseline */
|
||||
--text-lg: 1rem; /* 16px */
|
||||
--text-xl: 1.125rem; /* 18px */
|
||||
--text-2xl: clamp(1.125rem, 1rem + 0.5vw, 1.375rem); /* 18–22px */
|
||||
--text-3xl: clamp(1.375rem, 1.2rem + 0.75vw, 1.75rem); /* 22–28px */
|
||||
--text-4xl: clamp(1.625rem, 1.4rem + 1vw, 2.125rem); /* 26–34px */
|
||||
--text-5xl: clamp(2rem, 1.7rem + 1.5vw, 2.75rem); /* 32–44px */
|
||||
|
||||
/* ── Line-height tokens ─────────────────────────────────────── */
|
||||
--leading-none: 1;
|
||||
--leading-tight: 1.2;
|
||||
--leading-snug: 1.375;
|
||||
--leading-normal: 1.5;
|
||||
--leading-relaxed: 1.625;
|
||||
--leading-loose: 1.75;
|
||||
|
||||
/* ── Font-weight tokens ─────────────────────────────────────── */
|
||||
--font-regular: 400;
|
||||
--font-medium: 500;
|
||||
--font-semibold: 600;
|
||||
--font-bold: 700;
|
||||
|
||||
/* ── Letter-spacing tokens ──────────────────────────────────── */
|
||||
--tracking-tight: -0.01em;
|
||||
--tracking-normal: 0;
|
||||
--tracking-wide: 0.025em;
|
||||
/* Typography tokens defined in typography.tokens.css */
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
padding: 4px 10px 4px 10px;
|
||||
text-transform: uppercase;
|
||||
font-size: var(--text-sm);
|
||||
letter-spacing: 0.4px;
|
||||
letter-spacing: var(--tracking-wide);
|
||||
background: var(--app-blockquote-background-color);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,13 +36,13 @@
|
||||
}
|
||||
|
||||
h1 {
|
||||
--app-font-size: var(--text-2xl);
|
||||
--app-font-size: var(--text-page-title);
|
||||
--app-line-height: var(--leading-tight);
|
||||
--app-typography-spacing-top: 3rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
--app-font-size: var(--text-xl);
|
||||
--app-font-size: var(--text-title);
|
||||
--app-line-height: var(--leading-tight);
|
||||
--app-typography-spacing-top: 2.625rem;
|
||||
}
|
||||
@@ -178,7 +178,7 @@
|
||||
th {
|
||||
text-transform: uppercase;
|
||||
font-size: var(--text-xs);
|
||||
letter-spacing: 0.4px;
|
||||
letter-spacing: var(--tracking-wide);
|
||||
border-top: 1px solid var(--app-border);
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
.app-docs-nav-group summary small {
|
||||
font-size: var(--text-xs);
|
||||
letter-spacing: 0.05em;
|
||||
letter-spacing: var(--tracking-wider);
|
||||
text-transform: uppercase;
|
||||
display: inline;
|
||||
}
|
||||
@@ -133,7 +133,7 @@
|
||||
display: block;
|
||||
padding: 0 0 0.5rem;
|
||||
font-size: var(--text-xs);
|
||||
letter-spacing: 0.05em;
|
||||
letter-spacing: var(--tracking-wider);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user