From 14fd2897e29c822a80dfa593243de9e31de2c3cd Mon Sep 17 00:00:00 2001 From: 4gray Date: Thu, 1 Oct 2026 07:33:39 +0200 Subject: [PATCH] perf(web): read the font stack from one variable to halve the token cost The 103-character app font stack was copied into every Material typography token: 47 component tokens, the 30 new --mat-sys-* typescale tokens and the typography hierarchy, 91 copies in all. Declare it once as --app-font-family on html and pass var(--app-font-family) to the theme and the hierarchy instead: -6,941 bytes of initial CSS (1,611,987 -> 1,605,046), so the system tokens now cost 5,671 bytes net instead of 12,526. The rendered font is unchanged. Lower the raised baseline to the new measurement. theme-tokens.e2e.ts applies var(--mat-sys-label-small) and checks its family, size and weight, because a dangling var() would void the whole font shorthand while body text still inherited the same family. Co-Authored-By: Claude Opus 5.5 --- .../src/theme-tokens.e2e.ts | 25 +++++++++++++++++++ apps/web/src/m3-theme.scss | 10 +++++++- tools/performance/journey-baselines.json | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/apps/electron-backend-e2e/src/theme-tokens.e2e.ts b/apps/electron-backend-e2e/src/theme-tokens.e2e.ts index ffa1e4006..9c18a3147 100644 --- a/apps/electron-backend-e2e/src/theme-tokens.e2e.ts +++ b/apps/electron-backend-e2e/src/theme-tokens.e2e.ts @@ -126,6 +126,31 @@ test.describe('Theme tokens', () => { }), `floated label size in ${theme} theme` ).toBeGreaterThanOrEqual(11); + + // The typography tokens carry the font stack through + // --app-font-family. A dangling var() voids the whole `font` + // shorthand; body text would still inherit the same family, + // so check the token's own size and weight. + expect( + await page.evaluate(() => { + const probe = document.createElement('span'); + probe.style.font = 'var(--mat-sys-label-small)'; + document.body.appendChild(probe); + const style = getComputedStyle(probe); + const font = { + family: style.fontFamily, + size: Math.round(parseFloat(style.fontSize)), + weight: style.fontWeight, + }; + probe.remove(); + return font; + }), + `label-small typography in ${theme} theme` + ).toEqual({ + family: expect.stringMatching(/^"?DM Sans"?,/), + size: 11, + weight: '500', + }); } expect(surfaces['light']).not.toBe(surfaces['dark']); } finally { diff --git a/apps/web/src/m3-theme.scss b/apps/web/src/m3-theme.scss index 90cf70856..e5190fc8a 100644 --- a/apps/web/src/m3-theme.scss +++ b/apps/web/src/m3-theme.scss @@ -6,8 +6,16 @@ @include mat.app-background(); // ─── Typography ───────────────────────────────────────────────────────────── -$app-font-stack: +// The stack is declared once on html; every Material typography token and the +// typography hierarchy read it through the variable. Inlined, the 103-char +// stack was copied into ~90 declarations (about 7 KB of the initial CSS). +$app-font-stack-value: "'DM Sans', 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif"; +$app-font-stack: var(--app-font-family); + +html { + --app-font-family: #{$app-font-stack-value}; +} $my-typography: mat.m2-define-typography-config( $font-family: $app-font-stack, diff --git a/tools/performance/journey-baselines.json b/tools/performance/journey-baselines.json index 6cbf4e063..56c319574 100644 --- a/tools/performance/journey-baselines.json +++ b/tools/performance/journey-baselines.json @@ -3,7 +3,7 @@ "journeys": { "launch": { "renderer.initialBytes": { - "value": 1611987, + "value": 1605046, "unit": "bytes", "slack": 4096, "updatedAt": "2026-10-01",