BUG: menus inside Settings/overlays have no blur (nested backdrop root); two rows highlighted #246

Closed
opened 2026-09-27 16:56:14 +00:00 by kayg · 27 comments
Owner

Owner report from calternal.cloud (2026-09-27, Settings → Appearance → Dark theme dropdown, dark mode): "somehow this dropdown does not have blur?"

Observed (owner screenshot): the theme menu has the glass tint but NO backdrop blur: the controls behind it (System/Light/Dark pills, the Mono dropdown, Theme/Colour/Photo, the toggle) are crisp and legible through the menu, overlapping the menu text. Also TWO rows are highlighted at once ("Midnight" at the top and "Rosé Pine"), i.e. hover and keyboard-active states both paint the full highlight.

Likely cause: the menu renders inside the Settings overlay, which has its own backdrop-filter. That ancestor is a backdrop root, so the menu's backdrop-filter only samples the overlay's own layer group (same class of bug as #219's sheet header). Fix at the root:

  • Portal every floating menu/popover/listbox (the shared FloatingSurface/menu primitive) to a top-level layer outside any backdrop-filter ancestor, keeping anchoring, focus management and deep-link behaviour. Audit every surface that can open inside Settings, sheets, the search palette, the composer, Quick Look.
  • One highlight at a time: keyboard-active and pointer-hover share one "active" state (pointer move sets it), as in native menus.
  • Regression: a pixel check (like #219/#239) that text behind an open menu is blurred (local contrast under the menu < 30 % of uncovered), for a menu opened inside Settings, in light and dark; plus a unit test that only one row carries the active state.
    Screenshots before/after, desktop 1440 and phone 390 (attach, do not commit).
Owner report from calternal.cloud (2026-09-27, Settings → Appearance → Dark theme dropdown, dark mode): "somehow this dropdown does not have blur?" Observed (owner screenshot): the theme menu has the glass tint but NO backdrop blur: the controls behind it (System/Light/Dark pills, the Mono dropdown, Theme/Colour/Photo, the toggle) are crisp and legible through the menu, overlapping the menu text. Also TWO rows are highlighted at once ("Midnight" at the top and "Rosé Pine"), i.e. hover and keyboard-active states both paint the full highlight. Likely cause: the menu renders inside the Settings overlay, which has its own backdrop-filter. That ancestor is a backdrop root, so the menu's backdrop-filter only samples the overlay's own layer group (same class of bug as #219's sheet header). Fix at the root: - Portal every floating menu/popover/listbox (the shared FloatingSurface/menu primitive) to a top-level layer outside any backdrop-filter ancestor, keeping anchoring, focus management and deep-link behaviour. Audit every surface that can open inside Settings, sheets, the search palette, the composer, Quick Look. - One highlight at a time: keyboard-active and pointer-hover share one "active" state (pointer move sets it), as in native menus. - Regression: a pixel check (like #219/#239) that text behind an open menu is blurred (local contrast under the menu < 30 % of uncovered), for a menu opened inside Settings, in light and dark; plus a unit test that only one row carries the active state. Screenshots before/after, desktop 1440 and phone 390 (attach, do not commit).
Author
Owner

Owner (2026-09-27): 'focus on code reuse please'. The nested-backdrop-root blur bug is ONE root cause across #219 (sheet header), #239 (toasts) and #246 (menus). #246 owns the single shared fix: move apps/web/src/lib/actions/portal.ts into packages/ui, build it into the shared surfaces (OverlaySurface, PopoverSurface, FloatingSurface, menu/, toaster) so every floating surface renders in one top-level layer outside any backdrop-filter ancestor, and move each sheet/overlay's own glass onto a background layer that is not an ancestor of its content (so ProgressiveBlur and nested menus inside a sheet sample the real content). Remove ad-hoc per-component portal uses. #219 and #239 must NOT build their own workaround; they reuse #246. Merge order: #246 first.

Owner (2026-09-27): 'focus on code reuse please'. The nested-backdrop-root blur bug is ONE root cause across #219 (sheet header), #239 (toasts) and #246 (menus). #246 owns the single shared fix: move apps/web/src/lib/actions/portal.ts into packages/ui, build it into the shared surfaces (OverlaySurface, PopoverSurface, FloatingSurface, menu/, toaster) so every floating surface renders in one top-level layer outside any backdrop-filter ancestor, and move each sheet/overlay's own glass onto a background layer that is not an ancestor of its content (so ProgressiveBlur and nested menus inside a sheet sample the real content). Remove ad-hoc per-component portal uses. #219 and #239 must NOT build their own workaround; they reuse #246. Merge order: #246 first.
Author
Owner

Starting in job/menu-blur at base dev 200d171ea3. I’m tracing the shared surface and portal paths first, then I’ll add the light/dark blur regression and move the fix into packages/ui.

Starting in job/menu-blur at base dev 200d171ea35e1de53ad56e6c1d7fcef95881f0a3. I’m tracing the shared surface and portal paths first, then I’ll add the light/dark blur regression and move the fix into packages/ui.
Author
Owner

Reproduced the row-state bug with the new ThemePicker regression: keeps pointer and keyboard activity on one menu row fails after pointer entry (2 tests pass, 1 fails; the active-row query is empty). The shared MenuItem still styles :hover and :focus-visible independently. Code tracing confirms OverlaySurface currently keeps its scrim and content under .surface { contain: paint }, so the Settings scrim cannot blur page text outside that backdrop root. I’m testing the shared layer against those boundaries.

Reproduced the row-state bug with the new ThemePicker regression: `keeps pointer and keyboard activity on one menu row` fails after pointer entry (2 tests pass, 1 fails; the active-row query is empty). The shared MenuItem still styles `:hover` and `:focus-visible` independently. Code tracing confirms OverlaySurface currently keeps its scrim and content under `.surface { contain: paint }`, so the Settings scrim cannot blur page text outside that backdrop root. I’m testing the shared layer against those boundaries.
Author
Owner

A production-browser pixel probe showed the Settings menu computed backdrop-filter: none after it moved into the shared top-level layer. The remaining suppressor was html.scrim-open body * { backdrop-filter: none !important; }, which also reached into that layer. I narrowed suppression to app content outside the shared layer; sheet-local progressive bands and floating menus can now blur their bounded pixels. The toaster keeps its explicit tint-only style above a scrim.

A production-browser pixel probe showed the Settings menu computed `backdrop-filter: none` after it moved into the shared top-level layer. The remaining suppressor was `html.scrim-open body * { backdrop-filter: none !important; }`, which also reached into that layer. I narrowed suppression to app content outside the shared layer; sheet-local progressive bands and floating menus can now blur their bounded pixels. The toaster keeps its explicit tint-only style above a scrim.
Author
Owner

Finding: the old html.scrim-open body * filter suppression also disabled a nested menu's own backdrop filter. It now suspends app content outside the shared body-level overlay layer, while the overlay background is a sibling of sheet content. The production pixel probe passed in light and dark themes: Settings menu text contrast ratios were 0.06 and 0.03; toast ratios were 0.03 and 0.03; ProgressiveBlur inside the mobile Settings sheet was 0.13 in both themes. Every result is below the 0.30 threshold. Screenshots are in the ignored artifacts/menu-blur/ directory pending issue attachment.

Finding: the old `html.scrim-open body *` filter suppression also disabled a nested menu's own backdrop filter. It now suspends app content outside the shared body-level overlay layer, while the overlay background is a sibling of sheet content. The production pixel probe passed in light and dark themes: Settings menu text contrast ratios were 0.06 and 0.03; toast ratios were 0.03 and 0.03; ProgressiveBlur inside the mobile Settings sheet was 0.13 in both themes. Every result is below the 0.30 threshold. Screenshots are in the ignored `artifacts/menu-blur/` directory pending issue attachment.
Author
Owner

The post-merge production captures are attached to this issue: Settings menu and toast at 1440px, plus Settings sheet at 390px, in light and dark themes. The merged production build passed the pixel probe in all six cases. I merged dev once and resolved its menu changes together with the active-row fix. Web check reports 0 errors and 0 warnings; all 82 web test files (595 tests) pass. Cargo gates are running now.

The post-merge production captures are attached to this issue: Settings menu and toast at 1440px, plus Settings sheet at 390px, in light and dark themes. The merged production build passed the pixel probe in all six cases. I merged `dev` once and resolved its menu changes together with the active-row fix. Web check reports 0 errors and 0 warnings; all 82 web test files (595 tests) pass. Cargo gates are running now.
Author
Owner

One non-SLOW finding from the corrected adversarial round: tests/adversarial/authz_matrix.py expected 403 for unauthenticated PUT /api/v1/admin/config, but received 422. In crates/calternal-server/src/wire.rs, put_config extracts Json<InstanceConfig> before calling admin(...), so Axum rejects the probe's malformed body before the admin authorization check. This does not modify data; it reveals a status-ordering inconsistency on an admin endpoint. This code came from the merged dev changes and is outside the menu blur UI scope, so I am recording it for server follow-up rather than changing server behavior in this job.

One non-SLOW finding from the corrected adversarial round: `tests/adversarial/authz_matrix.py` expected `403` for unauthenticated `PUT /api/v1/admin/config`, but received `422`. In `crates/calternal-server/src/wire.rs`, `put_config` extracts `Json<InstanceConfig>` before calling `admin(...)`, so Axum rejects the probe's malformed body before the admin authorization check. This does not modify data; it reveals a status-ordering inconsistency on an admin endpoint. This code came from the merged `dev` changes and is outside the menu blur UI scope, so I am recording it for server follow-up rather than changing server behavior in this job.
Author
Owner

A second non-SLOW finding from the corrected adversarial round: tests/adversarial/editor.mjs passed the 500-step undo/redo storm checks and saved the generated block anchors in 330 ms, then reported waitForFunction: Timeout 15000ms exceeded while checking copied heading/block links. The script does not include a stack in its finding, so it is unclear which of the heading or block viewport-restoration checks timed out. This is from the merged editor/deep-link behavior and is outside the menu blur UI scope; it needs a focused follow-up.

A second non-SLOW finding from the corrected adversarial round: `tests/adversarial/editor.mjs` passed the 500-step undo/redo storm checks and saved the generated block anchors in 330 ms, then reported `waitForFunction: Timeout 15000ms exceeded` while checking copied heading/block links. The script does not include a stack in its finding, so it is unclear which of the heading or block viewport-restoration checks timed out. This is from the merged editor/deep-link behavior and is outside the menu blur UI scope; it needs a focused follow-up.
Author
Owner

Another non-SLOW finding from the corrected adversarial round: tests/adversarial/attack.py sent REPORT with an empty sync token to the DAV home. The initial sync request returned no response before its 30-second timeout; the probe then observed SLOW responses for the invalid-token check. The initial-sync timeout is reported by the probe as NO RESPONSE, not SLOW, and may indicate a DAV sync stall under the current shared-host load. The DAV implementation is outside this UI job, so I am recording the evidence for server follow-up.

Another non-SLOW finding from the corrected adversarial round: `tests/adversarial/attack.py` sent `REPORT` with an empty sync token to the DAV home. The initial sync request returned no response before its 30-second timeout; the probe then observed `SLOW` responses for the invalid-token check. The initial-sync timeout is reported by the probe as `NO RESPONSE`, not `SLOW`, and may indicate a DAV sync stall under the current shared-host load. The DAV implementation is outside this UI job, so I am recording the evidence for server follow-up.
Author
Owner

A further non-SLOW finding from the corrected adversarial round: tests/adversarial/attack.py created a calendar Event linked to a Log entry, but the request returned no response before its 30-second timeout (calendar Event from Log). The preceding linked Note and Log creates completed, with reported latencies of 9.8s and 5.8s; this Event request did not. Calendar behavior is outside the menu blur UI scope, so I am recording this for calendar/server follow-up. The probe is still running; this is not the final finding count.

A further non-SLOW finding from the corrected adversarial round: `tests/adversarial/attack.py` created a calendar Event linked to a Log entry, but the request returned no response before its 30-second timeout (`calendar Event from Log`). The preceding linked Note and Log creates completed, with reported latencies of 9.8s and 5.8s; this Event request did not. Calendar behavior is outside the menu blur UI scope, so I am recording this for calendar/server follow-up. The probe is still running; this is not the final finding count.
Author
Owner

The ongoing adversarial round produced another non-SLOW no-response: tests/adversarial/attack.py timed out on the first write for Calendar Log occurrence 1 (calendar Log this occurrence 1 first write). Neighboring occurrence status and Journal-link reads completed but were tagged SLOW. This is a write timeout under the Calendar Log retry/occurrence probe; it is outside this UI job and needs server follow-up. The probe is still in progress.

The ongoing adversarial round produced another non-SLOW no-response: `tests/adversarial/attack.py` timed out on the first write for Calendar Log occurrence 1 (`calendar Log this occurrence 1 first write`). Neighboring occurrence status and Journal-link reads completed but were tagged `SLOW`. This is a write timeout under the Calendar Log retry/occurrence probe; it is outside this UI job and needs server follow-up. The probe is still in progress.
Author
Owner

The adversarial round found a potential Journal write stall: tests/adversarial/attack.py issued 24 parallel PATCH requests with the same If-Match value. The probe expected one 200 and twenty-three 412 responses, but all 24 requests timed out with NO RESPONSE. Four other adversarial run.sh processes were active in sibling worktrees on the shared build host at the time, so host saturation may contribute. The result is not marked SLOW; it needs follow-up before merge because the probe could not verify the conditional-write collision behavior. Journal/server behavior is outside the menu blur UI scope, so this is a filed finding rather than a code change in this job.

The adversarial round found a potential Journal write stall: `tests/adversarial/attack.py` issued 24 parallel `PATCH` requests with the same `If-Match` value. The probe expected one `200` and twenty-three `412` responses, but all 24 requests timed out with `NO RESPONSE`. Four other adversarial `run.sh` processes were active in sibling worktrees on the shared build host at the time, so host saturation may contribute. The result is not marked `SLOW`; it needs follow-up before merge because the probe could not verify the conditional-write collision behavior. Journal/server behavior is outside the menu blur UI scope, so this is a filed finding rather than a code change in this job.
Author
Owner

Follow-up evidence for the Journal write stall: the subsequent GET /api/v1/notes/journal/{date} check, Journal entry after PATCH storm, also returned no response before its 30-second timeout. This keeps the earlier 24-request result unresolved and strengthens the concern that the test left the Journal path unresponsive during the shared-host probe.

Follow-up evidence for the Journal write stall: the subsequent `GET /api/v1/notes/journal/{date}` check, `Journal entry after PATCH storm`, also returned no response before its 30-second timeout. This keeps the earlier 24-request result unresolved and strengthens the concern that the test left the Journal path unresponsive during the shared-host probe.
Author
Owner

Additional Journal-path evidence from the same round: Journal entry Note creation and Journal fix stale line also received no response before the 30-second timeout. Journal delete target create completed with a SLOW 19.6-second response. The Journal probe is still running; these results were observed while four sibling worktrees had adversarial suites active.

Additional Journal-path evidence from the same round: `Journal entry Note creation` and `Journal fix stale line` also received no response before the 30-second timeout. `Journal delete target create` completed with a `SLOW` 19.6-second response. The Journal probe is still running; these results were observed while four sibling worktrees had adversarial suites active.
Author
Owner

Another Journal collision result: Journal DELETE stale condition returned no response before the 30-second timeout, while the current-condition delete completed with 204 in 17.5 seconds (SLOW). This is consistent with the unresolved Journal-path stall under the same concurrent host load; the timeouts are not marked SLOW by the probe.

Another Journal collision result: `Journal DELETE stale condition` returned no response before the 30-second timeout, while the current-condition delete completed with `204` in 17.5 seconds (`SLOW`). This is consistent with the unresolved Journal-path stall under the same concurrent host load; the timeouts are not marked `SLOW` by the probe.
Author
Owner

The single adversarial round reached its 30-minute timeout (timeout exit 124). Before it ended, the local server stopped accepting requests: the editor proxy returned 502 local adversarial server is unavailable, and subsequent Photos probes received ECONNRESET followed by ECONNREFUSED. The server log was removed by the probe cleanup, and four sibling worktrees had adversarial suites active, so I cannot attribute the stop to a code defect versus shared-host resource pressure. This is a potential crash/DoS finding that needs triage before merge. The probe did not reach the media and attack2 sections.

The single adversarial round reached its 30-minute timeout (`timeout` exit 124). Before it ended, the local server stopped accepting requests: the editor proxy returned `502 local adversarial server is unavailable`, and subsequent Photos probes received `ECONNRESET` followed by `ECONNREFUSED`. The server log was removed by the probe cleanup, and four sibling worktrees had adversarial suites active, so I cannot attribute the stop to a code defect versus shared-host resource pressure. This is a potential crash/DoS finding that needs triage before merge. The probe did not reach the media and attack2 sections.
Author
Owner

#246 implementation report

Built and pushed the shared floating-surface portal, sibling glass layers for sheets and overlays, removal of component-local portal wrappers, chart-tooltip viewport positioning, and the single active menu row. The production E2E regression covers Settings menu, toast over text, and ProgressiveBlur in the Settings sheet in light and dark modes. Screenshots and pixel measurements are attached in the earlier issue comment. No dependencies were added.

Files: packages/ui/src/actions/portal.ts, packages/ui/src/index.ts, shared UI overlay/menu components, affected web sheets/dialogs/sidebar/toaster and chart tooltip, apps/web/e2e/menu-blur.mjs, related web tests, and docs/DESIGN.md.

Commits on job/menu-blur: b3fa5c7b (active menu row), 1e7448b0 (shared portal and glass layers), and merge commit 9dfeacc2b6015af55dc728fb89fb55700c1523f2. The branch was pushed; remote and local HEAD match.

Gate output:

  • cargo fmt --check: exit code 0; no output.
  • cargo clippy --all-targets -- -D warnings: Finished 'dev' profile [unoptimized + debuginfo] target(s) in 19m 44s.
  • cargo test: exit code 0. Final doc-test output: test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s.
  • bun run check: svelte-check found 0 errors and 0 warnings.
  • bun run test: Test Files 82 passed (82); Tests 595 passed (595).
  • Production build: ✓ built in 58.33s; Wrote site to "build"; ✔ done.
  • Production pixel probe passed in all six cases. Menu contrast ratios were 0.06 light / 0.03 dark; toast 0.03 / 0.03; sheet ProgressiveBlur 0.13 / 0.13 (probe limit 0.30).

Adversarial round: ran once against a server built from merged HEAD, with a 30-minute limit. It ended with exit 124 before the media and attack2 sections. The probe reported non-SLOW timeouts on DAV initial sync, Calendar Event from Log, and Journal writes. All 24 colliding Journal PATCH requests timed out; the following Journal GET also timed out. Later the local server became unavailable to the proxy and Photos probes received 502/connection-refused responses. Four sibling worktrees also had adversarial suites active. The probe cleaned its work directory, so the server log is unavailable; shared-host pressure may explain the server stop, but the result needs triage as a potential crash/DoS before merge. The admin-config 422 and editor deep-link timeout are also recorded in the earlier finding comments. Slow-only response findings were treated as host load.

Decisions for owner confirmation, where DESIGN.md did not settle the implementation: use one body-level display:contents portal root; render sheet/overlay glass as a sibling behind content; suspend a parent menu's local blur while a child menu is active; let the broad page scrim blur the page, bounded floating surfaces/sheet bands keep local blur, and keep toast tint-only over the scrim; translate the chart tooltip from chart-local to viewport coordinates after portalling; keep active-row state at Menu level.

Known gap: the adversarial round hit its time limit and did not reach the final media and attack2 probes. No baseline screenshot set was attached; the six production after screenshots are attached.

#246 implementation report Built and pushed the shared floating-surface portal, sibling glass layers for sheets and overlays, removal of component-local portal wrappers, chart-tooltip viewport positioning, and the single active menu row. The production E2E regression covers Settings menu, toast over text, and ProgressiveBlur in the Settings sheet in light and dark modes. Screenshots and pixel measurements are attached in the earlier issue comment. No dependencies were added. Files: `packages/ui/src/actions/portal.ts`, `packages/ui/src/index.ts`, shared UI overlay/menu components, affected web sheets/dialogs/sidebar/toaster and chart tooltip, `apps/web/e2e/menu-blur.mjs`, related web tests, and `docs/DESIGN.md`. Commits on `job/menu-blur`: `b3fa5c7b` (active menu row), `1e7448b0` (shared portal and glass layers), and merge commit `9dfeacc2b6015af55dc728fb89fb55700c1523f2`. The branch was pushed; remote and local HEAD match. Gate output: - `cargo fmt --check`: exit code 0; no output. - `cargo clippy --all-targets -- -D warnings`: `Finished 'dev' profile [unoptimized + debuginfo] target(s) in 19m 44s`. - `cargo test`: exit code 0. Final doc-test output: `test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s`. - `bun run check`: `svelte-check found 0 errors and 0 warnings`. - `bun run test`: `Test Files 82 passed (82)`; `Tests 595 passed (595)`. - Production build: `✓ built in 58.33s`; `Wrote site to "build"`; `✔ done`. - Production pixel probe passed in all six cases. Menu contrast ratios were 0.06 light / 0.03 dark; toast 0.03 / 0.03; sheet ProgressiveBlur 0.13 / 0.13 (probe limit 0.30). Adversarial round: ran once against a server built from merged HEAD, with a 30-minute limit. It ended with exit 124 before the media and attack2 sections. The probe reported non-SLOW timeouts on DAV initial sync, Calendar Event from Log, and Journal writes. All 24 colliding Journal PATCH requests timed out; the following Journal GET also timed out. Later the local server became unavailable to the proxy and Photos probes received 502/connection-refused responses. Four sibling worktrees also had adversarial suites active. The probe cleaned its work directory, so the server log is unavailable; shared-host pressure may explain the server stop, but the result needs triage as a potential crash/DoS before merge. The admin-config 422 and editor deep-link timeout are also recorded in the earlier finding comments. Slow-only response findings were treated as host load. Decisions for owner confirmation, where DESIGN.md did not settle the implementation: use one body-level `display:contents` portal root; render sheet/overlay glass as a sibling behind content; suspend a parent menu's local blur while a child menu is active; let the broad page scrim blur the page, bounded floating surfaces/sheet bands keep local blur, and keep toast tint-only over the scrim; translate the chart tooltip from chart-local to viewport coordinates after portalling; keep active-row state at Menu level. Known gap: the adversarial round hit its time limit and did not reach the final media and attack2 probes. No baseline screenshot set was attached; the six production after screenshots are attached.
Author
Owner

Started work on job/menu-blur at 9dfeacc2b6015af55dc728fb89fb55700c1523f2; branch base SHA: 19e65b2243453ac53ee1c374b74f499d708dda1b.

Started work on `job/menu-blur` at `9dfeacc2b6015af55dc728fb89fb55700c1523f2`; branch base SHA: `19e65b2243453ac53ee1c374b74f499d708dda1b`.
Author
Owner

The adversarial auth matrix reported one non-load result: PUT /api/v1/admin/config as a standard User returned 422; the matrix expected 403. This was the only auth-matrix failure in the 234-operation × 4-identity pass. It matches the existing follow-up in #268. No change was made under #246.

The adversarial auth matrix reported one non-load result: `PUT /api/v1/admin/config` as a standard User returned `422`; the matrix expected `403`. This was the only auth-matrix failure in the 234-operation × 4-identity pass. It matches the existing follow-up in #268. No change was made under #246.
Author
Owner

The same adversarial round also reported DAV initial sync: NO RESPONSE (timed out). The next invalid-sync-token probe returned 403 in 7.3s and was classified SLOW. The timeout matches the existing DAV timeout follow-up in #265.

The same adversarial round also reported `DAV initial sync: NO RESPONSE (timed out)`. The next invalid-sync-token probe returned `403` in `7.3s` and was classified SLOW. The timeout matches the existing DAV timeout follow-up in #265.
Author
Owner

The adversarial round also reported Calendar Event from Log: NO RESPONSE (timed out). This matches the existing shared-load follow-up in #250. The preceding Calendar Event from Note request returned 201 in 16.0s (SLOW).

The adversarial round also reported `Calendar Event from Log: NO RESPONSE (timed out)`. This matches the existing shared-load follow-up in #250. The preceding Calendar Event from Note request returned `201` in `16.0s` (SLOW).
Author
Owner

The same adversarial round also reported calendar Log this occurrence 0 first write: NO RESPONSE (timed out). This is covered by the existing Journal/Calendar write-load follow-up in #267, which tracks recurring Log occurrence timeouts and identity consistency.

The same adversarial round also reported `calendar Log this occurrence 0 first write: NO RESPONSE (timed out)`. This is covered by the existing Journal/Calendar write-load follow-up in #267, which tracks recurring Log occurrence timeouts and identity consistency.
Author
Owner

The 24-request Journal PATCH storm produced one 200 in 23.2s, seven 412 responses in 23.2–26.3s, and 16 NO RESPONSE (timed out) results. This matches the Journal PATCH storm tracked in #267.

The 24-request Journal PATCH storm produced one `200` in `23.2s`, seven `412` responses in `23.2–26.3s`, and 16 `NO RESPONSE (timed out)` results. This matches the Journal PATCH storm tracked in #267.
Author
Owner

The bookmark capture storm returned four -1 client timeouts and twelve 429 responses. The probe flagged the four timeouts as unexpected. This matches the timed-out concurrent capture follow-up in #210 (and the earlier load observation in #177).

The bookmark capture storm returned four `-1` client timeouts and twelve `429` responses. The probe flagged the four timeouts as unexpected. This matches the timed-out concurrent capture follow-up in #210 (and the earlier load observation in #177).
Author
Owner

The recurring Log check found an identity inconsistency: two Log occurrences resolved to the same Calendar Event identity, but the probe expected distinct Events. This matches the recurring occurrence identity finding already tracked in #267.

The recurring Log check found an identity inconsistency: two Log occurrences resolved to the same Calendar Event identity, but the probe expected distinct Events. This matches the recurring occurrence identity finding already tracked in #267.
Author
Owner

Final report — menu blur (#246)

Branch job/menu-blur is pushed at a5dad6a359adaf3f28807af0e7f5ecbc10b27a9e. I merged origin/dev once at 14de1c8f and kept the #219 phone sheet header/title behavior with the sheet glass paint as a sibling behind its content.

Built

  • Kept menus and floating surfaces in the shared top-level overlay layer so their backdrop blur remains visible. Preserved the single active menu row.
  • Promoted the phone sheet blur check from expected failure to an assertion. Extended the production pixel probe to check Settings → Appearance → Fonts.
  • Fixed the three sheet-chrome tests to query the document-level portal. The full suite had queried the component mount container, which does not contain a portaled sheet.

Core files: packages/ui/src/actions/portal.ts, packages/ui/src/components/{OverlaySurface.svelte,menu/FloatingSurface.svelte,menu/Menu.svelte,menu/MenuItem.svelte}, packages/ui/src/tokens.css, apps/web/src/lib/styles/scrim.css, apps/web/src/lib/overlay/scrim.ts, apps/web/e2e/{menu-blur.mjs,phone-chrome.mjs}, apps/web/src/lib/components/OverlaySurface.svelte.test.ts, and docs/DESIGN.md. The merge also includes #219/#234 changes across the web app and UI package.

Production checks

bun run build succeeded. bun run test:e2e:phone-chrome passed; phone sheet pixel ratios were light 0.088 and dark 0.011 (threshold < 0.3). bun run test:e2e:menu-blur passed:

  • Settings menu: light 0.06, dark 0.03
  • Fonts menu: light 0.10, dark 0.04
  • Toast: light 0.03, dark 0.03
  • Sheet progressive blur: light 0.14, dark 0.14

Screenshots are attached to this issue: Settings menu — light, Fonts menu — light, Toast — light, Phone sheet — light, Settings menu — dark, Fonts menu — dark, Toast — dark, Phone sheet — dark.

Gates

  • cargo fmt --check: exit 0; output was empty.
  • cargo clippy --all-targets -- -D warnings (verbatim):

    Finished dev profile [unoptimized + debuginfo] target(s) in 12m 25s

  • cargo test (verbatim):

    Finished test profile [unoptimized + debuginfo] target(s) in 3m 42s
    Summed test result lines: 1308 passed, 0 failed, 12 ignored.

  • bun run check (verbatim): svelte-check found 0 errors and 0 warnings
  • First full bun run test output: Test Files 1 failed | 85 passed (86) and Tests 3 failed | 602 passed (605). All three failures were the portal lookup in OverlaySurface.svelte.test.ts; after the fix, the focused file passed: Test Files 1 passed (1), Tests 3 passed (3). The full web suite was not rerun under the one-pass gate limit.
  • cargo clean output: Removed 16313 files, 14.6GiB total. Removed apps/web/build and apps/web/.svelte-kit/output.

Adversarial pass and gaps

Ran one real-local-server pass. It covered the auth matrix, editor, attack1, media uploads, hostile bytes, and attack2 through search_hidden; it was stopped at about 48 minutes while analytics probes were running, to leave time for the required gates. The runner exited 143 from that intentional stop, so later sections and the final round summary were not completed. No crash, 5xx, or accepted hostile input was observed in the completed probes. The Standard User admin-config status mismatch (422 where 403 is expected) is already tracked by #268. Earlier finding notes for #265, #250, #267 and #210 are in the prior comments on this issue.

Decisions

Kept the #219 sheet title/header and ProgressiveBlur behavior, with glass paint as a sibling behind sheet content. The Fonts picker uses the existing shared menu/overlay path. No other design-doc decisions were needed.

## Final report — menu blur (#246) Branch `job/menu-blur` is pushed at `a5dad6a359adaf3f28807af0e7f5ecbc10b27a9e`. I merged `origin/dev` once at `14de1c8f` and kept the #219 phone sheet header/title behavior with the sheet glass paint as a sibling behind its content. ### Built - Kept menus and floating surfaces in the shared top-level overlay layer so their backdrop blur remains visible. Preserved the single active menu row. - Promoted the phone sheet blur check from expected failure to an assertion. Extended the production pixel probe to check Settings → Appearance → Fonts. - Fixed the three sheet-chrome tests to query the document-level portal. The full suite had queried the component mount container, which does not contain a portaled sheet. Core files: `packages/ui/src/actions/portal.ts`, `packages/ui/src/components/{OverlaySurface.svelte,menu/FloatingSurface.svelte,menu/Menu.svelte,menu/MenuItem.svelte}`, `packages/ui/src/tokens.css`, `apps/web/src/lib/styles/scrim.css`, `apps/web/src/lib/overlay/scrim.ts`, `apps/web/e2e/{menu-blur.mjs,phone-chrome.mjs}`, `apps/web/src/lib/components/OverlaySurface.svelte.test.ts`, and `docs/DESIGN.md`. The merge also includes #219/#234 changes across the web app and UI package. ### Production checks `bun run build` succeeded. `bun run test:e2e:phone-chrome` passed; phone sheet pixel ratios were light `0.088` and dark `0.011` (threshold `< 0.3`). `bun run test:e2e:menu-blur` passed: - Settings menu: light `0.06`, dark `0.03` - Fonts menu: light `0.10`, dark `0.04` - Toast: light `0.03`, dark `0.03` - Sheet progressive blur: light `0.14`, dark `0.14` Screenshots are attached to this issue: [Settings menu — light](https://git.kayg.org/attachments/cee5d656-1583-402a-8087-2e5467e65b0b), [Fonts menu — light](https://git.kayg.org/attachments/1a061557-9cc4-4086-84dd-000f8d85249a), [Toast — light](https://git.kayg.org/attachments/06bd1c6c-7e72-4d40-aa7b-fa7d6d940823), [Phone sheet — light](https://git.kayg.org/attachments/bb153f2c-1e3a-4ecc-9b91-5b7a88ab04b7), [Settings menu — dark](https://git.kayg.org/attachments/e43d83d0-1c67-48d7-b852-e9a32c15212c), [Fonts menu — dark](https://git.kayg.org/attachments/7115c4f9-c456-4ae3-b432-1023953d747b), [Toast — dark](https://git.kayg.org/attachments/1a43a4b4-3fc6-4040-8106-ae898aba0bfc), [Phone sheet — dark](https://git.kayg.org/attachments/01cf9a36-7def-4c3f-8f40-4e55fb201565). ### Gates - `cargo fmt --check`: exit 0; output was empty. - `cargo clippy --all-targets -- -D warnings` (verbatim): > Finished `dev` profile [unoptimized + debuginfo] target(s) in 12m 25s - `cargo test` (verbatim): > Finished `test` profile [unoptimized + debuginfo] target(s) in 3m 42s Summed test result lines: 1308 passed, 0 failed, 12 ignored. - `bun run check` (verbatim): `svelte-check found 0 errors and 0 warnings` - First full `bun run test` output: `Test Files 1 failed | 85 passed (86)` and `Tests 3 failed | 602 passed (605)`. All three failures were the portal lookup in `OverlaySurface.svelte.test.ts`; after the fix, the focused file passed: `Test Files 1 passed (1)`, `Tests 3 passed (3)`. The full web suite was not rerun under the one-pass gate limit. - `cargo clean` output: `Removed 16313 files, 14.6GiB total`. Removed `apps/web/build` and `apps/web/.svelte-kit/output`. ### Adversarial pass and gaps Ran one real-local-server pass. It covered the auth matrix, editor, attack1, media uploads, hostile bytes, and attack2 through `search_hidden`; it was stopped at about 48 minutes while analytics probes were running, to leave time for the required gates. The runner exited 143 from that intentional stop, so later sections and the final round summary were not completed. No crash, 5xx, or accepted hostile input was observed in the completed probes. The Standard User admin-config status mismatch (422 where 403 is expected) is already tracked by #268. Earlier finding notes for #265, #250, #267 and #210 are in the prior comments on this issue. ### Decisions Kept the #219 sheet title/header and ProgressiveBlur behavior, with glass paint as a sibling behind sheet content. The Fonts picker uses the existing shared menu/overlay path. No other design-doc decisions were needed.
kayg referenced this issue from a commit 2026-09-27 22:19:38 +00:00
Author
Owner

Merged in 088e17d4 (full web tests 620/620, svelte-check 0/0). One shared top-level portal layer, sheet glass behind content, single active menu row; blur pixel-verified for Settings menu, Fonts menu, toast, phone sheet.

Merged in 088e17d4 (full web tests 620/620, svelte-check 0/0). One shared top-level portal layer, sheet glass behind content, single active menu row; blur pixel-verified for Settings menu, Fonts menu, toast, phone sheet.
kayg closed this issue 2026-09-27 22:19:39 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
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#246
No description provided.