diff --git a/desktop/src/features/messages/ui/DiffViewer.css b/desktop/src/features/messages/ui/DiffViewer.css index dcc97fb78..6b4c4168a 100644 --- a/desktop/src/features/messages/ui/DiffViewer.css +++ b/desktop/src/features/messages/ui/DiffViewer.css @@ -52,7 +52,7 @@ } .buzz-diff-theme .diff { - font-size: 0.75rem; + font-size: calc(var(--buzz-type-rem) * 0.75); } .buzz-diff-theme .diff td { @@ -68,7 +68,7 @@ padding: 0.125rem 0.5rem; border-right: 1px solid hsl(var(--border) / 0.65); color: hsl(var(--muted-foreground)); - font-size: 0.6875rem; + font-size: calc(var(--buzz-type-rem) * 0.6875); } .buzz-diff-theme .buzz-diff-code { @@ -86,7 +86,7 @@ padding: 0.2rem 0.75rem; background: hsl(var(--muted) / 0.35); color: hsl(var(--muted-foreground)); - font-size: 0.6875rem; + font-size: calc(var(--buzz-type-rem) * 0.6875); } .buzz-diff-theme .diff-gutter-omit::before { diff --git a/desktop/src/shared/lib/fontSizePreference.test.mjs b/desktop/src/shared/lib/fontSizePreference.test.mjs index f62b427bd..a9e7cd169 100644 --- a/desktop/src/shared/lib/fontSizePreference.test.mjs +++ b/desktop/src/shared/lib/fontSizePreference.test.mjs @@ -3,7 +3,10 @@ import test from "node:test"; const values = new Map(); const attributes = new Map(); -const style = { fontSize: "" }; +const styleValues = new Map(); +const style = { + setProperty: (name, value) => styleValues.set(name, value), +}; globalThis.localStorage = { getItem: (key) => values.get(key) ?? null, @@ -32,7 +35,7 @@ test("persists and applies the selected font size across the app", () => { assert.equal(preference.getFontSize(), "smaller"); assert.equal(values.get(preference.FONT_SIZE_STORAGE_KEY), "smaller"); assert.equal(attributes.get("data-font-size"), "smaller"); - assert.equal(style.fontSize, "16px"); + assert.equal(styleValues.get("--buzz-type-rem"), "16px"); }); test("previews a font size without changing the saved preference", () => { @@ -42,11 +45,11 @@ test("previews a font size without changing the saved preference", () => { assert.equal(preference.getFontSize(), "smaller"); assert.equal(values.get(preference.FONT_SIZE_STORAGE_KEY), "smaller"); assert.equal(attributes.get("data-font-size"), "larger"); - assert.equal(style.fontSize, "20.114286px"); + assert.equal(styleValues.get("--buzz-type-rem"), "20.114286px"); preference.previewFontSize(null); assert.equal(attributes.get("data-font-size"), "smaller"); - assert.equal(style.fontSize, "17.6px"); + assert.equal(styleValues.get("--buzz-type-rem"), "17.6px"); }); test("initializes from the stored font size", () => { @@ -55,5 +58,5 @@ test("initializes from the stored font size", () => { preference.initializeFontSizePreference(); assert.equal(preference.getFontSize(), "larger"); assert.equal(attributes.get("data-font-size"), "larger"); - assert.equal(style.fontSize, "18.285714px"); + assert.equal(styleValues.get("--buzz-type-rem"), "18.285714px"); }); diff --git a/desktop/src/shared/lib/fontSizePreference.ts b/desktop/src/shared/lib/fontSizePreference.ts index a55173624..9ccde63d0 100644 --- a/desktop/src/shared/lib/fontSizePreference.ts +++ b/desktop/src/shared/lib/fontSizePreference.ts @@ -7,15 +7,17 @@ export const FONT_SIZE_STORAGE_KEY = "buzz.appearance.fontSize"; export const DEFAULT_FONT_SIZE: FontSize = "default"; /** - * Root sizes that map Tailwind's production `text-sm` size (0.875rem) to the - * three app-wide 14px, 15px, and 16px steps. + * Virtual rem sizes used by typography tokens. Keeping the real root at 16px + * prevents a text preference from also resizing rem-based layout geometry. */ -const BASE_FONT_SIZE_PX: Record = { +const TYPE_REM_SIZE_PX: Record = { smaller: 14 / 0.875, default: 15 / 0.875, larger: 16 / 0.875, }; +const TYPE_REM_PROPERTY = "--buzz-type-rem"; + const listeners = new Set<() => void>(); let fontSize: FontSize = DEFAULT_FONT_SIZE; let textZoomFactor = 1; @@ -36,16 +38,16 @@ function readStoredFontSize(): FontSize { } } -function rootFontSizePx(size: FontSize): number { +function typeRemSizePx(size: FontSize): number { return ( - Math.round(BASE_FONT_SIZE_PX[size] * textZoomFactor * 1_000_000) / 1_000_000 + Math.round(TYPE_REM_SIZE_PX[size] * textZoomFactor * 1_000_000) / 1_000_000 ); } function applyFontSize(size: FontSize): void { const root = globalThis.document?.documentElement; root?.setAttribute("data-font-size", size); - if (root) root.style.fontSize = `${rootFontSizePx(size)}px`; + root?.style.setProperty(TYPE_REM_PROPERTY, `${typeRemSizePx(size)}px`); } function notifyListeners(): void { diff --git a/desktop/src/shared/styles/globals/avatar-framing.css b/desktop/src/shared/styles/globals/avatar-framing.css index 44c11163f..79a2e2ad0 100644 --- a/desktop/src/shared/styles/globals/avatar-framing.css +++ b/desktop/src/shared/styles/globals/avatar-framing.css @@ -132,8 +132,8 @@ width: 100%; max-width: none; color: hsl(var(--muted-foreground) / 0.72); - font-size: 0.875rem; - line-height: 1.25rem; + font-size: calc(var(--buzz-type-rem) * 0.875); + line-height: calc(var(--buzz-type-rem) * 1.25); text-align: center; opacity: 0; pointer-events: none; diff --git a/desktop/src/shared/styles/globals/components.css b/desktop/src/shared/styles/globals/components.css index 5ab551bed..7bd3fc463 100644 --- a/desktop/src/shared/styles/globals/components.css +++ b/desktop/src/shared/styles/globals/components.css @@ -548,7 +548,7 @@ .buzz-onboarding-runtime-pill { animation: buzz-onboarding-runtime-pill-in 180ms cubic-bezier(0.22, 1, 0.36, 1) both; - font-size: 0.625rem; + font-size: calc(var(--buzz-type-rem) * 0.625); letter-spacing: 0; line-height: 1; text-transform: uppercase; diff --git a/desktop/src/shared/styles/globals/terminal.css b/desktop/src/shared/styles/globals/terminal.css index 544f0ae2e..c5aa001af 100644 --- a/desktop/src/shared/styles/globals/terminal.css +++ b/desktop/src/shared/styles/globals/terminal.css @@ -45,7 +45,7 @@ color: hsl(var(--muted-foreground)); display: flex; font: inherit; - font-size: 0.75rem; + font-size: calc(var(--buzz-type-rem) * 0.75); font-weight: 500; gap: 6px; height: 30px; @@ -130,7 +130,7 @@ .buzz-terminal-designator { align-items: center; display: inline-flex; - font-size: 0.75rem; + font-size: calc(var(--buzz-type-rem) * 0.75); gap: 2px; letter-spacing: 0; } diff --git a/desktop/src/shared/styles/globals/theme.css b/desktop/src/shared/styles/globals/theme.css index ec7fdc67e..614a0e79b 100644 --- a/desktop/src/shared/styles/globals/theme.css +++ b/desktop/src/shared/styles/globals/theme.css @@ -3,19 +3,35 @@ /* Catppuccin Latte (mauve accent) */ --radius: 0.625rem; /* - * Default conversation type and comfy spacing for channels, DMs, threads, - * Inbox, and the composer. The app-wide Font size preference and Cmd +/- - * scale these rem values; Conversation density overrides only spacing. + * A virtual typography rem. Font preferences and Cmd +/- change this + * token while the browser root remains 16px, so text scales without also + * resizing rem-based widths, gaps, radii, and controls. */ - --conversation-message-font-size: 0.875rem; - --conversation-message-line-height: 1.25rem; - --conversation-author-line-height: 1rem; + --buzz-type-rem: 1rem; + --text-xs: calc(var(--buzz-type-rem) * 0.75); + --text-sm: calc(var(--buzz-type-rem) * 0.875); + --text-base: var(--buzz-type-rem); + --text-lg: calc(var(--buzz-type-rem) * 1.125); + --text-xl: calc(var(--buzz-type-rem) * 1.25); + --text-2xl: calc(var(--buzz-type-rem) * 1.5); + --text-3xl: calc(var(--buzz-type-rem) * 1.875); + --text-4xl: calc(var(--buzz-type-rem) * 2.25); + --text-5xl: calc(var(--buzz-type-rem) * 3); + --text-6xl: calc(var(--buzz-type-rem) * 3.75); + /* + * Default conversation type and comfy spacing for channels, DMs, threads, + * Inbox, and the composer. Font size changes the type tokens only; + * Conversation density overrides only spacing. + */ + --conversation-message-font-size: calc(var(--buzz-type-rem) * 0.875); + --conversation-message-line-height: calc(var(--buzz-type-rem) * 1.25); + --conversation-author-line-height: var(--buzz-type-rem); --conversation-body-gap: 0.125rem; --conversation-row-padding-block: 0.375rem; --conversation-paragraph-gap: 0.5rem; --conversation-list-item-gap: 0.375rem; - --conversation-timestamp-font-size: 0.75rem; - --conversation-timestamp-line-height: 1rem; + --conversation-timestamp-font-size: calc(var(--buzz-type-rem) * 0.75); + --conversation-timestamp-line-height: var(--buzz-type-rem); --background: 220 23.08% 94.9%; --foreground: 234 16.02% 35.49%; --card: 220 23.08% 94.9%; diff --git a/desktop/tailwind.config.js b/desktop/tailwind.config.js index 5db047b76..5d2c1444f 100644 --- a/desktop/tailwind.config.js +++ b/desktop/tailwind.config.js @@ -3,17 +3,16 @@ export default { theme: { extend: { // Sub-`text-xs` ramp for meta text (timestamps, count badges, tracking - // labels) and tiny glyphs. Defined in rem so Cmd +/- zoom — which scales - // the root font-size — keeps scaling them. Do NOT reintroduce - // arbitrary `text-[…rem]` / `text-[…px]` literals; the px-text guard - // rejects them. Stock scale picks up from here: xs (12px), sm (14px)… + // labels) and tiny glyphs. These follow the virtual typography rem so + // preferences and Cmd +/- scale text without changing layout geometry. + // Do NOT reintroduce arbitrary `text-[…rem]` / `text-[…px]` literals; + // the px-text guard rejects them. Stock scale picks up from xs. fontSize: { - "2xs": "0.6875rem", // 11px — meta-text workhorse (timestamps, badges) - "3xs": "0.5rem", // 8px — tiny glyphs / micro labels - badge: "0.625rem", // 10px — compact status badges - // Shared channel, DM, thread, and composer type. Rem-backed variables - // keep app-wide font size and keyboard zoom consistent without - // branching individual components. + "2xs": "calc(var(--buzz-type-rem) * 0.6875)", // 11px at 16px type rem + "3xs": "calc(var(--buzz-type-rem) * 0.5)", // 8px at 16px type rem + badge: "calc(var(--buzz-type-rem) * 0.625)", // 10px at 16px type rem + // Shared channel, DM, thread, and composer type. Variables keep app-wide + // font size and keyboard zoom consistent without branching components. message: [ "var(--conversation-message-font-size)", { lineHeight: "var(--conversation-message-line-height)" }, @@ -22,10 +21,16 @@ export default { "var(--conversation-timestamp-font-size)", { lineHeight: "var(--conversation-timestamp-line-height)" }, ], - // 40px — onboarding page titles (tightened tracking for large display type) - title: ["2.5rem", { lineHeight: "1.15", letterSpacing: "-0.02em" }], - // 36px — the backup-step private key, shown large in monospace - "nsec-key": ["2.25rem", { lineHeight: "1.3" }], + // 40px at the 16px type rem — onboarding page titles. + title: [ + "calc(var(--buzz-type-rem) * 2.5)", + { lineHeight: "1.15", letterSpacing: "-0.02em" }, + ], + // 36px at the 16px type rem — backup-step private key. + "nsec-key": [ + "calc(var(--buzz-type-rem) * 2.25)", + { lineHeight: "1.3" }, + ], }, lineHeight: { "message-author": "var(--conversation-author-line-height)", diff --git a/desktop/tests/e2e/buzz-theme-screenshots.spec.ts b/desktop/tests/e2e/buzz-theme-screenshots.spec.ts index 7ab8c1618..efabbe59d 100644 --- a/desktop/tests/e2e/buzz-theme-screenshots.spec.ts +++ b/desktop/tests/e2e/buzz-theme-screenshots.spec.ts @@ -663,11 +663,9 @@ test("app font size and conversation density apply independently", async ({ bodyGap: Number.parseFloat( style.getPropertyValue("--conversation-body-gap"), ), - fontSize: Number.parseFloat( - style.getPropertyValue("--conversation-message-font-size"), - ), - lineHeight: Number.parseFloat( - style.getPropertyValue("--conversation-message-line-height"), + fontSize: style.getPropertyValue("--conversation-message-font-size"), + lineHeight: style.getPropertyValue( + "--conversation-message-line-height", ), paragraphGap: Number.parseFloat( style.getPropertyValue("--conversation-paragraph-gap"), @@ -675,8 +673,8 @@ test("app font size and conversation density apply independently", async ({ rowPadding: Number.parseFloat( style.getPropertyValue("--conversation-row-padding-block"), ), - timestampFontSize: Number.parseFloat( - style.getPropertyValue("--conversation-timestamp-font-size"), + timestampFontSize: style.getPropertyValue( + "--conversation-timestamp-font-size", ), timestampLineHeight: Number.parseFloat( style.getPropertyValue("--conversation-timestamp-line-height"), @@ -731,14 +729,14 @@ test("app font size and conversation density apply independently", async ({ ); await expect(fontSizeDescription).toHaveText("Adjust text throughout Buzz."); await expect.poll(readScale).toEqual({ - authorLineHeight: 1, + authorLineHeight: 17.142857, bodyGap: 0.125, - fontSize: 0.875, - lineHeight: 1.25, + fontSize: "calc(17.142857px * .875)", + lineHeight: "calc(17.142857px * 1.25)", paragraphGap: 0.5, rowPadding: 0.375, - timestampFontSize: 0.75, - timestampLineHeight: 1, + timestampFontSize: "calc(17.142857px * .75)", + timestampLineHeight: 17.142857, }); await expect .poll(() => @@ -754,7 +752,7 @@ test("app font size and conversation density apply independently", async ({ .evaluate((element) => element.getBoundingClientRect().width), ]), ) - .toEqual([308.5625, 308.5625, 257.140625]); + .toEqual([288, 288, 240]); await expect .poll(() => previewMessage.evaluate((element) => { @@ -766,8 +764,8 @@ test("app font size and conversation density apply independently", async ({ await expect.poll(readSettingsScale).toEqual({ fontSize: "15px", lineHeight: "21.4286px", - minHeight: "68.5714px", - paddingBlock: "12.8571px", + minHeight: "64px", + paddingBlock: "12px", }); await expect .poll(readPreviewTimestampScale) @@ -820,24 +818,24 @@ test("app font size and conversation density apply independently", async ({ const previewChipTopInset = previewChipBox.y - previewSurfaceBox.y; expect(previewChipTopInset).toBeGreaterThanOrEqual(13); expect(previewChipTopInset).toBeLessThanOrEqual(15); - await expect(previewContent).toHaveCSS("padding-top", "17.1429px"); - await expect(previewContent).toHaveCSS("padding-right", "17.1429px"); - await expect(previewContent).toHaveCSS("padding-bottom", "17.1429px"); - await expect(previewContent).toHaveCSS("padding-left", "17.1429px"); + await expect(previewContent).toHaveCSS("padding-top", "16px"); + await expect(previewContent).toHaveCSS("padding-right", "16px"); + await expect(previewContent).toHaveCSS("padding-bottom", "16px"); + await expect(previewContent).toHaveCSS("padding-left", "16px"); expect(firstPreviewMessageBox.x - previewSurfaceBox.x).toBeGreaterThanOrEqual( - 17, + 15, ); expect(firstPreviewMessageBox.x - previewSurfaceBox.x).toBeLessThanOrEqual( - 19, - ); - expect(firstPreviewMessageBox.y - previewSurfaceBox.y).toBeGreaterThanOrEqual( 17, ); - expect(firstPreviewMessageBox.y - previewSurfaceBox.y).toBeLessThanOrEqual( - 19, + expect(firstPreviewMessageBox.y - previewSurfaceBox.y).toBeGreaterThanOrEqual( + 15, ); - await expect(previewChip).toHaveCSS("padding-top", "4.28571px"); - await expect(previewChip).toHaveCSS("padding-bottom", "4.28571px"); + expect(firstPreviewMessageBox.y - previewSurfaceBox.y).toBeLessThanOrEqual( + 17, + ); + await expect(previewChip).toHaveCSS("padding-top", "4px"); + await expect(previewChip).toHaveCSS("padding-bottom", "4px"); await densityIndicator.evaluate((element) => { element.addEventListener( @@ -861,20 +859,20 @@ test("app font size and conversation density apply independently", async ({ ) .toBe("compact"); await expect.poll(readScale).toEqual({ - authorLineHeight: 1, + authorLineHeight: 17.142857, bodyGap: 0, - fontSize: 0.875, - lineHeight: 1.25, + fontSize: "calc(17.142857px * .875)", + lineHeight: "calc(17.142857px * 1.25)", paragraphGap: 0.375, rowPadding: 0.25, - timestampFontSize: 0.75, - timestampLineHeight: 1, + timestampFontSize: "calc(17.142857px * .75)", + timestampLineHeight: 17.142857, }); await expect.poll(readSettingsScale).toEqual({ fontSize: "15px", lineHeight: "21.4286px", - minHeight: "68.5714px", - paddingBlock: "12.8571px", + minHeight: "64px", + paddingBlock: "12px", }); await expect .poll(readPreviewTimestampScale) @@ -896,14 +894,14 @@ test("app font size and conversation density apply independently", async ({ ) .toBe("larger"); await expect.poll(readScale).toEqual({ - authorLineHeight: 1, + authorLineHeight: 18.285714, bodyGap: 0, - fontSize: 0.875, - lineHeight: 1.25, + fontSize: "calc(18.285714px * .875)", + lineHeight: "calc(18.285714px * 1.25)", paragraphGap: 0.375, rowPadding: 0.25, - timestampFontSize: 0.75, - timestampLineHeight: 1, + timestampFontSize: "calc(18.285714px * .75)", + timestampLineHeight: 18.285714, }); await expect .poll(() => @@ -916,8 +914,8 @@ test("app font size and conversation density apply independently", async ({ await expect.poll(readSettingsScale).toEqual({ fontSize: "16px", lineHeight: "22.8571px", - minHeight: "73.1429px", - paddingBlock: "13.7143px", + minHeight: "64px", + paddingBlock: "12px", }); await expect .poll(readPreviewTimestampScale) @@ -935,20 +933,20 @@ test("app font size and conversation density apply independently", async ({ await expect(root).toHaveAttribute("data-font-size", "larger"); await expect(spacious).toHaveAttribute("aria-pressed", "true"); await expect.poll(readScale).toEqual({ - authorLineHeight: 1, + authorLineHeight: 18.285714, bodyGap: 0.25, - fontSize: 0.875, - lineHeight: 1.25, + fontSize: "calc(18.285714px * .875)", + lineHeight: "calc(18.285714px * 1.25)", paragraphGap: 0.625, rowPadding: 0.5, - timestampFontSize: 0.75, - timestampLineHeight: 1, + timestampFontSize: "calc(18.285714px * .75)", + timestampLineHeight: 18.285714, }); await expect.poll(readSettingsScale).toEqual({ fontSize: "16px", lineHeight: "22.8571px", - minHeight: "73.1429px", - paddingBlock: "13.7143px", + minHeight: "64px", + paddingBlock: "12px", }); await expect .poll(readPreviewTimestampScale) @@ -962,14 +960,14 @@ test("app font size and conversation density apply independently", async ({ await expect(root).toHaveAttribute("data-font-size", "smaller"); await expect(smaller).toHaveAttribute("aria-pressed", "true"); await expect.poll(readScale).toEqual({ - authorLineHeight: 1, + authorLineHeight: 16, bodyGap: 0.25, - fontSize: 0.875, - lineHeight: 1.25, + fontSize: "calc(16px * .875)", + lineHeight: "calc(16px * 1.25)", paragraphGap: 0.625, rowPadding: 0.5, - timestampFontSize: 0.75, - timestampLineHeight: 1, + timestampFontSize: "calc(16px * .75)", + timestampLineHeight: 16, }); await expect .poll(() => @@ -1029,20 +1027,20 @@ test("app font size and conversation density apply independently", async ({ ) .toBe("comfortable"); await expect.poll(readScale).toEqual({ - authorLineHeight: 1, + authorLineHeight: 17.142857, bodyGap: 0.25, - fontSize: 0.875, - lineHeight: 1.25, + fontSize: "calc(17.142857px * .875)", + lineHeight: "calc(17.142857px * 1.25)", paragraphGap: 0.625, rowPadding: 0.5, - timestampFontSize: 0.75, - timestampLineHeight: 1, + timestampFontSize: "calc(17.142857px * .75)", + timestampLineHeight: 17.142857, }); await expect.poll(readSettingsScale).toEqual({ fontSize: "15px", lineHeight: "21.4286px", - minHeight: "68.5714px", - paddingBlock: "12.8571px", + minHeight: "64px", + paddingBlock: "12px", }); await page.mouse.up(); await expect(spacious).toHaveAttribute("aria-pressed", "true"); diff --git a/desktop/tests/e2e/inbox-refactor-screenshots.spec.ts b/desktop/tests/e2e/inbox-refactor-screenshots.spec.ts index 1f5e477f8..64e0b6af1 100644 --- a/desktop/tests/e2e/inbox-refactor-screenshots.spec.ts +++ b/desktop/tests/e2e/inbox-refactor-screenshots.spec.ts @@ -414,7 +414,7 @@ test.describe("inbox refactor screenshots", () => { ]); await expect.poll(readConversationMetrics).toEqual([ { fontSize: "15px", lineHeight: "21.4286px" }, - { paddingBottom: "6.42857px", paddingTop: "6.42857px" }, + { paddingBottom: "6px", paddingTop: "6px" }, { fontSize: "15px", lineHeight: "17.1429px" }, { fontSize: "15px", lineHeight: "21.4286px" }, { fontSize: "12.8571px", lineHeight: "17.1429px" }, @@ -429,7 +429,7 @@ test.describe("inbox refactor screenshots", () => { }); await expect.poll(readConversationMetrics).toEqual([ { fontSize: "15px", lineHeight: "21.4286px" }, - { paddingBottom: "4.28571px", paddingTop: "4.28571px" }, + { paddingBottom: "4px", paddingTop: "4px" }, { fontSize: "15px", lineHeight: "17.1429px" }, { fontSize: "15px", lineHeight: "21.4286px" }, { fontSize: "12.8571px", lineHeight: "17.1429px" }, @@ -438,7 +438,7 @@ test.describe("inbox refactor screenshots", () => { await page.locator("html").evaluate((root) => { root.setAttribute("data-font-size", "smaller"); - root.style.fontSize = "16px"; + root.style.setProperty("--buzz-type-rem", "16px"); }); await expect.poll(readConversationMetrics).toEqual([ { fontSize: "14px", lineHeight: "20px" }, @@ -454,11 +454,11 @@ test.describe("inbox refactor screenshots", () => { await page.locator("html").evaluate((root) => { root.setAttribute("data-conversation-density", "spacious"); root.setAttribute("data-font-size", "larger"); - root.style.fontSize = "18.285714px"; + root.style.setProperty("--buzz-type-rem", "18.285714px"); }); await expect.poll(readConversationMetrics).toEqual([ { fontSize: "16px", lineHeight: "22.8571px" }, - { paddingBottom: "9.14286px", paddingTop: "9.14286px" }, + { paddingBottom: "8px", paddingTop: "8px" }, { fontSize: "16px", lineHeight: "18.2857px" }, { fontSize: "16px", lineHeight: "22.8571px" }, { fontSize: "13.7143px", lineHeight: "18.2857px" }, @@ -470,7 +470,7 @@ test.describe("inbox refactor screenshots", () => { await page.locator("html").evaluate((root) => { root.setAttribute("data-conversation-density", "comfortable"); root.setAttribute("data-font-size", "default"); - root.style.fontSize = "17.142857px"; + root.style.setProperty("--buzz-type-rem", "17.142857px"); }); await page.evaluate(() => { @@ -490,15 +490,18 @@ test.describe("inbox refactor screenshots", () => { await expect .poll(async () => [ - await page.evaluate( - () => window.getComputedStyle(document.documentElement).fontSize, + await page.evaluate(() => + window + .getComputedStyle(document.documentElement) + .getPropertyValue("--buzz-type-rem") + .trim(), ), ...(await readConversationMetrics()), ]) .toEqual([ - "18.8571px", + "18.857143px", { fontSize: "16.5px", lineHeight: "23.5714px" }, - { paddingBottom: "7.07143px", paddingTop: "7.07143px" }, + { paddingBottom: "6px", paddingTop: "6px" }, { fontSize: "16.5px", lineHeight: "18.8571px" }, { fontSize: "16.5px", lineHeight: "23.5714px" }, { fontSize: "14.1429px", lineHeight: "18.8571px" }, diff --git a/desktop/tests/e2e/profile.spec.ts b/desktop/tests/e2e/profile.spec.ts index 263113547..d84925d8a 100644 --- a/desktop/tests/e2e/profile.spec.ts +++ b/desktop/tests/e2e/profile.spec.ts @@ -1556,7 +1556,10 @@ test("supports webview zoom keyboard shortcuts", async ({ page }) => { const getTextScaleState = () => page.evaluate(() => ({ - fontSize: getComputedStyle(document.documentElement).fontSize, + rootFontSize: getComputedStyle(document.documentElement).fontSize, + textRemSize: getComputedStyle(document.documentElement) + .getPropertyValue("--buzz-type-rem") + .trim(), storedScale: localStorage.getItem("buzz:text-scale"), webviewZoom: (window as Window & { __BUZZ_E2E_WEBVIEW_ZOOM__?: number }) .__BUZZ_E2E_WEBVIEW_ZOOM__, @@ -1587,7 +1590,8 @@ test("supports webview zoom keyboard shortcuts", async ({ page }) => { await dispatchPrimaryShortcut("+", "Equal", true); await expect.poll(getTextScaleState).toEqual({ - fontSize: "17.6px", + rootFontSize: "16px", + textRemSize: "18.857143px", storedScale: "1.1", webviewZoom: 1, }); @@ -1595,7 +1599,8 @@ test("supports webview zoom keyboard shortcuts", async ({ page }) => { await dispatchPrimaryShortcut("-", "Minus"); await expect.poll(getTextScaleState).toEqual({ - fontSize: "16px", + rootFontSize: "16px", + textRemSize: "17.142857px", storedScale: null, webviewZoom: 1, }); @@ -1604,7 +1609,8 @@ test("supports webview zoom keyboard shortcuts", async ({ page }) => { await dispatchPrimaryShortcut("+", "Equal", true); await expect.poll(getTextScaleState).toEqual({ - fontSize: "19.2px", + rootFontSize: "16px", + textRemSize: "20.571429px", storedScale: "1.2", webviewZoom: 1, }); @@ -1612,7 +1618,8 @@ test("supports webview zoom keyboard shortcuts", async ({ page }) => { await dispatchPrimaryShortcut("0", "Digit0"); await expect.poll(getTextScaleState).toEqual({ - fontSize: "16px", + rootFontSize: "16px", + textRemSize: "17.142857px", storedScale: null, webviewZoom: 1, }); diff --git a/desktop/tests/e2e/top-chrome-zoom-clearance.spec.ts b/desktop/tests/e2e/top-chrome-zoom-clearance.spec.ts index eb69953db..5101de3e1 100644 --- a/desktop/tests/e2e/top-chrome-zoom-clearance.spec.ts +++ b/desktop/tests/e2e/top-chrome-zoom-clearance.spec.ts @@ -23,7 +23,7 @@ const EXPECTED_NAV_CENTER_Y = 23; // The macOS traffic lights are native chrome: with `trafficLightPosition` // x:16 they occupy roughly x 16–68 regardless of the app's Cmd +/- text // zoom. The top-chrome nav row must clear that band in fixed px, so the -// clearance cannot shrink when the root font size scales down. +// clearance cannot change when text scales. const TRAFFIC_LIGHT_RIGHT_EDGE = 72; async function spoofMacPlatform(page: import("@playwright/test").Page) { @@ -90,15 +90,24 @@ async function seedTextScale( }, scale); } -async function expectRootFontSize( +async function expectTextRemSize( page: import("@playwright/test").Page, fontSize: string, ) { await expect .poll(() => - page.evaluate(() => getComputedStyle(document.documentElement).fontSize), + page.evaluate(() => + getComputedStyle(document.documentElement) + .getPropertyValue("--buzz-type-rem") + .trim(), + ), ) .toBe(fontSize); + await expect + .poll(() => + page.evaluate(() => getComputedStyle(document.documentElement).fontSize), + ) + .toBe("16px"); } test.describe("top chrome macOS traffic-light clearance under text zoom", () => { @@ -143,8 +152,8 @@ test.describe("top chrome macOS traffic-light clearance under text zoom", () => await installMockBridge(page); await page.goto("/"); - // Confirm the zoomed-out scale actually applied to the root font size. - await expectRootFontSize(page, "12px"); + // Confirm the zoomed-out text scale applied without changing the root. + await expectTextRemSize(page, "12.857143px"); expect(await firstNavButtonX(page)).toBeGreaterThanOrEqual( TRAFFIC_LIGHT_RIGHT_EDGE, @@ -161,7 +170,7 @@ test.describe("top chrome macOS traffic-light clearance under text zoom", () => await installMockBridge(page); await page.goto("/"); - await expectRootFontSize(page, "24px"); + await expectTextRemSize(page, "25.714286px"); expect(await firstNavButtonX(page)).toBeGreaterThanOrEqual( TRAFFIC_LIGHT_RIGHT_EDGE,