Mail Morph: the reader is translucent; list rows show through it #1373

Closed
opened 2026-10-07 14:31:32 +00:00 by kayg · 4 comments
Owner

Owner screenshot 2026-10-07 (production 0419170fe, Mail Morph layout, dark): the open message reader is translucent; message list rows behind it show through its surface ("Claude Start...", "Update...", "[Action ...", "OAuth cli..." visible over the message body and header). Expected: the reader surface uses the shared opaque-enough reading surface (glass edge allowed, but body content sits on a surface where nothing behind is legible), per DESIGN glass rules and the #1189/#1208 material tokens. Fix in the shared material token or the Morph reader, not with a local override.

Owner screenshot 2026-10-07 (production 0419170fe, Mail Morph layout, dark): the open message reader is translucent; message list rows behind it show through its surface ("Claude Start...", "Update...", "[Action ...", "OAuth cli..." visible over the message body and header). Expected: the reader surface uses the shared opaque-enough reading surface (glass edge allowed, but body content sits on a surface where nothing behind is legible), per DESIGN glass rules and the #1189/#1208 material tokens. Fix in the shared material token or the Morph reader, not with a local override.
Author
Owner

Starting #1373 on job/mailglass-1373, based on dev at 0419170fe456b58614befcb12d9c9d255118f1c0. I am tracing the Mail Morph reader's shared surface role and checking the Mail split and Search preview consumers before making a token-level or role-selection fix.

Starting #1373 on `job/mailglass-1373`, based on `dev` at `0419170fe456b58614befcb12d9c9d255118f1c0`. I am tracing the Mail Morph reader's shared surface role and checking the Mail split and Search preview consumers before making a token-level or role-selection fix.
Author
Owner

Root cause found in the shared paint chain: MailMorphCard.svelte paints its content plane with .morph-material.glass-overlay, while tokens.css sets --glass-overlay-bg from --glass-material-alpha (30% in dark themes, 20% in light). The 16 px blur softens the busy Mail list but does not hide its row text. MailReaderContent has no fill of its own. The shared glass layer already supports an opaque per-surface --glass-tint: var(--surface) state, used for dense timeline popovers; I am checking how to make that role reusable for Morph without changing the transparent glass used by Search's outer palette.

Root cause found in the shared paint chain: `MailMorphCard.svelte` paints its content plane with `.morph-material.glass-overlay`, while `tokens.css` sets `--glass-overlay-bg` from `--glass-material-alpha` (30% in dark themes, 20% in light). The 16 px blur softens the busy Mail list but does not hide its row text. `MailReaderContent` has no fill of its own. The shared glass layer already supports an opaque per-surface `--glass-tint: var(--surface)` state, used for dense timeline popovers; I am checking how to make that role reusable for Morph without changing the transparent glass used by Search's outer palette.
Author
Owner

#1373 final report

Branch: job/mailglass-1373
Base: dev at 0419170fe456b58614befcb12d9c9d255118f1c0; git fetch origin && git merge origin/dev returned Already up to date.
Head: 3457d4c9c2d253ea8fab6615a41a3077725b5328

What changed

Added the shared .glass-opaque material modifier in packages/ui/src/tokens.css. It selects the theme's --surface fill and keeps the shared glass edge, shadow and filter. Mail Morph uses this role. FloatingSurface's existing opaque prop now reuses the same role. Search preview keeps its translucent material.

The production screenshot run seeded 36 separate Inbox rows through the real API in a disposable Index. Morph checks prove the surface fill equals the current theme surface while retaining the edge and filter, and Split checks prove the reader and busy list do not overlap. The screenshot context reports the macOS platform.

Files

  • packages/ui/src/tokens.css
  • packages/ui/src/components/menu/FloatingSurface.svelte
  • apps/web/src/lib/mail/MailMorphCard.svelte
  • apps/web/src/lib/themes.test.ts
  • apps/web/e2e/mailglass-1373.mjs
  • tests/adversarial/mail_screenshot_fixture.py
  • docs/DESIGN.md
  • contracts/perf/exceptions.json
  • contracts/perf/adoption-1058.json

UX gaps closed

  • Mail Morph no longer leaves dense Inbox text legible through the reader surface.
  • Mail Split, Morph and phone push layouts were captured. Pointer and Enter openings were captured, and the reader edge crops are 4x.
  • The shared FloatingSurface opaque path and Mail Morph use one token role. Search preview does not opt into it.

UX gaps left

  • The focused Search review passed its Mail preview assertions and captured search-1123-results-mail-1440-mono.png. It then stopped on the existing Calendar task-preview assertion in apps/web/e2e/search.mjs:1730: task preview did not render its Calendar card. I left that expectation unchanged. The later Search theme and viewport cases did not run.
  • bun run check reports one existing empty CSS ruleset warning in packages/ui/src/components/calendar/AttachmentDeck.svelte:1055; it reports zero errors.

Decisions

  • DESIGN §34 did not define a reader-specific opaque treatment. I added a shared modifier that binds to --surface instead of changing the global translucent overlay values. That keeps the choice local to surfaces with live text behind them and leaves Search translucent in all themes.
  • The parser's exact material.local-filter hashes include the full parsed tokens.css syntax. I refreshed the 18 corresponding hashes in the exception ledger and #1058 checkpoint. Their symbols, limits and counts did not change.
  • Expected cost is one shared CSS custom-property resolution per reader surface; the change adds no per-item work, JavaScript work or layout reads.

Gates

cd apps/web && bun run build — exit code 0:

Compressed 917 static variants; saved 21487960 bytes.

cd apps/web && bun run check — exit code 0:

PASS production dependency licences: 431 locked package releases across apps/web, apps/docs and packages
perf-lint: PASS; 0 violations; 22544 scoped exceptions
Glass alpha, blur and backdrop-filter roles use packages/ui/src/tokens.css.
Text sizes and UI shape values use shared role tokens.
Keyboard focus rings use the shared focus tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
svelte-check found 0 errors and 1 warning in 1 file
Warn: Do not use empty rulesets (css)
  }
  .attachment-row:focus {

cd apps/web && bun run test — exit code 0:

 Test Files  287 passed (287)
      Tests  2037 passed (2037)
   Duration  327.30s (transform 32%, environment 27%, import 20%, tests 15%, setup 6%)

Screenshot attachments

Morph

Split

4x reader edge crops

Phone, interaction and Search preview

## #1373 final report **Branch:** `job/mailglass-1373` **Base:** `dev` at `0419170fe456b58614befcb12d9c9d255118f1c0`; `git fetch origin && git merge origin/dev` returned `Already up to date.` **Head:** `3457d4c9c2d253ea8fab6615a41a3077725b5328` ### What changed Added the shared `.glass-opaque` material modifier in `packages/ui/src/tokens.css`. It selects the theme's `--surface` fill and keeps the shared glass edge, shadow and filter. Mail Morph uses this role. `FloatingSurface`'s existing `opaque` prop now reuses the same role. Search preview keeps its translucent material. The production screenshot run seeded 36 separate Inbox rows through the real API in a disposable Index. Morph checks prove the surface fill equals the current theme surface while retaining the edge and filter, and Split checks prove the reader and busy list do not overlap. The screenshot context reports the macOS platform. ### Files - `packages/ui/src/tokens.css` - `packages/ui/src/components/menu/FloatingSurface.svelte` - `apps/web/src/lib/mail/MailMorphCard.svelte` - `apps/web/src/lib/themes.test.ts` - `apps/web/e2e/mailglass-1373.mjs` - `tests/adversarial/mail_screenshot_fixture.py` - `docs/DESIGN.md` - `contracts/perf/exceptions.json` - `contracts/perf/adoption-1058.json` ### UX gaps closed - Mail Morph no longer leaves dense Inbox text legible through the reader surface. - Mail Split, Morph and phone push layouts were captured. Pointer and Enter openings were captured, and the reader edge crops are 4x. - The shared `FloatingSurface` opaque path and Mail Morph use one token role. Search preview does not opt into it. ### UX gaps left - The focused Search review passed its Mail preview assertions and captured `search-1123-results-mail-1440-mono.png`. It then stopped on the existing Calendar task-preview assertion in `apps/web/e2e/search.mjs:1730`: `task preview did not render its Calendar card`. I left that expectation unchanged. The later Search theme and viewport cases did not run. - `bun run check` reports one existing empty CSS ruleset warning in `packages/ui/src/components/calendar/AttachmentDeck.svelte:1055`; it reports zero errors. ### Decisions - DESIGN §34 did not define a reader-specific opaque treatment. I added a shared modifier that binds to `--surface` instead of changing the global translucent overlay values. That keeps the choice local to surfaces with live text behind them and leaves Search translucent in all themes. - The parser's exact `material.local-filter` hashes include the full parsed `tokens.css` syntax. I refreshed the 18 corresponding hashes in the exception ledger and #1058 checkpoint. Their symbols, limits and counts did not change. - Expected cost is one shared CSS custom-property resolution per reader surface; the change adds no per-item work, JavaScript work or layout reads. ### Gates `cd apps/web && bun run build` — exit code 0: ```text Compressed 917 static variants; saved 21487960 bytes. ``` `cd apps/web && bun run check` — exit code 0: ```text PASS production dependency licences: 431 locked package releases across apps/web, apps/docs and packages perf-lint: PASS; 0 violations; 22544 scoped exceptions Glass alpha, blur and backdrop-filter roles use packages/ui/src/tokens.css. Text sizes and UI shape values use shared role tokens. Keyboard focus rings use the shared focus tokens. UI transitions and animation options use shared motion tokens or documented exceptions. svelte-check found 0 errors and 1 warning in 1 file ``` ```text Warn: Do not use empty rulesets (css) } .attachment-row:focus { ``` `cd apps/web && bun run test` — exit code 0: ```text Test Files 287 passed (287) Tests 2037 passed (2037) Duration 327.30s (transform 32%, environment 27%, import 20%, tests 15%, setup 6%) ``` ### Screenshot attachments **Morph** - [Paper light, desktop 1440×900](https://git.kayg.org/attachments/0a51a62a-08f0-4578-a3df-f1757237c8b6) - [Paper light, tablet 820×1000](https://git.kayg.org/attachments/b4712d90-217e-4e42-83e3-3a71163539fb) - [Tokyo Night dark, desktop 1440×900](https://git.kayg.org/attachments/7e2fc098-0a8e-4cf2-b348-1aae75d5036c) - [Tokyo Night dark, tablet 820×1000](https://git.kayg.org/attachments/e0a9e385-ff28-4c5e-a291-7cc3f60e896c) **Split** - [Paper light, desktop 1440×900](https://git.kayg.org/attachments/5f180a5e-64c9-4eca-b6ad-654a21145f9a) - [Paper light, tablet 820×1000](https://git.kayg.org/attachments/adfad5fd-49ba-4250-9110-2aac47c0696b) - [Tokyo Night dark, desktop 1440×900](https://git.kayg.org/attachments/4dc25069-3a55-4f14-88eb-5bfc785f6b4a) - [Tokyo Night dark, tablet 820×1000](https://git.kayg.org/attachments/8f4b99c6-b5c6-4567-86ac-1dc2322373e5) **4x reader edge crops** - [Paper light, desktop top](https://git.kayg.org/attachments/100adbd6-497d-43b8-a802-68deaf1ca19c) · [bottom](https://git.kayg.org/attachments/f388225f-477e-4c2c-b336-7345ebd51728) - [Paper light, tablet top](https://git.kayg.org/attachments/5be7137d-f3bf-4b03-83a3-549efb2ddaec) · [bottom](https://git.kayg.org/attachments/c24bba33-720c-4731-b8b4-7e363ceed915) - [Tokyo Night dark, desktop top](https://git.kayg.org/attachments/d5c872ae-13d5-43c7-af3a-63c77530c156) · [bottom](https://git.kayg.org/attachments/1db850c3-d827-4db0-ae03-7b0c6126d79f) - [Tokyo Night dark, tablet top](https://git.kayg.org/attachments/af2f3e93-d113-47cf-bbdf-db2be2dcc075) · [bottom](https://git.kayg.org/attachments/fb20fc8c-80bf-495f-bed2-c808f08efb61) **Phone, interaction and Search preview** - [Paper light, phone 390×844](https://git.kayg.org/attachments/f4f8f2cf-c28e-490d-82a0-0afc43fd09ee) - [Tokyo Night dark, phone 390×844](https://git.kayg.org/attachments/a143565d-f2f1-42eb-b550-1eea2b3204c6) - [Tokyo Night dark, after pointer open](https://git.kayg.org/attachments/0ca6292a-e3b9-491f-9f59-c9195f3c8909) - [Tokyo Night dark, after Enter open](https://git.kayg.org/attachments/72b78c11-6c71-4420-a5f2-7268de46ab42) - [Search Mail preview, 1440×900, mono light](https://git.kayg.org/attachments/1b3aa5f9-99e9-409d-ad42-12292e7adbe6)
Author
Owner

Deployed to calternal.cloud in merge round 34 (d395b6e25), 2026-10-08 05:03 IST. Backup: /srv/calternal/backup-pre-m34-20261008-0132. Production healthy after 36 s; served build matches.

Deployed to calternal.cloud in merge round 34 (d395b6e25), 2026-10-08 05:03 IST. Backup: /srv/calternal/backup-pre-m34-20261008-0132. Production healthy after 36 s; served build matches.
kayg closed this issue 2026-10-07 23:54:43 +00:00
Commenting is not possible because the repository is archived.
No labels
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal-v0#1373
No description provided.