Skip to content

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) - #526

Merged
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge
Jul 4, 2026
Merged

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1)#526
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

First PR of #520 (design-spec governance tracking issue). Converges the three ungoverned typography dimensions — line-height, font-weight, letter-spacing — onto tokens aligned to a Tailwind scale subset, plus folds font-variation-settings into font-weight and cleans up font-family minor drift. Same converge-contract pattern as #430 PR2 / #448.

Tokens added (maka-tokens.css)

TokenValueAligned toSites
--leading-none/tight/snug/normal1 / 1.25 / 1.375 / 1.5Tailwind leading-* (drop relaxed/loose, 0 sites)~160
--font-weight-normal/medium/semibold/bold400 / 500 / 600 / 700Tailwind font-* (drop thin/extralight/light/extrabold/black)~205
--tracking-normal/wide/wider/widest0 / 0.025em / 0.05em / 0.1emTailwind tracking-* (no tight — CJK-first, roadmap §1.6 bans tightening CJK)~62
--font-serifGeorgia, "Times New Roman", Times, serif(onboarding hero)1

Bridged to Tailwind utilities via @theme inline in styles.css (same inline-bridge pattern as --text-* / --space-*), so var(--leading-*) in CSS and leading-* in TSX share one source.

Convergence

  • line-height: 15 distinct bare values + one 20px magic → 4 tokens. Snug is the one tier that can't be cut (40 sites at 1.3-1.4 are 13/11px small-text dense zones; collapsing to tight/normal is ±0.1-0.2, visible on small text). Largest delta: .maka-bubble-assistant 1.68 → 1.5 (chat core, magic-number fix).
  • font-weight: 8 distinct (incl. mid-axis 550/620/650/680 from variable Geist) → 4 tokens. Snap: 550/620/650 → semibold (600), 680 → bold (700).
  • letter-spacing: 14 distinct (incl. CJK mis-tightening -0.018em) → 4 tokens. All negatives snap to --tracking-normal (0) per CJK ban. ALL-CAPS labels use --tracking-wider/widest (roadmap §4.1). 1px--tracking-widest (0.1em ≈ 1.3px @ 13px).
  • font-variation-settings: dropped .maka-hero h1's "wght" 550, "wdth" 105 — wght folds into font-weight token, wdth:105 one-off dropped (hero stays distinct via size + weight + leading alone).
  • font-family minor: var(--mono-font, monospace)var(--font-mono) (was a typo'd token name); redundant var(--font-mono, ui-monospace, …) fallbacks → var(--font-mono); Georgia, serifvar(--font-serif) (tokenized).

Contracts

Two new converge-contract tests, mirroring typography-converge-contract.test.ts:

  • font-weight-converge-contract.test.ts — bans bare font-weight numbers + normal/bold/lighter/bolder + bare weight in font: shorthand; whitelists 4 --font-weight-* tokens + inherit/initial/unset/revert; pins values; asserts @theme inline bridging.
  • letter-spacing-converge-contract.test.ts — bans bare letter-spacing em/px/normal/negative; whitelists 4 --tracking-* tokens + 0 + literals; pins values; asserts bridging.

(line-height-converge-contract.test.ts landed in the first commit pair of this PR.)

Verification

  • line-height-converge-contract: 9/9 pass
  • font-weight-converge-contract: 8/8 pass
  • letter-spacing-converge-contract: 7/7 pass
  • Full desktop suite: 1910/1910 pass (no regressions; +15 from the two new contracts)
  • renderer build: success (Tailwind v4 @theme inline same-name --leading-* / --font-weight-* / --tracking-* bridging verified)
  • Dual-theme screenshots (turn-narrative + first-run, light + dark): visual deltas are barely-perceptible as expected — .maka-hero h1 is the most visible (wdth 105 drop + 550→600 + tracking -0.018→0); confirmed acceptable.

Commits

  1. refactor(ui): converge line-height onto --leading-* tokens — 25 css, 4 tokens + bridge + ~160 replacements
  2. test(ui): lock line-height converge contract — 9 tests
  3. refactor(ui): converge font-weight + letter-spacing + font-family minor — 28 css, 9 tokens + bridge + ~267 replacements + font-variation fold + font-family cleanup
  4. test(ui): lock font-weight + letter-spacing converge contracts — 15 tests

Out of scope (next PRs in #520)

PR2 visual-state & motion tokens (opacity / focus-ring / motion duration+amplitude), PR3 anti-layout-shift, PR4 layout surface & sizing, PR5 Base UI small migrations + style-hook, PR6 Base UI large migrations.

Tracking issue: #520.

Add 4 --leading-* tokens (none/tight/snug/normal = 1/1.25/1.375/1.5) aligned to a Tailwind leading-scale subset (drop relaxed/loose, zero sites). Bridge --leading-* to Tailwind leading-* utilities in @theme inline. Replace ~160 bare line-height values across renderer CSS with var(--leading-*), incl. the 20px magic (plan-reminders.css) and 1.68 bubble-assistant. Snug is the one tier that can't be cut: 40 sites at 1.3-1.4 (13/11px small-text dense zones) collapse to tight/normal at ±0.1-0.2, visible on small text. Tracking issue: #520.
Add line-height-converge-contract.test.ts mirroring typography-converge-contract: ban bare line-height numbers/px/rem/normal, whitelist 4 --leading-* tokens + em + inherit/initial/unset/revert/0; pin token values; assert Tailwind @theme inline bridging. Tracking issue: #520.
…or (#520 PR1)
Add --font-weight-* (4-tier: normal/medium/semibold/bold = 400/500/600/700) + --tracking-* (4-tier: normal/wide/wider/widest = 0/0.025em/0.05em/0.1em; no tight — CJK-first, roadmap §1.6 bans tightening CJK) + --font-serif tokens; bridge to Tailwind font-*/tracking-* utilities in @theme inline. Replace ~205 font-weight (550/620/650→semibold, 680→bold) + ~62 letter-spacing (negatives→normal) values across renderer CSS. Drop font-variation-settings "wght" 550 "wdth" 105 on .maka-hero h1 (wght folds into font-weight token, wdth one-off dropped). Fix font-family: var(--mono-font,…)→var(--font-mono), redundant fallbacks→var(--font-mono), Georgia,serif→var(--font-serif). Tracking issue: #520.
…PR1)
Add font-weight-converge-contract.test.ts + letter-spacing-converge-contract.test.ts mirroring line-height-converge-contract: ban bare font-weight numbers/normal/bold + bare letter-spacing em/px/normal/negative; whitelist --font-weight-*/--tracking-* tokens + literals; pin token values; assert @theme inline bridging. Tracking issue: #520.
@Astro-Han
Astro-Hanforce-pushed the refactor/typography-converge branch from dfb801b to e772d93CompareJuly 4, 2026 17:54
P2: readAllRendererCss now fails closed — removed the catch that silently degraded to reading only styles.css when @import expansion broke, so a missing/bad import surfaces instead of skipping styles/* (exported expandCssImports for direct testing).
P2: ban non-literal font: shorthand via shared findFontShorthandOffenders — covers font-weight + line-height bypass vectors (font: 600 var(--font-size-ui) …, font: var(--font-size-ui)/1.4 …). Renderer CSS only uses font: inherit today, so the whitelist is literals-only (inherit/initial/unset/revert); no regex arms race over which shorthand component is bare. Value is extracted and checked against the literal set rather than using a negative lookahead (which backtracks \s* and matches font: inherit as an offender).
P2: pin regex now ends with \s*; so drifted values fail — --font-weight-normal: 4000; no longer prefix-matches 400, --tracking-normal: 0.02em; no longer prefix-matches 0, --leading-normal: 1.55; no longer prefix-matches 1.5.
P3: extracted shared font: shorthand scanner into css-test-helpers (partial de-dup of the three converge contracts; full table-driven merge left out to keep per-property invariant docs readable).
New css-test-helpers.test.ts: bad @import throws with the missing path, font: shorthand literal vs non-literal, comment stripping.
Tracking issue: #520.
P2: replace scattered assert.match pin regex with shared assertTokenPinnedOnce. assert.match only proved a correct declaration existed somewhere — a later overriding declaration (--font-weight-normal: 400; --font-weight-normal: 450;) still passed because the first match satisfied it, so a drifted override would slip through. assertTokenPinnedOnce parses maka-tokens.css custom property declarations and asserts each typography token is declared exactly once with the exact value (fails on duplicates and on a single drifted value).
New css-test-helpers exports: parseCssCustomProps, assertTokenPinnedOnce.
css-test-helpers.test.ts: assertTokenPinnedOnce unit tests (duplicate override, single drift, missing token, comment strip).
Three converge contracts: pin tests use assertTokenPinnedOnce; dropped the now-redundant pin regex negative tests (they tested the old regex mechanism, now superseded).
Reviewer negative cases locked: --font-weight-normal: 400; --font-weight-normal: 450; / --leading-normal: 1.5; --leading-normal: 1.55; / --tracking-normal: 0; --tracking-normal: 0.02em; all fail with 'exactly once'.
Tracking issue: #520.
P2: the styles.css @theme inline bridge aliases (--leading-*/--font-weight-*/--tracking-* → var(--*)) were still checked with assert.match, so a duplicate overriding declaration (--leading-normal: var(--leading-normal); --leading-normal: 1.55;) would pass because the first match satisfied assert.match. Reused the custom property parser: bridge tests now use assertCustomPropPinnedOnce (exactly-once + exact value), same as token pin tests.
Renamed assertTokenPinnedOnce → assertCustomPropPinnedOnce (the helper was never token-specific — it parses any custom property; the new name fits both maka-tokens.css definitions and styles.css bridge aliases).
Reviewer negative cases locked: --font-weight-normal: var(--font-weight-normal); --font-weight-normal: 450; / --leading-normal: var(--leading-normal); --leading-normal: 1.55; / --tracking-normal: var(--tracking-normal); --tracking-normal: 0.02em; all fail with 'exactly once' (css-test-helpers.test.ts).
Tracking issue: #520.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) by Astro-Han · Pull Request #526 · apache/maka · GitHub
Skip to content

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) - #526

Merged
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge
Jul 4, 2026
Merged

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1)#526
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

First PR of #520 (design-spec governance tracking issue). Converges the three ungoverned typography dimensions — line-height, font-weight, letter-spacing — onto tokens aligned to a Tailwind scale subset, plus folds font-variation-settings into font-weight and cleans up font-family minor drift. Same converge-contract pattern as #430 PR2 / #448.

Tokens added (maka-tokens.css)

TokenValueAligned toSites
--leading-none/tight/snug/normal1 / 1.25 / 1.375 / 1.5Tailwind leading-* (drop relaxed/loose, 0 sites)~160
--font-weight-normal/medium/semibold/bold400 / 500 / 600 / 700Tailwind font-* (drop thin/extralight/light/extrabold/black)~205
--tracking-normal/wide/wider/widest0 / 0.025em / 0.05em / 0.1emTailwind tracking-* (no tight — CJK-first, roadmap §1.6 bans tightening CJK)~62
--font-serifGeorgia, "Times New Roman", Times, serif(onboarding hero)1

Bridged to Tailwind utilities via @theme inline in styles.css (same inline-bridge pattern as --text-* / --space-*), so var(--leading-*) in CSS and leading-* in TSX share one source.

Convergence

  • line-height: 15 distinct bare values + one 20px magic → 4 tokens. Snug is the one tier that can't be cut (40 sites at 1.3-1.4 are 13/11px small-text dense zones; collapsing to tight/normal is ±0.1-0.2, visible on small text). Largest delta: .maka-bubble-assistant 1.68 → 1.5 (chat core, magic-number fix).
  • font-weight: 8 distinct (incl. mid-axis 550/620/650/680 from variable Geist) → 4 tokens. Snap: 550/620/650 → semibold (600), 680 → bold (700).
  • letter-spacing: 14 distinct (incl. CJK mis-tightening -0.018em) → 4 tokens. All negatives snap to --tracking-normal (0) per CJK ban. ALL-CAPS labels use --tracking-wider/widest (roadmap §4.1). 1px--tracking-widest (0.1em ≈ 1.3px @ 13px).
  • font-variation-settings: dropped .maka-hero h1's "wght" 550, "wdth" 105 — wght folds into font-weight token, wdth:105 one-off dropped (hero stays distinct via size + weight + leading alone).
  • font-family minor: var(--mono-font, monospace)var(--font-mono) (was a typo'd token name); redundant var(--font-mono, ui-monospace, …) fallbacks → var(--font-mono); Georgia, serifvar(--font-serif) (tokenized).

Contracts

Two new converge-contract tests, mirroring typography-converge-contract.test.ts:

  • font-weight-converge-contract.test.ts — bans bare font-weight numbers + normal/bold/lighter/bolder + bare weight in font: shorthand; whitelists 4 --font-weight-* tokens + inherit/initial/unset/revert; pins values; asserts @theme inline bridging.
  • letter-spacing-converge-contract.test.ts — bans bare letter-spacing em/px/normal/negative; whitelists 4 --tracking-* tokens + 0 + literals; pins values; asserts bridging.

(line-height-converge-contract.test.ts landed in the first commit pair of this PR.)

Verification

  • line-height-converge-contract: 9/9 pass
  • font-weight-converge-contract: 8/8 pass
  • letter-spacing-converge-contract: 7/7 pass
  • Full desktop suite: 1910/1910 pass (no regressions; +15 from the two new contracts)
  • renderer build: success (Tailwind v4 @theme inline same-name --leading-* / --font-weight-* / --tracking-* bridging verified)
  • Dual-theme screenshots (turn-narrative + first-run, light + dark): visual deltas are barely-perceptible as expected — .maka-hero h1 is the most visible (wdth 105 drop + 550→600 + tracking -0.018→0); confirmed acceptable.

Commits

  1. refactor(ui): converge line-height onto --leading-* tokens — 25 css, 4 tokens + bridge + ~160 replacements
  2. test(ui): lock line-height converge contract — 9 tests
  3. refactor(ui): converge font-weight + letter-spacing + font-family minor — 28 css, 9 tokens + bridge + ~267 replacements + font-variation fold + font-family cleanup
  4. test(ui): lock font-weight + letter-spacing converge contracts — 15 tests

Out of scope (next PRs in #520)

PR2 visual-state & motion tokens (opacity / focus-ring / motion duration+amplitude), PR3 anti-layout-shift, PR4 layout surface & sizing, PR5 Base UI small migrations + style-hook, PR6 Base UI large migrations.

Tracking issue: #520.

Add 4 --leading-* tokens (none/tight/snug/normal = 1/1.25/1.375/1.5) aligned to a Tailwind leading-scale subset (drop relaxed/loose, zero sites). Bridge --leading-* to Tailwind leading-* utilities in @theme inline. Replace ~160 bare line-height values across renderer CSS with var(--leading-*), incl. the 20px magic (plan-reminders.css) and 1.68 bubble-assistant. Snug is the one tier that can't be cut: 40 sites at 1.3-1.4 (13/11px small-text dense zones) collapse to tight/normal at ±0.1-0.2, visible on small text. Tracking issue: #520.
Add line-height-converge-contract.test.ts mirroring typography-converge-contract: ban bare line-height numbers/px/rem/normal, whitelist 4 --leading-* tokens + em + inherit/initial/unset/revert/0; pin token values; assert Tailwind @theme inline bridging. Tracking issue: #520.
…or (#520 PR1)
Add --font-weight-* (4-tier: normal/medium/semibold/bold = 400/500/600/700) + --tracking-* (4-tier: normal/wide/wider/widest = 0/0.025em/0.05em/0.1em; no tight — CJK-first, roadmap §1.6 bans tightening CJK) + --font-serif tokens; bridge to Tailwind font-*/tracking-* utilities in @theme inline. Replace ~205 font-weight (550/620/650→semibold, 680→bold) + ~62 letter-spacing (negatives→normal) values across renderer CSS. Drop font-variation-settings "wght" 550 "wdth" 105 on .maka-hero h1 (wght folds into font-weight token, wdth one-off dropped). Fix font-family: var(--mono-font,…)→var(--font-mono), redundant fallbacks→var(--font-mono), Georgia,serif→var(--font-serif). Tracking issue: #520.
…PR1)
Add font-weight-converge-contract.test.ts + letter-spacing-converge-contract.test.ts mirroring line-height-converge-contract: ban bare font-weight numbers/normal/bold + bare letter-spacing em/px/normal/negative; whitelist --font-weight-*/--tracking-* tokens + literals; pin token values; assert @theme inline bridging. Tracking issue: #520.
@Astro-Han
Astro-Hanforce-pushed the refactor/typography-converge branch from dfb801b to e772d93CompareJuly 4, 2026 17:54
P2: readAllRendererCss now fails closed — removed the catch that silently degraded to reading only styles.css when @import expansion broke, so a missing/bad import surfaces instead of skipping styles/* (exported expandCssImports for direct testing).
P2: ban non-literal font: shorthand via shared findFontShorthandOffenders — covers font-weight + line-height bypass vectors (font: 600 var(--font-size-ui) …, font: var(--font-size-ui)/1.4 …). Renderer CSS only uses font: inherit today, so the whitelist is literals-only (inherit/initial/unset/revert); no regex arms race over which shorthand component is bare. Value is extracted and checked against the literal set rather than using a negative lookahead (which backtracks \s* and matches font: inherit as an offender).
P2: pin regex now ends with \s*; so drifted values fail — --font-weight-normal: 4000; no longer prefix-matches 400, --tracking-normal: 0.02em; no longer prefix-matches 0, --leading-normal: 1.55; no longer prefix-matches 1.5.
P3: extracted shared font: shorthand scanner into css-test-helpers (partial de-dup of the three converge contracts; full table-driven merge left out to keep per-property invariant docs readable).
New css-test-helpers.test.ts: bad @import throws with the missing path, font: shorthand literal vs non-literal, comment stripping.
Tracking issue: #520.
P2: replace scattered assert.match pin regex with shared assertTokenPinnedOnce. assert.match only proved a correct declaration existed somewhere — a later overriding declaration (--font-weight-normal: 400; --font-weight-normal: 450;) still passed because the first match satisfied it, so a drifted override would slip through. assertTokenPinnedOnce parses maka-tokens.css custom property declarations and asserts each typography token is declared exactly once with the exact value (fails on duplicates and on a single drifted value).
New css-test-helpers exports: parseCssCustomProps, assertTokenPinnedOnce.
css-test-helpers.test.ts: assertTokenPinnedOnce unit tests (duplicate override, single drift, missing token, comment strip).
Three converge contracts: pin tests use assertTokenPinnedOnce; dropped the now-redundant pin regex negative tests (they tested the old regex mechanism, now superseded).
Reviewer negative cases locked: --font-weight-normal: 400; --font-weight-normal: 450; / --leading-normal: 1.5; --leading-normal: 1.55; / --tracking-normal: 0; --tracking-normal: 0.02em; all fail with 'exactly once'.
Tracking issue: #520.
P2: the styles.css @theme inline bridge aliases (--leading-*/--font-weight-*/--tracking-* → var(--*)) were still checked with assert.match, so a duplicate overriding declaration (--leading-normal: var(--leading-normal); --leading-normal: 1.55;) would pass because the first match satisfied assert.match. Reused the custom property parser: bridge tests now use assertCustomPropPinnedOnce (exactly-once + exact value), same as token pin tests.
Renamed assertTokenPinnedOnce → assertCustomPropPinnedOnce (the helper was never token-specific — it parses any custom property; the new name fits both maka-tokens.css definitions and styles.css bridge aliases).
Reviewer negative cases locked: --font-weight-normal: var(--font-weight-normal); --font-weight-normal: 450; / --leading-normal: var(--leading-normal); --leading-normal: 1.55; / --tracking-normal: var(--tracking-normal); --tracking-normal: 0.02em; all fail with 'exactly once' (css-test-helpers.test.ts).
Tracking issue: #520.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) by Astro-Han · Pull Request #526 · apache/maka · GitHub
Skip to content

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) - #526

Merged
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge
Jul 4, 2026
Merged

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1)#526
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

First PR of #520 (design-spec governance tracking issue). Converges the three ungoverned typography dimensions — line-height, font-weight, letter-spacing — onto tokens aligned to a Tailwind scale subset, plus folds font-variation-settings into font-weight and cleans up font-family minor drift. Same converge-contract pattern as #430 PR2 / #448.

Tokens added (maka-tokens.css)

TokenValueAligned toSites
--leading-none/tight/snug/normal1 / 1.25 / 1.375 / 1.5Tailwind leading-* (drop relaxed/loose, 0 sites)~160
--font-weight-normal/medium/semibold/bold400 / 500 / 600 / 700Tailwind font-* (drop thin/extralight/light/extrabold/black)~205
--tracking-normal/wide/wider/widest0 / 0.025em / 0.05em / 0.1emTailwind tracking-* (no tight — CJK-first, roadmap §1.6 bans tightening CJK)~62
--font-serifGeorgia, "Times New Roman", Times, serif(onboarding hero)1

Bridged to Tailwind utilities via @theme inline in styles.css (same inline-bridge pattern as --text-* / --space-*), so var(--leading-*) in CSS and leading-* in TSX share one source.

Convergence

  • line-height: 15 distinct bare values + one 20px magic → 4 tokens. Snug is the one tier that can't be cut (40 sites at 1.3-1.4 are 13/11px small-text dense zones; collapsing to tight/normal is ±0.1-0.2, visible on small text). Largest delta: .maka-bubble-assistant 1.68 → 1.5 (chat core, magic-number fix).
  • font-weight: 8 distinct (incl. mid-axis 550/620/650/680 from variable Geist) → 4 tokens. Snap: 550/620/650 → semibold (600), 680 → bold (700).
  • letter-spacing: 14 distinct (incl. CJK mis-tightening -0.018em) → 4 tokens. All negatives snap to --tracking-normal (0) per CJK ban. ALL-CAPS labels use --tracking-wider/widest (roadmap §4.1). 1px--tracking-widest (0.1em ≈ 1.3px @ 13px).
  • font-variation-settings: dropped .maka-hero h1's "wght" 550, "wdth" 105 — wght folds into font-weight token, wdth:105 one-off dropped (hero stays distinct via size + weight + leading alone).
  • font-family minor: var(--mono-font, monospace)var(--font-mono) (was a typo'd token name); redundant var(--font-mono, ui-monospace, …) fallbacks → var(--font-mono); Georgia, serifvar(--font-serif) (tokenized).

Contracts

Two new converge-contract tests, mirroring typography-converge-contract.test.ts:

  • font-weight-converge-contract.test.ts — bans bare font-weight numbers + normal/bold/lighter/bolder + bare weight in font: shorthand; whitelists 4 --font-weight-* tokens + inherit/initial/unset/revert; pins values; asserts @theme inline bridging.
  • letter-spacing-converge-contract.test.ts — bans bare letter-spacing em/px/normal/negative; whitelists 4 --tracking-* tokens + 0 + literals; pins values; asserts bridging.

(line-height-converge-contract.test.ts landed in the first commit pair of this PR.)

Verification

  • line-height-converge-contract: 9/9 pass
  • font-weight-converge-contract: 8/8 pass
  • letter-spacing-converge-contract: 7/7 pass
  • Full desktop suite: 1910/1910 pass (no regressions; +15 from the two new contracts)
  • renderer build: success (Tailwind v4 @theme inline same-name --leading-* / --font-weight-* / --tracking-* bridging verified)
  • Dual-theme screenshots (turn-narrative + first-run, light + dark): visual deltas are barely-perceptible as expected — .maka-hero h1 is the most visible (wdth 105 drop + 550→600 + tracking -0.018→0); confirmed acceptable.

Commits

  1. refactor(ui): converge line-height onto --leading-* tokens — 25 css, 4 tokens + bridge + ~160 replacements
  2. test(ui): lock line-height converge contract — 9 tests
  3. refactor(ui): converge font-weight + letter-spacing + font-family minor — 28 css, 9 tokens + bridge + ~267 replacements + font-variation fold + font-family cleanup
  4. test(ui): lock font-weight + letter-spacing converge contracts — 15 tests

Out of scope (next PRs in #520)

PR2 visual-state & motion tokens (opacity / focus-ring / motion duration+amplitude), PR3 anti-layout-shift, PR4 layout surface & sizing, PR5 Base UI small migrations + style-hook, PR6 Base UI large migrations.

Tracking issue: #520.

Add 4 --leading-* tokens (none/tight/snug/normal = 1/1.25/1.375/1.5) aligned to a Tailwind leading-scale subset (drop relaxed/loose, zero sites). Bridge --leading-* to Tailwind leading-* utilities in @theme inline. Replace ~160 bare line-height values across renderer CSS with var(--leading-*), incl. the 20px magic (plan-reminders.css) and 1.68 bubble-assistant. Snug is the one tier that can't be cut: 40 sites at 1.3-1.4 (13/11px small-text dense zones) collapse to tight/normal at ±0.1-0.2, visible on small text. Tracking issue: #520.
Add line-height-converge-contract.test.ts mirroring typography-converge-contract: ban bare line-height numbers/px/rem/normal, whitelist 4 --leading-* tokens + em + inherit/initial/unset/revert/0; pin token values; assert Tailwind @theme inline bridging. Tracking issue: #520.
…or (#520 PR1)
Add --font-weight-* (4-tier: normal/medium/semibold/bold = 400/500/600/700) + --tracking-* (4-tier: normal/wide/wider/widest = 0/0.025em/0.05em/0.1em; no tight — CJK-first, roadmap §1.6 bans tightening CJK) + --font-serif tokens; bridge to Tailwind font-*/tracking-* utilities in @theme inline. Replace ~205 font-weight (550/620/650→semibold, 680→bold) + ~62 letter-spacing (negatives→normal) values across renderer CSS. Drop font-variation-settings "wght" 550 "wdth" 105 on .maka-hero h1 (wght folds into font-weight token, wdth one-off dropped). Fix font-family: var(--mono-font,…)→var(--font-mono), redundant fallbacks→var(--font-mono), Georgia,serif→var(--font-serif). Tracking issue: #520.
…PR1)
Add font-weight-converge-contract.test.ts + letter-spacing-converge-contract.test.ts mirroring line-height-converge-contract: ban bare font-weight numbers/normal/bold + bare letter-spacing em/px/normal/negative; whitelist --font-weight-*/--tracking-* tokens + literals; pin token values; assert @theme inline bridging. Tracking issue: #520.
@Astro-Han
Astro-Hanforce-pushed the refactor/typography-converge branch from dfb801b to e772d93CompareJuly 4, 2026 17:54
P2: readAllRendererCss now fails closed — removed the catch that silently degraded to reading only styles.css when @import expansion broke, so a missing/bad import surfaces instead of skipping styles/* (exported expandCssImports for direct testing).
P2: ban non-literal font: shorthand via shared findFontShorthandOffenders — covers font-weight + line-height bypass vectors (font: 600 var(--font-size-ui) …, font: var(--font-size-ui)/1.4 …). Renderer CSS only uses font: inherit today, so the whitelist is literals-only (inherit/initial/unset/revert); no regex arms race over which shorthand component is bare. Value is extracted and checked against the literal set rather than using a negative lookahead (which backtracks \s* and matches font: inherit as an offender).
P2: pin regex now ends with \s*; so drifted values fail — --font-weight-normal: 4000; no longer prefix-matches 400, --tracking-normal: 0.02em; no longer prefix-matches 0, --leading-normal: 1.55; no longer prefix-matches 1.5.
P3: extracted shared font: shorthand scanner into css-test-helpers (partial de-dup of the three converge contracts; full table-driven merge left out to keep per-property invariant docs readable).
New css-test-helpers.test.ts: bad @import throws with the missing path, font: shorthand literal vs non-literal, comment stripping.
Tracking issue: #520.
P2: replace scattered assert.match pin regex with shared assertTokenPinnedOnce. assert.match only proved a correct declaration existed somewhere — a later overriding declaration (--font-weight-normal: 400; --font-weight-normal: 450;) still passed because the first match satisfied it, so a drifted override would slip through. assertTokenPinnedOnce parses maka-tokens.css custom property declarations and asserts each typography token is declared exactly once with the exact value (fails on duplicates and on a single drifted value).
New css-test-helpers exports: parseCssCustomProps, assertTokenPinnedOnce.
css-test-helpers.test.ts: assertTokenPinnedOnce unit tests (duplicate override, single drift, missing token, comment strip).
Three converge contracts: pin tests use assertTokenPinnedOnce; dropped the now-redundant pin regex negative tests (they tested the old regex mechanism, now superseded).
Reviewer negative cases locked: --font-weight-normal: 400; --font-weight-normal: 450; / --leading-normal: 1.5; --leading-normal: 1.55; / --tracking-normal: 0; --tracking-normal: 0.02em; all fail with 'exactly once'.
Tracking issue: #520.
P2: the styles.css @theme inline bridge aliases (--leading-*/--font-weight-*/--tracking-* → var(--*)) were still checked with assert.match, so a duplicate overriding declaration (--leading-normal: var(--leading-normal); --leading-normal: 1.55;) would pass because the first match satisfied assert.match. Reused the custom property parser: bridge tests now use assertCustomPropPinnedOnce (exactly-once + exact value), same as token pin tests.
Renamed assertTokenPinnedOnce → assertCustomPropPinnedOnce (the helper was never token-specific — it parses any custom property; the new name fits both maka-tokens.css definitions and styles.css bridge aliases).
Reviewer negative cases locked: --font-weight-normal: var(--font-weight-normal); --font-weight-normal: 450; / --leading-normal: var(--leading-normal); --leading-normal: 1.55; / --tracking-normal: var(--tracking-normal); --tracking-normal: 0.02em; all fail with 'exactly once' (css-test-helpers.test.ts).
Tracking issue: #520.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) by Astro-Han · Pull Request #526 · apache/maka · GitHub
Skip to content

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) - #526

Merged
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge
Jul 4, 2026
Merged

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1)#526
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

First PR of #520 (design-spec governance tracking issue). Converges the three ungoverned typography dimensions — line-height, font-weight, letter-spacing — onto tokens aligned to a Tailwind scale subset, plus folds font-variation-settings into font-weight and cleans up font-family minor drift. Same converge-contract pattern as #430 PR2 / #448.

Tokens added (maka-tokens.css)

TokenValueAligned toSites
--leading-none/tight/snug/normal1 / 1.25 / 1.375 / 1.5Tailwind leading-* (drop relaxed/loose, 0 sites)~160
--font-weight-normal/medium/semibold/bold400 / 500 / 600 / 700Tailwind font-* (drop thin/extralight/light/extrabold/black)~205
--tracking-normal/wide/wider/widest0 / 0.025em / 0.05em / 0.1emTailwind tracking-* (no tight — CJK-first, roadmap §1.6 bans tightening CJK)~62
--font-serifGeorgia, "Times New Roman", Times, serif(onboarding hero)1

Bridged to Tailwind utilities via @theme inline in styles.css (same inline-bridge pattern as --text-* / --space-*), so var(--leading-*) in CSS and leading-* in TSX share one source.

Convergence

  • line-height: 15 distinct bare values + one 20px magic → 4 tokens. Snug is the one tier that can't be cut (40 sites at 1.3-1.4 are 13/11px small-text dense zones; collapsing to tight/normal is ±0.1-0.2, visible on small text). Largest delta: .maka-bubble-assistant 1.68 → 1.5 (chat core, magic-number fix).
  • font-weight: 8 distinct (incl. mid-axis 550/620/650/680 from variable Geist) → 4 tokens. Snap: 550/620/650 → semibold (600), 680 → bold (700).
  • letter-spacing: 14 distinct (incl. CJK mis-tightening -0.018em) → 4 tokens. All negatives snap to --tracking-normal (0) per CJK ban. ALL-CAPS labels use --tracking-wider/widest (roadmap §4.1). 1px--tracking-widest (0.1em ≈ 1.3px @ 13px).
  • font-variation-settings: dropped .maka-hero h1's "wght" 550, "wdth" 105 — wght folds into font-weight token, wdth:105 one-off dropped (hero stays distinct via size + weight + leading alone).
  • font-family minor: var(--mono-font, monospace)var(--font-mono) (was a typo'd token name); redundant var(--font-mono, ui-monospace, …) fallbacks → var(--font-mono); Georgia, serifvar(--font-serif) (tokenized).

Contracts

Two new converge-contract tests, mirroring typography-converge-contract.test.ts:

  • font-weight-converge-contract.test.ts — bans bare font-weight numbers + normal/bold/lighter/bolder + bare weight in font: shorthand; whitelists 4 --font-weight-* tokens + inherit/initial/unset/revert; pins values; asserts @theme inline bridging.
  • letter-spacing-converge-contract.test.ts — bans bare letter-spacing em/px/normal/negative; whitelists 4 --tracking-* tokens + 0 + literals; pins values; asserts bridging.

(line-height-converge-contract.test.ts landed in the first commit pair of this PR.)

Verification

  • line-height-converge-contract: 9/9 pass
  • font-weight-converge-contract: 8/8 pass
  • letter-spacing-converge-contract: 7/7 pass
  • Full desktop suite: 1910/1910 pass (no regressions; +15 from the two new contracts)
  • renderer build: success (Tailwind v4 @theme inline same-name --leading-* / --font-weight-* / --tracking-* bridging verified)
  • Dual-theme screenshots (turn-narrative + first-run, light + dark): visual deltas are barely-perceptible as expected — .maka-hero h1 is the most visible (wdth 105 drop + 550→600 + tracking -0.018→0); confirmed acceptable.

Commits

  1. refactor(ui): converge line-height onto --leading-* tokens — 25 css, 4 tokens + bridge + ~160 replacements
  2. test(ui): lock line-height converge contract — 9 tests
  3. refactor(ui): converge font-weight + letter-spacing + font-family minor — 28 css, 9 tokens + bridge + ~267 replacements + font-variation fold + font-family cleanup
  4. test(ui): lock font-weight + letter-spacing converge contracts — 15 tests

Out of scope (next PRs in #520)

PR2 visual-state & motion tokens (opacity / focus-ring / motion duration+amplitude), PR3 anti-layout-shift, PR4 layout surface & sizing, PR5 Base UI small migrations + style-hook, PR6 Base UI large migrations.

Tracking issue: #520.

Add 4 --leading-* tokens (none/tight/snug/normal = 1/1.25/1.375/1.5) aligned to a Tailwind leading-scale subset (drop relaxed/loose, zero sites). Bridge --leading-* to Tailwind leading-* utilities in @theme inline. Replace ~160 bare line-height values across renderer CSS with var(--leading-*), incl. the 20px magic (plan-reminders.css) and 1.68 bubble-assistant. Snug is the one tier that can't be cut: 40 sites at 1.3-1.4 (13/11px small-text dense zones) collapse to tight/normal at ±0.1-0.2, visible on small text. Tracking issue: #520.
Add line-height-converge-contract.test.ts mirroring typography-converge-contract: ban bare line-height numbers/px/rem/normal, whitelist 4 --leading-* tokens + em + inherit/initial/unset/revert/0; pin token values; assert Tailwind @theme inline bridging. Tracking issue: #520.
…or (#520 PR1)
Add --font-weight-* (4-tier: normal/medium/semibold/bold = 400/500/600/700) + --tracking-* (4-tier: normal/wide/wider/widest = 0/0.025em/0.05em/0.1em; no tight — CJK-first, roadmap §1.6 bans tightening CJK) + --font-serif tokens; bridge to Tailwind font-*/tracking-* utilities in @theme inline. Replace ~205 font-weight (550/620/650→semibold, 680→bold) + ~62 letter-spacing (negatives→normal) values across renderer CSS. Drop font-variation-settings "wght" 550 "wdth" 105 on .maka-hero h1 (wght folds into font-weight token, wdth one-off dropped). Fix font-family: var(--mono-font,…)→var(--font-mono), redundant fallbacks→var(--font-mono), Georgia,serif→var(--font-serif). Tracking issue: #520.
…PR1)
Add font-weight-converge-contract.test.ts + letter-spacing-converge-contract.test.ts mirroring line-height-converge-contract: ban bare font-weight numbers/normal/bold + bare letter-spacing em/px/normal/negative; whitelist --font-weight-*/--tracking-* tokens + literals; pin token values; assert @theme inline bridging. Tracking issue: #520.
@Astro-Han
Astro-Hanforce-pushed the refactor/typography-converge branch from dfb801b to e772d93CompareJuly 4, 2026 17:54
P2: readAllRendererCss now fails closed — removed the catch that silently degraded to reading only styles.css when @import expansion broke, so a missing/bad import surfaces instead of skipping styles/* (exported expandCssImports for direct testing).
P2: ban non-literal font: shorthand via shared findFontShorthandOffenders — covers font-weight + line-height bypass vectors (font: 600 var(--font-size-ui) …, font: var(--font-size-ui)/1.4 …). Renderer CSS only uses font: inherit today, so the whitelist is literals-only (inherit/initial/unset/revert); no regex arms race over which shorthand component is bare. Value is extracted and checked against the literal set rather than using a negative lookahead (which backtracks \s* and matches font: inherit as an offender).
P2: pin regex now ends with \s*; so drifted values fail — --font-weight-normal: 4000; no longer prefix-matches 400, --tracking-normal: 0.02em; no longer prefix-matches 0, --leading-normal: 1.55; no longer prefix-matches 1.5.
P3: extracted shared font: shorthand scanner into css-test-helpers (partial de-dup of the three converge contracts; full table-driven merge left out to keep per-property invariant docs readable).
New css-test-helpers.test.ts: bad @import throws with the missing path, font: shorthand literal vs non-literal, comment stripping.
Tracking issue: #520.
P2: replace scattered assert.match pin regex with shared assertTokenPinnedOnce. assert.match only proved a correct declaration existed somewhere — a later overriding declaration (--font-weight-normal: 400; --font-weight-normal: 450;) still passed because the first match satisfied it, so a drifted override would slip through. assertTokenPinnedOnce parses maka-tokens.css custom property declarations and asserts each typography token is declared exactly once with the exact value (fails on duplicates and on a single drifted value).
New css-test-helpers exports: parseCssCustomProps, assertTokenPinnedOnce.
css-test-helpers.test.ts: assertTokenPinnedOnce unit tests (duplicate override, single drift, missing token, comment strip).
Three converge contracts: pin tests use assertTokenPinnedOnce; dropped the now-redundant pin regex negative tests (they tested the old regex mechanism, now superseded).
Reviewer negative cases locked: --font-weight-normal: 400; --font-weight-normal: 450; / --leading-normal: 1.5; --leading-normal: 1.55; / --tracking-normal: 0; --tracking-normal: 0.02em; all fail with 'exactly once'.
Tracking issue: #520.
P2: the styles.css @theme inline bridge aliases (--leading-*/--font-weight-*/--tracking-* → var(--*)) were still checked with assert.match, so a duplicate overriding declaration (--leading-normal: var(--leading-normal); --leading-normal: 1.55;) would pass because the first match satisfied assert.match. Reused the custom property parser: bridge tests now use assertCustomPropPinnedOnce (exactly-once + exact value), same as token pin tests.
Renamed assertTokenPinnedOnce → assertCustomPropPinnedOnce (the helper was never token-specific — it parses any custom property; the new name fits both maka-tokens.css definitions and styles.css bridge aliases).
Reviewer negative cases locked: --font-weight-normal: var(--font-weight-normal); --font-weight-normal: 450; / --leading-normal: var(--leading-normal); --leading-normal: 1.55; / --tracking-normal: var(--tracking-normal); --tracking-normal: 0.02em; all fail with 'exactly once' (css-test-helpers.test.ts).
Tracking issue: #520.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) by Astro-Han · Pull Request #526 · apache/maka · GitHub
Skip to content

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) - #526

Merged
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge
Jul 4, 2026
Merged

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1)#526
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

First PR of #520 (design-spec governance tracking issue). Converges the three ungoverned typography dimensions — line-height, font-weight, letter-spacing — onto tokens aligned to a Tailwind scale subset, plus folds font-variation-settings into font-weight and cleans up font-family minor drift. Same converge-contract pattern as #430 PR2 / #448.

Tokens added (maka-tokens.css)

TokenValueAligned toSites
--leading-none/tight/snug/normal1 / 1.25 / 1.375 / 1.5Tailwind leading-* (drop relaxed/loose, 0 sites)~160
--font-weight-normal/medium/semibold/bold400 / 500 / 600 / 700Tailwind font-* (drop thin/extralight/light/extrabold/black)~205
--tracking-normal/wide/wider/widest0 / 0.025em / 0.05em / 0.1emTailwind tracking-* (no tight — CJK-first, roadmap §1.6 bans tightening CJK)~62
--font-serifGeorgia, "Times New Roman", Times, serif(onboarding hero)1

Bridged to Tailwind utilities via @theme inline in styles.css (same inline-bridge pattern as --text-* / --space-*), so var(--leading-*) in CSS and leading-* in TSX share one source.

Convergence

  • line-height: 15 distinct bare values + one 20px magic → 4 tokens. Snug is the one tier that can't be cut (40 sites at 1.3-1.4 are 13/11px small-text dense zones; collapsing to tight/normal is ±0.1-0.2, visible on small text). Largest delta: .maka-bubble-assistant 1.68 → 1.5 (chat core, magic-number fix).
  • font-weight: 8 distinct (incl. mid-axis 550/620/650/680 from variable Geist) → 4 tokens. Snap: 550/620/650 → semibold (600), 680 → bold (700).
  • letter-spacing: 14 distinct (incl. CJK mis-tightening -0.018em) → 4 tokens. All negatives snap to --tracking-normal (0) per CJK ban. ALL-CAPS labels use --tracking-wider/widest (roadmap §4.1). 1px--tracking-widest (0.1em ≈ 1.3px @ 13px).
  • font-variation-settings: dropped .maka-hero h1's "wght" 550, "wdth" 105 — wght folds into font-weight token, wdth:105 one-off dropped (hero stays distinct via size + weight + leading alone).
  • font-family minor: var(--mono-font, monospace)var(--font-mono) (was a typo'd token name); redundant var(--font-mono, ui-monospace, …) fallbacks → var(--font-mono); Georgia, serifvar(--font-serif) (tokenized).

Contracts

Two new converge-contract tests, mirroring typography-converge-contract.test.ts:

  • font-weight-converge-contract.test.ts — bans bare font-weight numbers + normal/bold/lighter/bolder + bare weight in font: shorthand; whitelists 4 --font-weight-* tokens + inherit/initial/unset/revert; pins values; asserts @theme inline bridging.
  • letter-spacing-converge-contract.test.ts — bans bare letter-spacing em/px/normal/negative; whitelists 4 --tracking-* tokens + 0 + literals; pins values; asserts bridging.

(line-height-converge-contract.test.ts landed in the first commit pair of this PR.)

Verification

  • line-height-converge-contract: 9/9 pass
  • font-weight-converge-contract: 8/8 pass
  • letter-spacing-converge-contract: 7/7 pass
  • Full desktop suite: 1910/1910 pass (no regressions; +15 from the two new contracts)
  • renderer build: success (Tailwind v4 @theme inline same-name --leading-* / --font-weight-* / --tracking-* bridging verified)
  • Dual-theme screenshots (turn-narrative + first-run, light + dark): visual deltas are barely-perceptible as expected — .maka-hero h1 is the most visible (wdth 105 drop + 550→600 + tracking -0.018→0); confirmed acceptable.

Commits

  1. refactor(ui): converge line-height onto --leading-* tokens — 25 css, 4 tokens + bridge + ~160 replacements
  2. test(ui): lock line-height converge contract — 9 tests
  3. refactor(ui): converge font-weight + letter-spacing + font-family minor — 28 css, 9 tokens + bridge + ~267 replacements + font-variation fold + font-family cleanup
  4. test(ui): lock font-weight + letter-spacing converge contracts — 15 tests

Out of scope (next PRs in #520)

PR2 visual-state & motion tokens (opacity / focus-ring / motion duration+amplitude), PR3 anti-layout-shift, PR4 layout surface & sizing, PR5 Base UI small migrations + style-hook, PR6 Base UI large migrations.

Tracking issue: #520.

Add 4 --leading-* tokens (none/tight/snug/normal = 1/1.25/1.375/1.5) aligned to a Tailwind leading-scale subset (drop relaxed/loose, zero sites). Bridge --leading-* to Tailwind leading-* utilities in @theme inline. Replace ~160 bare line-height values across renderer CSS with var(--leading-*), incl. the 20px magic (plan-reminders.css) and 1.68 bubble-assistant. Snug is the one tier that can't be cut: 40 sites at 1.3-1.4 (13/11px small-text dense zones) collapse to tight/normal at ±0.1-0.2, visible on small text. Tracking issue: #520.
Add line-height-converge-contract.test.ts mirroring typography-converge-contract: ban bare line-height numbers/px/rem/normal, whitelist 4 --leading-* tokens + em + inherit/initial/unset/revert/0; pin token values; assert Tailwind @theme inline bridging. Tracking issue: #520.
…or (#520 PR1)
Add --font-weight-* (4-tier: normal/medium/semibold/bold = 400/500/600/700) + --tracking-* (4-tier: normal/wide/wider/widest = 0/0.025em/0.05em/0.1em; no tight — CJK-first, roadmap §1.6 bans tightening CJK) + --font-serif tokens; bridge to Tailwind font-*/tracking-* utilities in @theme inline. Replace ~205 font-weight (550/620/650→semibold, 680→bold) + ~62 letter-spacing (negatives→normal) values across renderer CSS. Drop font-variation-settings "wght" 550 "wdth" 105 on .maka-hero h1 (wght folds into font-weight token, wdth one-off dropped). Fix font-family: var(--mono-font,…)→var(--font-mono), redundant fallbacks→var(--font-mono), Georgia,serif→var(--font-serif). Tracking issue: #520.
…PR1)
Add font-weight-converge-contract.test.ts + letter-spacing-converge-contract.test.ts mirroring line-height-converge-contract: ban bare font-weight numbers/normal/bold + bare letter-spacing em/px/normal/negative; whitelist --font-weight-*/--tracking-* tokens + literals; pin token values; assert @theme inline bridging. Tracking issue: #520.
@Astro-Han
Astro-Hanforce-pushed the refactor/typography-converge branch from dfb801b to e772d93CompareJuly 4, 2026 17:54
P2: readAllRendererCss now fails closed — removed the catch that silently degraded to reading only styles.css when @import expansion broke, so a missing/bad import surfaces instead of skipping styles/* (exported expandCssImports for direct testing).
P2: ban non-literal font: shorthand via shared findFontShorthandOffenders — covers font-weight + line-height bypass vectors (font: 600 var(--font-size-ui) …, font: var(--font-size-ui)/1.4 …). Renderer CSS only uses font: inherit today, so the whitelist is literals-only (inherit/initial/unset/revert); no regex arms race over which shorthand component is bare. Value is extracted and checked against the literal set rather than using a negative lookahead (which backtracks \s* and matches font: inherit as an offender).
P2: pin regex now ends with \s*; so drifted values fail — --font-weight-normal: 4000; no longer prefix-matches 400, --tracking-normal: 0.02em; no longer prefix-matches 0, --leading-normal: 1.55; no longer prefix-matches 1.5.
P3: extracted shared font: shorthand scanner into css-test-helpers (partial de-dup of the three converge contracts; full table-driven merge left out to keep per-property invariant docs readable).
New css-test-helpers.test.ts: bad @import throws with the missing path, font: shorthand literal vs non-literal, comment stripping.
Tracking issue: #520.
P2: replace scattered assert.match pin regex with shared assertTokenPinnedOnce. assert.match only proved a correct declaration existed somewhere — a later overriding declaration (--font-weight-normal: 400; --font-weight-normal: 450;) still passed because the first match satisfied it, so a drifted override would slip through. assertTokenPinnedOnce parses maka-tokens.css custom property declarations and asserts each typography token is declared exactly once with the exact value (fails on duplicates and on a single drifted value).
New css-test-helpers exports: parseCssCustomProps, assertTokenPinnedOnce.
css-test-helpers.test.ts: assertTokenPinnedOnce unit tests (duplicate override, single drift, missing token, comment strip).
Three converge contracts: pin tests use assertTokenPinnedOnce; dropped the now-redundant pin regex negative tests (they tested the old regex mechanism, now superseded).
Reviewer negative cases locked: --font-weight-normal: 400; --font-weight-normal: 450; / --leading-normal: 1.5; --leading-normal: 1.55; / --tracking-normal: 0; --tracking-normal: 0.02em; all fail with 'exactly once'.
Tracking issue: #520.
P2: the styles.css @theme inline bridge aliases (--leading-*/--font-weight-*/--tracking-* → var(--*)) were still checked with assert.match, so a duplicate overriding declaration (--leading-normal: var(--leading-normal); --leading-normal: 1.55;) would pass because the first match satisfied assert.match. Reused the custom property parser: bridge tests now use assertCustomPropPinnedOnce (exactly-once + exact value), same as token pin tests.
Renamed assertTokenPinnedOnce → assertCustomPropPinnedOnce (the helper was never token-specific — it parses any custom property; the new name fits both maka-tokens.css definitions and styles.css bridge aliases).
Reviewer negative cases locked: --font-weight-normal: var(--font-weight-normal); --font-weight-normal: 450; / --leading-normal: var(--leading-normal); --leading-normal: 1.55; / --tracking-normal: var(--tracking-normal); --tracking-normal: 0.02em; all fail with 'exactly once' (css-test-helpers.test.ts).
Tracking issue: #520.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) by Astro-Han · Pull Request #526 · apache/maka · GitHub
Skip to content

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) - #526

Merged
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge
Jul 4, 2026
Merged

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1)#526
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

First PR of #520 (design-spec governance tracking issue). Converges the three ungoverned typography dimensions — line-height, font-weight, letter-spacing — onto tokens aligned to a Tailwind scale subset, plus folds font-variation-settings into font-weight and cleans up font-family minor drift. Same converge-contract pattern as #430 PR2 / #448.

Tokens added (maka-tokens.css)

TokenValueAligned toSites
--leading-none/tight/snug/normal1 / 1.25 / 1.375 / 1.5Tailwind leading-* (drop relaxed/loose, 0 sites)~160
--font-weight-normal/medium/semibold/bold400 / 500 / 600 / 700Tailwind font-* (drop thin/extralight/light/extrabold/black)~205
--tracking-normal/wide/wider/widest0 / 0.025em / 0.05em / 0.1emTailwind tracking-* (no tight — CJK-first, roadmap §1.6 bans tightening CJK)~62
--font-serifGeorgia, "Times New Roman", Times, serif(onboarding hero)1

Bridged to Tailwind utilities via @theme inline in styles.css (same inline-bridge pattern as --text-* / --space-*), so var(--leading-*) in CSS and leading-* in TSX share one source.

Convergence

  • line-height: 15 distinct bare values + one 20px magic → 4 tokens. Snug is the one tier that can't be cut (40 sites at 1.3-1.4 are 13/11px small-text dense zones; collapsing to tight/normal is ±0.1-0.2, visible on small text). Largest delta: .maka-bubble-assistant 1.68 → 1.5 (chat core, magic-number fix).
  • font-weight: 8 distinct (incl. mid-axis 550/620/650/680 from variable Geist) → 4 tokens. Snap: 550/620/650 → semibold (600), 680 → bold (700).
  • letter-spacing: 14 distinct (incl. CJK mis-tightening -0.018em) → 4 tokens. All negatives snap to --tracking-normal (0) per CJK ban. ALL-CAPS labels use --tracking-wider/widest (roadmap §4.1). 1px--tracking-widest (0.1em ≈ 1.3px @ 13px).
  • font-variation-settings: dropped .maka-hero h1's "wght" 550, "wdth" 105 — wght folds into font-weight token, wdth:105 one-off dropped (hero stays distinct via size + weight + leading alone).
  • font-family minor: var(--mono-font, monospace)var(--font-mono) (was a typo'd token name); redundant var(--font-mono, ui-monospace, …) fallbacks → var(--font-mono); Georgia, serifvar(--font-serif) (tokenized).

Contracts

Two new converge-contract tests, mirroring typography-converge-contract.test.ts:

  • font-weight-converge-contract.test.ts — bans bare font-weight numbers + normal/bold/lighter/bolder + bare weight in font: shorthand; whitelists 4 --font-weight-* tokens + inherit/initial/unset/revert; pins values; asserts @theme inline bridging.
  • letter-spacing-converge-contract.test.ts — bans bare letter-spacing em/px/normal/negative; whitelists 4 --tracking-* tokens + 0 + literals; pins values; asserts bridging.

(line-height-converge-contract.test.ts landed in the first commit pair of this PR.)

Verification

  • line-height-converge-contract: 9/9 pass
  • font-weight-converge-contract: 8/8 pass
  • letter-spacing-converge-contract: 7/7 pass
  • Full desktop suite: 1910/1910 pass (no regressions; +15 from the two new contracts)
  • renderer build: success (Tailwind v4 @theme inline same-name --leading-* / --font-weight-* / --tracking-* bridging verified)
  • Dual-theme screenshots (turn-narrative + first-run, light + dark): visual deltas are barely-perceptible as expected — .maka-hero h1 is the most visible (wdth 105 drop + 550→600 + tracking -0.018→0); confirmed acceptable.

Commits

  1. refactor(ui): converge line-height onto --leading-* tokens — 25 css, 4 tokens + bridge + ~160 replacements
  2. test(ui): lock line-height converge contract — 9 tests
  3. refactor(ui): converge font-weight + letter-spacing + font-family minor — 28 css, 9 tokens + bridge + ~267 replacements + font-variation fold + font-family cleanup
  4. test(ui): lock font-weight + letter-spacing converge contracts — 15 tests

Out of scope (next PRs in #520)

PR2 visual-state & motion tokens (opacity / focus-ring / motion duration+amplitude), PR3 anti-layout-shift, PR4 layout surface & sizing, PR5 Base UI small migrations + style-hook, PR6 Base UI large migrations.

Tracking issue: #520.

Add 4 --leading-* tokens (none/tight/snug/normal = 1/1.25/1.375/1.5) aligned to a Tailwind leading-scale subset (drop relaxed/loose, zero sites). Bridge --leading-* to Tailwind leading-* utilities in @theme inline. Replace ~160 bare line-height values across renderer CSS with var(--leading-*), incl. the 20px magic (plan-reminders.css) and 1.68 bubble-assistant. Snug is the one tier that can't be cut: 40 sites at 1.3-1.4 (13/11px small-text dense zones) collapse to tight/normal at ±0.1-0.2, visible on small text. Tracking issue: #520.
Add line-height-converge-contract.test.ts mirroring typography-converge-contract: ban bare line-height numbers/px/rem/normal, whitelist 4 --leading-* tokens + em + inherit/initial/unset/revert/0; pin token values; assert Tailwind @theme inline bridging. Tracking issue: #520.
…or (#520 PR1)
Add --font-weight-* (4-tier: normal/medium/semibold/bold = 400/500/600/700) + --tracking-* (4-tier: normal/wide/wider/widest = 0/0.025em/0.05em/0.1em; no tight — CJK-first, roadmap §1.6 bans tightening CJK) + --font-serif tokens; bridge to Tailwind font-*/tracking-* utilities in @theme inline. Replace ~205 font-weight (550/620/650→semibold, 680→bold) + ~62 letter-spacing (negatives→normal) values across renderer CSS. Drop font-variation-settings "wght" 550 "wdth" 105 on .maka-hero h1 (wght folds into font-weight token, wdth one-off dropped). Fix font-family: var(--mono-font,…)→var(--font-mono), redundant fallbacks→var(--font-mono), Georgia,serif→var(--font-serif). Tracking issue: #520.
…PR1)
Add font-weight-converge-contract.test.ts + letter-spacing-converge-contract.test.ts mirroring line-height-converge-contract: ban bare font-weight numbers/normal/bold + bare letter-spacing em/px/normal/negative; whitelist --font-weight-*/--tracking-* tokens + literals; pin token values; assert @theme inline bridging. Tracking issue: #520.
@Astro-Han
Astro-Hanforce-pushed the refactor/typography-converge branch from dfb801b to e772d93CompareJuly 4, 2026 17:54
P2: readAllRendererCss now fails closed — removed the catch that silently degraded to reading only styles.css when @import expansion broke, so a missing/bad import surfaces instead of skipping styles/* (exported expandCssImports for direct testing).
P2: ban non-literal font: shorthand via shared findFontShorthandOffenders — covers font-weight + line-height bypass vectors (font: 600 var(--font-size-ui) …, font: var(--font-size-ui)/1.4 …). Renderer CSS only uses font: inherit today, so the whitelist is literals-only (inherit/initial/unset/revert); no regex arms race over which shorthand component is bare. Value is extracted and checked against the literal set rather than using a negative lookahead (which backtracks \s* and matches font: inherit as an offender).
P2: pin regex now ends with \s*; so drifted values fail — --font-weight-normal: 4000; no longer prefix-matches 400, --tracking-normal: 0.02em; no longer prefix-matches 0, --leading-normal: 1.55; no longer prefix-matches 1.5.
P3: extracted shared font: shorthand scanner into css-test-helpers (partial de-dup of the three converge contracts; full table-driven merge left out to keep per-property invariant docs readable).
New css-test-helpers.test.ts: bad @import throws with the missing path, font: shorthand literal vs non-literal, comment stripping.
Tracking issue: #520.
P2: replace scattered assert.match pin regex with shared assertTokenPinnedOnce. assert.match only proved a correct declaration existed somewhere — a later overriding declaration (--font-weight-normal: 400; --font-weight-normal: 450;) still passed because the first match satisfied it, so a drifted override would slip through. assertTokenPinnedOnce parses maka-tokens.css custom property declarations and asserts each typography token is declared exactly once with the exact value (fails on duplicates and on a single drifted value).
New css-test-helpers exports: parseCssCustomProps, assertTokenPinnedOnce.
css-test-helpers.test.ts: assertTokenPinnedOnce unit tests (duplicate override, single drift, missing token, comment strip).
Three converge contracts: pin tests use assertTokenPinnedOnce; dropped the now-redundant pin regex negative tests (they tested the old regex mechanism, now superseded).
Reviewer negative cases locked: --font-weight-normal: 400; --font-weight-normal: 450; / --leading-normal: 1.5; --leading-normal: 1.55; / --tracking-normal: 0; --tracking-normal: 0.02em; all fail with 'exactly once'.
Tracking issue: #520.
P2: the styles.css @theme inline bridge aliases (--leading-*/--font-weight-*/--tracking-* → var(--*)) were still checked with assert.match, so a duplicate overriding declaration (--leading-normal: var(--leading-normal); --leading-normal: 1.55;) would pass because the first match satisfied assert.match. Reused the custom property parser: bridge tests now use assertCustomPropPinnedOnce (exactly-once + exact value), same as token pin tests.
Renamed assertTokenPinnedOnce → assertCustomPropPinnedOnce (the helper was never token-specific — it parses any custom property; the new name fits both maka-tokens.css definitions and styles.css bridge aliases).
Reviewer negative cases locked: --font-weight-normal: var(--font-weight-normal); --font-weight-normal: 450; / --leading-normal: var(--leading-normal); --leading-normal: 1.55; / --tracking-normal: var(--tracking-normal); --tracking-normal: 0.02em; all fail with 'exactly once' (css-test-helpers.test.ts).
Tracking issue: #520.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) by Astro-Han · Pull Request #526 · apache/maka · GitHub
Skip to content

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) - #526

Merged
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge
Jul 4, 2026
Merged

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1)#526
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

First PR of #520 (design-spec governance tracking issue). Converges the three ungoverned typography dimensions — line-height, font-weight, letter-spacing — onto tokens aligned to a Tailwind scale subset, plus folds font-variation-settings into font-weight and cleans up font-family minor drift. Same converge-contract pattern as #430 PR2 / #448.

Tokens added (maka-tokens.css)

TokenValueAligned toSites
--leading-none/tight/snug/normal1 / 1.25 / 1.375 / 1.5Tailwind leading-* (drop relaxed/loose, 0 sites)~160
--font-weight-normal/medium/semibold/bold400 / 500 / 600 / 700Tailwind font-* (drop thin/extralight/light/extrabold/black)~205
--tracking-normal/wide/wider/widest0 / 0.025em / 0.05em / 0.1emTailwind tracking-* (no tight — CJK-first, roadmap §1.6 bans tightening CJK)~62
--font-serifGeorgia, "Times New Roman", Times, serif(onboarding hero)1

Bridged to Tailwind utilities via @theme inline in styles.css (same inline-bridge pattern as --text-* / --space-*), so var(--leading-*) in CSS and leading-* in TSX share one source.

Convergence

  • line-height: 15 distinct bare values + one 20px magic → 4 tokens. Snug is the one tier that can't be cut (40 sites at 1.3-1.4 are 13/11px small-text dense zones; collapsing to tight/normal is ±0.1-0.2, visible on small text). Largest delta: .maka-bubble-assistant 1.68 → 1.5 (chat core, magic-number fix).
  • font-weight: 8 distinct (incl. mid-axis 550/620/650/680 from variable Geist) → 4 tokens. Snap: 550/620/650 → semibold (600), 680 → bold (700).
  • letter-spacing: 14 distinct (incl. CJK mis-tightening -0.018em) → 4 tokens. All negatives snap to --tracking-normal (0) per CJK ban. ALL-CAPS labels use --tracking-wider/widest (roadmap §4.1). 1px--tracking-widest (0.1em ≈ 1.3px @ 13px).
  • font-variation-settings: dropped .maka-hero h1's "wght" 550, "wdth" 105 — wght folds into font-weight token, wdth:105 one-off dropped (hero stays distinct via size + weight + leading alone).
  • font-family minor: var(--mono-font, monospace)var(--font-mono) (was a typo'd token name); redundant var(--font-mono, ui-monospace, …) fallbacks → var(--font-mono); Georgia, serifvar(--font-serif) (tokenized).

Contracts

Two new converge-contract tests, mirroring typography-converge-contract.test.ts:

  • font-weight-converge-contract.test.ts — bans bare font-weight numbers + normal/bold/lighter/bolder + bare weight in font: shorthand; whitelists 4 --font-weight-* tokens + inherit/initial/unset/revert; pins values; asserts @theme inline bridging.
  • letter-spacing-converge-contract.test.ts — bans bare letter-spacing em/px/normal/negative; whitelists 4 --tracking-* tokens + 0 + literals; pins values; asserts bridging.

(line-height-converge-contract.test.ts landed in the first commit pair of this PR.)

Verification

  • line-height-converge-contract: 9/9 pass
  • font-weight-converge-contract: 8/8 pass
  • letter-spacing-converge-contract: 7/7 pass
  • Full desktop suite: 1910/1910 pass (no regressions; +15 from the two new contracts)
  • renderer build: success (Tailwind v4 @theme inline same-name --leading-* / --font-weight-* / --tracking-* bridging verified)
  • Dual-theme screenshots (turn-narrative + first-run, light + dark): visual deltas are barely-perceptible as expected — .maka-hero h1 is the most visible (wdth 105 drop + 550→600 + tracking -0.018→0); confirmed acceptable.

Commits

  1. refactor(ui): converge line-height onto --leading-* tokens — 25 css, 4 tokens + bridge + ~160 replacements
  2. test(ui): lock line-height converge contract — 9 tests
  3. refactor(ui): converge font-weight + letter-spacing + font-family minor — 28 css, 9 tokens + bridge + ~267 replacements + font-variation fold + font-family cleanup
  4. test(ui): lock font-weight + letter-spacing converge contracts — 15 tests

Out of scope (next PRs in #520)

PR2 visual-state & motion tokens (opacity / focus-ring / motion duration+amplitude), PR3 anti-layout-shift, PR4 layout surface & sizing, PR5 Base UI small migrations + style-hook, PR6 Base UI large migrations.

Tracking issue: #520.

Add 4 --leading-* tokens (none/tight/snug/normal = 1/1.25/1.375/1.5) aligned to a Tailwind leading-scale subset (drop relaxed/loose, zero sites). Bridge --leading-* to Tailwind leading-* utilities in @theme inline. Replace ~160 bare line-height values across renderer CSS with var(--leading-*), incl. the 20px magic (plan-reminders.css) and 1.68 bubble-assistant. Snug is the one tier that can't be cut: 40 sites at 1.3-1.4 (13/11px small-text dense zones) collapse to tight/normal at ±0.1-0.2, visible on small text. Tracking issue: #520.
Add line-height-converge-contract.test.ts mirroring typography-converge-contract: ban bare line-height numbers/px/rem/normal, whitelist 4 --leading-* tokens + em + inherit/initial/unset/revert/0; pin token values; assert Tailwind @theme inline bridging. Tracking issue: #520.
…or (#520 PR1)
Add --font-weight-* (4-tier: normal/medium/semibold/bold = 400/500/600/700) + --tracking-* (4-tier: normal/wide/wider/widest = 0/0.025em/0.05em/0.1em; no tight — CJK-first, roadmap §1.6 bans tightening CJK) + --font-serif tokens; bridge to Tailwind font-*/tracking-* utilities in @theme inline. Replace ~205 font-weight (550/620/650→semibold, 680→bold) + ~62 letter-spacing (negatives→normal) values across renderer CSS. Drop font-variation-settings "wght" 550 "wdth" 105 on .maka-hero h1 (wght folds into font-weight token, wdth one-off dropped). Fix font-family: var(--mono-font,…)→var(--font-mono), redundant fallbacks→var(--font-mono), Georgia,serif→var(--font-serif). Tracking issue: #520.
…PR1)
Add font-weight-converge-contract.test.ts + letter-spacing-converge-contract.test.ts mirroring line-height-converge-contract: ban bare font-weight numbers/normal/bold + bare letter-spacing em/px/normal/negative; whitelist --font-weight-*/--tracking-* tokens + literals; pin token values; assert @theme inline bridging. Tracking issue: #520.
@Astro-Han
Astro-Hanforce-pushed the refactor/typography-converge branch from dfb801b to e772d93CompareJuly 4, 2026 17:54
P2: readAllRendererCss now fails closed — removed the catch that silently degraded to reading only styles.css when @import expansion broke, so a missing/bad import surfaces instead of skipping styles/* (exported expandCssImports for direct testing).
P2: ban non-literal font: shorthand via shared findFontShorthandOffenders — covers font-weight + line-height bypass vectors (font: 600 var(--font-size-ui) …, font: var(--font-size-ui)/1.4 …). Renderer CSS only uses font: inherit today, so the whitelist is literals-only (inherit/initial/unset/revert); no regex arms race over which shorthand component is bare. Value is extracted and checked against the literal set rather than using a negative lookahead (which backtracks \s* and matches font: inherit as an offender).
P2: pin regex now ends with \s*; so drifted values fail — --font-weight-normal: 4000; no longer prefix-matches 400, --tracking-normal: 0.02em; no longer prefix-matches 0, --leading-normal: 1.55; no longer prefix-matches 1.5.
P3: extracted shared font: shorthand scanner into css-test-helpers (partial de-dup of the three converge contracts; full table-driven merge left out to keep per-property invariant docs readable).
New css-test-helpers.test.ts: bad @import throws with the missing path, font: shorthand literal vs non-literal, comment stripping.
Tracking issue: #520.
P2: replace scattered assert.match pin regex with shared assertTokenPinnedOnce. assert.match only proved a correct declaration existed somewhere — a later overriding declaration (--font-weight-normal: 400; --font-weight-normal: 450;) still passed because the first match satisfied it, so a drifted override would slip through. assertTokenPinnedOnce parses maka-tokens.css custom property declarations and asserts each typography token is declared exactly once with the exact value (fails on duplicates and on a single drifted value).
New css-test-helpers exports: parseCssCustomProps, assertTokenPinnedOnce.
css-test-helpers.test.ts: assertTokenPinnedOnce unit tests (duplicate override, single drift, missing token, comment strip).
Three converge contracts: pin tests use assertTokenPinnedOnce; dropped the now-redundant pin regex negative tests (they tested the old regex mechanism, now superseded).
Reviewer negative cases locked: --font-weight-normal: 400; --font-weight-normal: 450; / --leading-normal: 1.5; --leading-normal: 1.55; / --tracking-normal: 0; --tracking-normal: 0.02em; all fail with 'exactly once'.
Tracking issue: #520.
P2: the styles.css @theme inline bridge aliases (--leading-*/--font-weight-*/--tracking-* → var(--*)) were still checked with assert.match, so a duplicate overriding declaration (--leading-normal: var(--leading-normal); --leading-normal: 1.55;) would pass because the first match satisfied assert.match. Reused the custom property parser: bridge tests now use assertCustomPropPinnedOnce (exactly-once + exact value), same as token pin tests.
Renamed assertTokenPinnedOnce → assertCustomPropPinnedOnce (the helper was never token-specific — it parses any custom property; the new name fits both maka-tokens.css definitions and styles.css bridge aliases).
Reviewer negative cases locked: --font-weight-normal: var(--font-weight-normal); --font-weight-normal: 450; / --leading-normal: var(--leading-normal); --leading-normal: 1.55; / --tracking-normal: var(--tracking-normal); --tracking-normal: 0.02em; all fail with 'exactly once' (css-test-helpers.test.ts).
Tracking issue: #520.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) by Astro-Han · Pull Request #526 · apache/maka · GitHub
Skip to content

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1) - #526

Merged
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge
Jul 4, 2026
Merged

refactor(ui): converge typography onto --leading-*/--font-weight-*/--tracking-* tokens (#520 PR1)#526
Astro-Han merged 7 commits into
mainfrom
refactor/typography-converge

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

First PR of #520 (design-spec governance tracking issue). Converges the three ungoverned typography dimensions — line-height, font-weight, letter-spacing — onto tokens aligned to a Tailwind scale subset, plus folds font-variation-settings into font-weight and cleans up font-family minor drift. Same converge-contract pattern as #430 PR2 / #448.

Tokens added (maka-tokens.css)

TokenValueAligned toSites
--leading-none/tight/snug/normal1 / 1.25 / 1.375 / 1.5Tailwind leading-* (drop relaxed/loose, 0 sites)~160
--font-weight-normal/medium/semibold/bold400 / 500 / 600 / 700Tailwind font-* (drop thin/extralight/light/extrabold/black)~205
--tracking-normal/wide/wider/widest0 / 0.025em / 0.05em / 0.1emTailwind tracking-* (no tight — CJK-first, roadmap §1.6 bans tightening CJK)~62
--font-serifGeorgia, "Times New Roman", Times, serif(onboarding hero)1

Bridged to Tailwind utilities via @theme inline in styles.css (same inline-bridge pattern as --text-* / --space-*), so var(--leading-*) in CSS and leading-* in TSX share one source.

Convergence

  • line-height: 15 distinct bare values + one 20px magic → 4 tokens. Snug is the one tier that can't be cut (40 sites at 1.3-1.4 are 13/11px small-text dense zones; collapsing to tight/normal is ±0.1-0.2, visible on small text). Largest delta: .maka-bubble-assistant 1.68 → 1.5 (chat core, magic-number fix).
  • font-weight: 8 distinct (incl. mid-axis 550/620/650/680 from variable Geist) → 4 tokens. Snap: 550/620/650 → semibold (600), 680 → bold (700).
  • letter-spacing: 14 distinct (incl. CJK mis-tightening -0.018em) → 4 tokens. All negatives snap to --tracking-normal (0) per CJK ban. ALL-CAPS labels use --tracking-wider/widest (roadmap §4.1). 1px--tracking-widest (0.1em ≈ 1.3px @ 13px).
  • font-variation-settings: dropped .maka-hero h1's "wght" 550, "wdth" 105 — wght folds into font-weight token, wdth:105 one-off dropped (hero stays distinct via size + weight + leading alone).
  • font-family minor: var(--mono-font, monospace)var(--font-mono) (was a typo'd token name); redundant var(--font-mono, ui-monospace, …) fallbacks → var(--font-mono); Georgia, serifvar(--font-serif) (tokenized).

Contracts

Two new converge-contract tests, mirroring typography-converge-contract.test.ts:

  • font-weight-converge-contract.test.ts — bans bare font-weight numbers + normal/bold/lighter/bolder + bare weight in font: shorthand; whitelists 4 --font-weight-* tokens + inherit/initial/unset/revert; pins values; asserts @theme inline bridging.
  • letter-spacing-converge-contract.test.ts — bans bare letter-spacing em/px/normal/negative; whitelists 4 --tracking-* tokens + 0 + literals; pins values; asserts bridging.

(line-height-converge-contract.test.ts landed in the first commit pair of this PR.)

Verification

  • line-height-converge-contract: 9/9 pass
  • font-weight-converge-contract: 8/8 pass
  • letter-spacing-converge-contract: 7/7 pass
  • Full desktop suite: 1910/1910 pass (no regressions; +15 from the two new contracts)
  • renderer build: success (Tailwind v4 @theme inline same-name --leading-* / --font-weight-* / --tracking-* bridging verified)
  • Dual-theme screenshots (turn-narrative + first-run, light + dark): visual deltas are barely-perceptible as expected — .maka-hero h1 is the most visible (wdth 105 drop + 550→600 + tracking -0.018→0); confirmed acceptable.

Commits

  1. refactor(ui): converge line-height onto --leading-* tokens — 25 css, 4 tokens + bridge + ~160 replacements
  2. test(ui): lock line-height converge contract — 9 tests
  3. refactor(ui): converge font-weight + letter-spacing + font-family minor — 28 css, 9 tokens + bridge + ~267 replacements + font-variation fold + font-family cleanup
  4. test(ui): lock font-weight + letter-spacing converge contracts — 15 tests

Out of scope (next PRs in #520)

PR2 visual-state & motion tokens (opacity / focus-ring / motion duration+amplitude), PR3 anti-layout-shift, PR4 layout surface & sizing, PR5 Base UI small migrations + style-hook, PR6 Base UI large migrations.

Tracking issue: #520.

Add 4 --leading-* tokens (none/tight/snug/normal = 1/1.25/1.375/1.5) aligned to a Tailwind leading-scale subset (drop relaxed/loose, zero sites). Bridge --leading-* to Tailwind leading-* utilities in @theme inline. Replace ~160 bare line-height values across renderer CSS with var(--leading-*), incl. the 20px magic (plan-reminders.css) and 1.68 bubble-assistant. Snug is the one tier that can't be cut: 40 sites at 1.3-1.4 (13/11px small-text dense zones) collapse to tight/normal at ±0.1-0.2, visible on small text. Tracking issue: #520.
Add line-height-converge-contract.test.ts mirroring typography-converge-contract: ban bare line-height numbers/px/rem/normal, whitelist 4 --leading-* tokens + em + inherit/initial/unset/revert/0; pin token values; assert Tailwind @theme inline bridging. Tracking issue: #520.
…or (#520 PR1)
Add --font-weight-* (4-tier: normal/medium/semibold/bold = 400/500/600/700) + --tracking-* (4-tier: normal/wide/wider/widest = 0/0.025em/0.05em/0.1em; no tight — CJK-first, roadmap §1.6 bans tightening CJK) + --font-serif tokens; bridge to Tailwind font-*/tracking-* utilities in @theme inline. Replace ~205 font-weight (550/620/650→semibold, 680→bold) + ~62 letter-spacing (negatives→normal) values across renderer CSS. Drop font-variation-settings "wght" 550 "wdth" 105 on .maka-hero h1 (wght folds into font-weight token, wdth one-off dropped). Fix font-family: var(--mono-font,…)→var(--font-mono), redundant fallbacks→var(--font-mono), Georgia,serif→var(--font-serif). Tracking issue: #520.
…PR1)
Add font-weight-converge-contract.test.ts + letter-spacing-converge-contract.test.ts mirroring line-height-converge-contract: ban bare font-weight numbers/normal/bold + bare letter-spacing em/px/normal/negative; whitelist --font-weight-*/--tracking-* tokens + literals; pin token values; assert @theme inline bridging. Tracking issue: #520.
@Astro-Han
Astro-Hanforce-pushed the refactor/typography-converge branch from dfb801b to e772d93CompareJuly 4, 2026 17:54
P2: readAllRendererCss now fails closed — removed the catch that silently degraded to reading only styles.css when @import expansion broke, so a missing/bad import surfaces instead of skipping styles/* (exported expandCssImports for direct testing).
P2: ban non-literal font: shorthand via shared findFontShorthandOffenders — covers font-weight + line-height bypass vectors (font: 600 var(--font-size-ui) …, font: var(--font-size-ui)/1.4 …). Renderer CSS only uses font: inherit today, so the whitelist is literals-only (inherit/initial/unset/revert); no regex arms race over which shorthand component is bare. Value is extracted and checked against the literal set rather than using a negative lookahead (which backtracks \s* and matches font: inherit as an offender).
P2: pin regex now ends with \s*; so drifted values fail — --font-weight-normal: 4000; no longer prefix-matches 400, --tracking-normal: 0.02em; no longer prefix-matches 0, --leading-normal: 1.55; no longer prefix-matches 1.5.
P3: extracted shared font: shorthand scanner into css-test-helpers (partial de-dup of the three converge contracts; full table-driven merge left out to keep per-property invariant docs readable).
New css-test-helpers.test.ts: bad @import throws with the missing path, font: shorthand literal vs non-literal, comment stripping.
Tracking issue: #520.
P2: replace scattered assert.match pin regex with shared assertTokenPinnedOnce. assert.match only proved a correct declaration existed somewhere — a later overriding declaration (--font-weight-normal: 400; --font-weight-normal: 450;) still passed because the first match satisfied it, so a drifted override would slip through. assertTokenPinnedOnce parses maka-tokens.css custom property declarations and asserts each typography token is declared exactly once with the exact value (fails on duplicates and on a single drifted value).
New css-test-helpers exports: parseCssCustomProps, assertTokenPinnedOnce.
css-test-helpers.test.ts: assertTokenPinnedOnce unit tests (duplicate override, single drift, missing token, comment strip).
Three converge contracts: pin tests use assertTokenPinnedOnce; dropped the now-redundant pin regex negative tests (they tested the old regex mechanism, now superseded).
Reviewer negative cases locked: --font-weight-normal: 400; --font-weight-normal: 450; / --leading-normal: 1.5; --leading-normal: 1.55; / --tracking-normal: 0; --tracking-normal: 0.02em; all fail with 'exactly once'.
Tracking issue: #520.
P2: the styles.css @theme inline bridge aliases (--leading-*/--font-weight-*/--tracking-* → var(--*)) were still checked with assert.match, so a duplicate overriding declaration (--leading-normal: var(--leading-normal); --leading-normal: 1.55;) would pass because the first match satisfied assert.match. Reused the custom property parser: bridge tests now use assertCustomPropPinnedOnce (exactly-once + exact value), same as token pin tests.
Renamed assertTokenPinnedOnce → assertCustomPropPinnedOnce (the helper was never token-specific — it parses any custom property; the new name fits both maka-tokens.css definitions and styles.css bridge aliases).
Reviewer negative cases locked: --font-weight-normal: var(--font-weight-normal); --font-weight-normal: 450; / --leading-normal: var(--leading-normal); --leading-normal: 1.55; / --tracking-normal: var(--tracking-normal); --tracking-normal: 0.02em; all fail with 'exactly once' (css-test-helpers.test.ts).
Tracking issue: #520.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han