Skip to content

fix(desktop): restore true zoom by scaling the root rem - #6514

Merged
morgmart merged 2 commits into
mainfrom
morgan/root-rem-zoom
Aug 22, 2026
Merged

fix(desktop): restore true zoom by scaling the root rem#6514
morgmart merged 2 commits into
mainfrom
morgan/root-rem-zoom

Conversation

@morgmart

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #5644. Cmd +/- had become a text-only zoom: type scaled while rem-based padding, gaps, widths, avatars, and controls stayed frozen, which produced cramped layouts (see #buzz-frontend thread).

Root cause: #5644 introduced a virtual typography rem so the Font size preference could change text without moving layout — a good decoupling — but it also routed Cmd +/- zoom through that same px-valued token and pinned the real root at 16px. One decision ("freeze layout") was applied to two dials that shouldn't share it.

This PR gives each dial one owner and lets CSS compose them:

Control Changes How
Cmd +/- zoom Everything — true zoom Scales the real <html> font-size again (useWebviewZoomShortcuts)
Font size preference Text only Sets data-font-size; typography.css maps it to a unitless --buzz-type-scale, mirroring how density already works

--buzz-type-rem becomes calc(1rem * var(--buzz-type-scale)) — rem-relative, so it rides on zoom automatically. Resulting text px = 16 × zoom × scale × token-ratio. The 13 / 14 / 15px conversation contract is unchanged at default zoom. Density and the type ramp from #5644 are untouched.

The preference module no longer does px math or knows about zoom; the zoom hook no longer imports the preference module. Net deletion in production code.

Validation

  • pnpm test — 5,308 desktop unit tests
  • pnpm check:px-text, tsc --noEmit, biome
  • Playwright: top-chrome-zoom-clearance.spec.ts (native-chrome clearance stays fixed under root zoom), inbox-refactor-screenshots.spec.ts (zoomed row padding now asserts 4.4px instead of the frozen 4px), and both profile.spec.ts zoom tests (composed zoom × preference, cross-window storage reset)
  • Before/after screenshots at 140% zoom in the comment below

PR #5644 introduced a virtual typography rem so the Font size preference
could change text without moving layout, but it also routed Cmd +/- zoom
through that same px-valued token while pinning the real root at 16px.
Zoom became text-only: type grew while rem-based padding, gaps, widths,
and controls stayed frozen, producing cramped layouts.

Give each dial one owner and let CSS compose them:

- Cmd +/- once again scales the real root font-size, so every rem in the
  app zooms together.
- The Font size preference only sets `data-font-size`; typography.css
  maps it to a unitless `--buzz-type-scale`, mirroring how density works.
- `--buzz-type-rem` is now `calc(1rem * var(--buzz-type-scale))` — rem-
  relative, so it rides on top of zoom automatically. The 13 / 14 / 15px
  conversation contract is unchanged at default zoom.

The preference module no longer does px math or knows about zoom, and
the zoom hook no longer imports the preference module.

Tests now assert root font-size scales and measure the composed type rem
through a rendered probe instead of reading a px literal off the custom
property. The inbox spec's zoomed row padding expectation moves from the
frozen 4px to the correct 4.4px.

Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
@morgmart
morgmart requested a review from a team as a code owner August 21, 2026 20:20
@morgmart

Copy link
Copy Markdown
Contributor Author

Same channel, same 1280×720 window, Cmd+ to 140%. Note avatars, buttons, search field, sidebar row height, composer padding.

Before (main) — text-only zoom

Type grew; everything around it stayed at 100% geometry.

before-zoom-140

After — true zoom

Text and layout scale together. The sidebar is scrolled to keep #general in view because it is now genuinely taller than the viewport.

after-zoom-140

100% reference (before / after are pixel-identical in layout)

after-zoom-100

morgmart added a commit that referenced this pull request Aug 21, 2026
@morgmart

Copy link
Copy Markdown
Contributor Author

Blocking finding

desktop/tests/e2e/buzz-theme-screenshots.spec.ts:679-706 still reads the typography custom properties as if --buzz-type-rem were a resolved px literal, and the assertions at lines 756-765 (plus the later readScale assertions in this test) still expect the old calc(16px …) / 17.142857px strings. This PR intentionally changes those properties to rem-relative expressions in desktop/src/shared/styles/globals/typography.css:16-17, so getComputedStyle(html).getPropertyValue(...) now returns calc(calc(1rem * 1) …) and parseFloat("var(--buzz-type-rem)") produces NaN.

That is the deterministic Desktop Smoke E2E (1) failure: CI failed the same assertion on the initial run and both retries, and I reproduced it locally at head 65a3c9c86 with the focused smoke spec. Please update this existing contract test to measure resolved styles on rendered elements/probes (as the changed profile test already does) and assert the 13/14/15px type results independently from rem-based spacing. Updating only the first expectation will expose the same stale assumptions in the later readScale assertions.

The production behavior I exercised looks sound: 5,308 desktop unit tests, typecheck/check, the two focused zoom/storage integration tests, and all three top-chrome zoom tests pass at this head. I found no second material issue in the changed code.

… smoke test

The appearance smoke test read `--conversation-*` custom properties straight
off <html> and asserted on their raw strings (`calc(16px * .875)`,
`parseFloat("0.125rem")`). That only worked while `--buzz-type-rem` was a
px literal. Now that the type rem is `calc(1rem * --buzz-type-scale)` so
Cmd +/- zooms layout and text together, the raw strings are unresolved
calc text and the assertions fail deterministically in CI.

Resolve every token to px through a probe element (assign the token to the
probe's font-size, read the computed value) and assert the 13 / 14 / 15px
type contract and comfy / compact / spacious spacing as numbers, independent
of each other. This is the same approach profile.spec.ts already uses.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:bot: Jude’s code review agent — approved at head 8555bafa3ef9247ccfbfe6e42c7ab69358e2e0a3 (base 025425591ed67518a63870316f1473ffd02dd520).

The ownership split is coherent and appropriately small:

  • keyboard zoom alone owns the real root rem scale and persisted buzz:text-scale, while pinning native webview zoom to 1;
  • the Font size preference owns only data-font-size and its separate persisted key;
  • CSS composes the unitless 13/14, 1, and 15/14 type scale with root zoom, preserving the default 13/14/15 px conversation contract while scaling rem-based layout geometry;
  • storage-event handling covers peer-window updates and clears, with listener cleanup intact. No competing production root-font-size writer was found in the searched Desktop source.

Validation on this exact head:

  • just desktop-check — pass (pre-existing unrelated Biome warnings only)
  • just desktop-typecheck — pass
  • just desktop-test — 5,308/5,308 pass
  • just desktop-build — pass
  • focused changed-spec E2E after pnpm build:e2e — 31/31 pass
  • full pnpm test, pnpm check, pnpm typecheck, and pnpm build — pass in an independent clean checkout
  • applicable Desktop CI, smoke/integration shards, macOS build, and DCO — green

The regression evidence is causal rather than ornamental: restoring the old frozen-root behavior makes the inbox zoom assertion fail on root size, row padding, and gap (16px/4px/2px instead of 17.6px/4.4px/2.2px). Supplied screenshots also distinguish text-only scaling from whole-layout zoom across controls, avatars, row geometry, and spacing while retaining fixed native-chrome clearance.

Residual evidence limit: no fresh interactive real-Tauri keyboard run or fresh narrow-window screenshot was produced during review; confidence there rests on source tracing, focused E2E contracts, supplied visual evidence, and exact-head CI. No contrary evidence found. Any head change invalidates this approval pending delta review.

@morgmart
morgmart merged commit 97b1fee into main Aug 22, 2026
24 checks passed
@morgmart
morgmart deleted the morgan/root-rem-zoom branch August 22, 2026 00:21
brow added a commit that referenced this pull request Aug 22, 2026
* origin/main: (65 commits)
  fix(composer): preserve caret when inserting mentions mid-message (#6531)
  chore(deps): update rust crate async-trait to v0.1.92 (#6094)
  chore(deps): update dependency sonner to v2.0.8 (#6093)
  chore(deps): update rust crate http-body-util to v0.1.4 (#5452)
  chore(deps): update rust crate http to v1.4.2 (#5451)
  chore(deps): update rust crate futures-util to v0.3.33 (#5448)
  chore(deps): update rust crate futures to v0.3.33 (#5445)
  chore(deps): update dependency @tauri-apps/api to v2.11.1 (#5444)
  chore(deps): update ubuntu:24.04 docker digest to 561618e (#5442)
  chore(deps): update swatinem/rust-cache digest to 6323deb (#5441)
  fix(desktop): restore true zoom by scaling the root rem (#6514)
  chore(desktop): drop unused ORIGINAL_CONTENT from empty-edit-delete spec (#6517)
  feat(workflows): clarify workflow setup and activation (#6470)
  perf(desktop): stop the Projects fan refetching on re-entry and running after leave (#6458)
  perf(desktop): keep the member roster off the channel-switch path (#6456)
  fix(deletion): allow IRSA S3 credentials (#6495)
  docs(nips): comprehensive NIP-FI — core + claimable profiles (EDGE/LIFECYCLE/DELEG/CONF) (#5946)
  fix(benchmarks): wait for scripted event delivery (#6487)
  Polish mobile channel navigation and message sends (#6488)
  Clarify huddle message destination (#6496)
  ...

Signed-off-by: leader <71e9f2c44a6932b6772caaaccda1911d010463c3e2c6c40410b8329956046801@buzz.block.builderlab.xyz>

Co-authored-by: Tom Brow <tomb@block.xyz>
Signed-off-by: Tom Brow <tomb@block.xyz>
brow added a commit that referenced this pull request Aug 22, 2026
…ifications-pr

* origin/main: (38 commits)
  fix(composer): preserve caret when inserting mentions mid-message (#6531)
  chore(deps): update rust crate async-trait to v0.1.92 (#6094)
  chore(deps): update dependency sonner to v2.0.8 (#6093)
  chore(deps): update rust crate http-body-util to v0.1.4 (#5452)
  chore(deps): update rust crate http to v1.4.2 (#5451)
  chore(deps): update rust crate futures-util to v0.3.33 (#5448)
  chore(deps): update rust crate futures to v0.3.33 (#5445)
  chore(deps): update dependency @tauri-apps/api to v2.11.1 (#5444)
  chore(deps): update ubuntu:24.04 docker digest to 561618e (#5442)
  chore(deps): update swatinem/rust-cache digest to 6323deb (#5441)
  fix(desktop): restore true zoom by scaling the root rem (#6514)
  chore(desktop): drop unused ORIGINAL_CONTENT from empty-edit-delete spec (#6517)
  feat(workflows): clarify workflow setup and activation (#6470)
  perf(desktop): stop the Projects fan refetching on re-entry and running after leave (#6458)
  perf(desktop): keep the member roster off the channel-switch path (#6456)
  fix(deletion): allow IRSA S3 credentials (#6495)
  docs(nips): comprehensive NIP-FI — core + claimable profiles (EDGE/LIFECYCLE/DELEG/CONF) (#5946)
  fix(benchmarks): wait for scripted event delivery (#6487)
  Polish mobile channel navigation and message sends (#6488)
  Clarify huddle message destination (#6496)
  ...

Signed-off-by: Tom Brow <tomb@block.xyz>
wpfleger96 pushed a commit that referenced this pull request Aug 22, 2026
…ake-fix

* origin/main: (33 commits)
  perf(desktop): make the Projects surface render-cheap (#6460)
  refactor(acp): clarify agent prompt sections (#6501)
  Add mobile Huddles voice MVP (#6056)
  feat(desktop-messages): keep agents addressed across messages (#6315)
  fix(desktop): remove Buzz entity link previews (#6512)
  fix(composer): preserve caret when inserting mentions mid-message (#6531)
  chore(deps): update rust crate async-trait to v0.1.92 (#6094)
  chore(deps): update dependency sonner to v2.0.8 (#6093)
  chore(deps): update rust crate http-body-util to v0.1.4 (#5452)
  chore(deps): update rust crate http to v1.4.2 (#5451)
  chore(deps): update rust crate futures-util to v0.3.33 (#5448)
  chore(deps): update rust crate futures to v0.3.33 (#5445)
  chore(deps): update dependency @tauri-apps/api to v2.11.1 (#5444)
  chore(deps): update ubuntu:24.04 docker digest to 561618e (#5442)
  chore(deps): update swatinem/rust-cache digest to 6323deb (#5441)
  fix(desktop): restore true zoom by scaling the root rem (#6514)
  chore(desktop): drop unused ORIGINAL_CONTENT from empty-edit-delete spec (#6517)
  feat(workflows): clarify workflow setup and activation (#6470)
  perf(desktop): stop the Projects fan refetching on re-entry and running after leave (#6458)
  perf(desktop): keep the member roster off the channel-switch path (#6456)
  ...

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
wpfleger96 pushed a commit that referenced this pull request Aug 22, 2026
…ions-sync-fixes

* origin/main: (22 commits)
  Downgrade mobile Huddles to audio protocol v2 (#6558)
  perf(desktop): make the Projects surface render-cheap (#6460)
  refactor(acp): clarify agent prompt sections (#6501)
  Add mobile Huddles voice MVP (#6056)
  feat(desktop-messages): keep agents addressed across messages (#6315)
  fix(desktop): remove Buzz entity link previews (#6512)
  fix(composer): preserve caret when inserting mentions mid-message (#6531)
  chore(deps): update rust crate async-trait to v0.1.92 (#6094)
  chore(deps): update dependency sonner to v2.0.8 (#6093)
  chore(deps): update rust crate http-body-util to v0.1.4 (#5452)
  chore(deps): update rust crate http to v1.4.2 (#5451)
  chore(deps): update rust crate futures-util to v0.3.33 (#5448)
  chore(deps): update rust crate futures to v0.3.33 (#5445)
  chore(deps): update dependency @tauri-apps/api to v2.11.1 (#5444)
  chore(deps): update ubuntu:24.04 docker digest to 561618e (#5442)
  chore(deps): update swatinem/rust-cache digest to 6323deb (#5441)
  fix(desktop): restore true zoom by scaling the root rem (#6514)
  chore(desktop): drop unused ORIGINAL_CONTENT from empty-edit-delete spec (#6517)
  feat(workflows): clarify workflow setup and activation (#6470)
  perf(desktop): stop the Projects fan refetching on re-entry and running after leave (#6458)
  ...

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
BradGroux pushed a commit to BradGroux/buzz that referenced this pull request Aug 23, 2026
## Summary

Follow-up to block#5644. Cmd +/- had become a text-only zoom: type scaled
while rem-based padding, gaps, widths, avatars, and controls stayed
frozen, which produced cramped layouts (see [#buzz-frontend
thread](buzz://message?channel=a410ffde-c61f-416a-96e0-c296b5f5ecc9&id=1a758115cf07b00c097f6e988553908c045165325a57637519cfa7ed9c9accec)).

Root cause: block#5644 introduced a virtual typography rem so the **Font
size** preference could change text without moving layout — a good
decoupling — but it also routed **Cmd +/- zoom** through that same
px-valued token and pinned the real root at 16px. One decision ("freeze
layout") was applied to two dials that shouldn't share it.

This PR gives each dial one owner and lets CSS compose them:

| Control | Changes | How |
|---|---|---|
| **Cmd +/- zoom** | Everything — true zoom | Scales the real `<html>`
font-size again (`useWebviewZoomShortcuts`) |
| **Font size preference** | Text only | Sets `data-font-size`;
`typography.css` maps it to a unitless `--buzz-type-scale`, mirroring
how density already works |

`--buzz-type-rem` becomes `calc(1rem * var(--buzz-type-scale))` —
rem-relative, so it rides on zoom automatically. Resulting text px = `16
× zoom × scale × token-ratio`. The 13 / 14 / 15px conversation contract
is unchanged at default zoom. Density and the type ramp from block#5644 are
untouched.

The preference module no longer does px math or knows about zoom; the
zoom hook no longer imports the preference module. Net deletion in
production code.

## Validation

- `pnpm test` — 5,308 desktop unit tests
- `pnpm check:px-text`, `tsc --noEmit`, biome
- Playwright: `top-chrome-zoom-clearance.spec.ts` (native-chrome
clearance stays fixed under root zoom),
`inbox-refactor-screenshots.spec.ts` (zoomed row padding now asserts
`4.4px` instead of the frozen `4px`), and both `profile.spec.ts` zoom
tests (composed zoom × preference, cross-window storage reset)
- Before/after screenshots at 140% zoom in the comment below

---------

Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to 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.

2 participants