Block action pill: opaque, no blur, lopsided rectangle behind it #1189

Open
opened 2026-10-06 08:05:44 +00:00 by kayg · 3 comments
Owner

Owner report (2026-10-06, production f5fbced3c): "the hell is this? there's no blur, somehow the right side is bigger? pills were supposed to be transparent/blurry … nothing subtle or nice about this"

The Note block action capsule (#1152; NoteEditorSurface.svelte) over a full-width line renders as (a) an OPAQUE dark pill — no glass material, no backdrop blur — and (b) a solid dark RECTANGLE behind/right of the pill (the intended progressive text fade was implemented as a hard-edged box that extends past the pill's right end), so the pill looks lopsided and heavy.

Fix (one shared look, reuse the existing glass Pill/capsule primitive and tokens — the same material as the search palette action pills and the header action capsule):

  1. The capsule uses the shared glass capsule material: translucent fill token + backdrop-filter blur/saturate token + hairline border, fully rounded; NO opaque background. It must look identical to the header action capsule (compare side by side at actual size).
  2. Text behind the capsule: no box. If the line runs under the capsule, the capsule's own backdrop blur is the softening; optionally a short horizontal mask-gradient on the TEXT LINE only (fading the last ~24px of glyphs under the capsule), never a filled rectangle. Nothing may extend beyond the capsule's rounded shape.
  3. The capsule's left and right padding are equal; icons optically centred; total width = content + equal padding.
  4. Reduced transparency (prefers-reduced-transparency / the app's setting): use the shared opaque fallback token, still rounded, no extra rectangle.
  5. Design-rule test: the capsule's computed backdrop-filter is the shared glass token; no element under/around the capsule has an opaque background larger than the capsule's border box.
    Screenshots at ACTUAL size (1x and 2x, not only 4x) over: a short line, a full-width line, a long paragraph, in light and dark, next to the header capsule for comparison. Merge origin/dev; bun run check + bun run test pass. Commit; comment on this issue with head SHA. Do not push.
## Owner report (2026-10-06, production f5fbced3c): "the hell is this? there's no blur, somehow the right side is bigger? pills were supposed to be transparent/blurry … nothing subtle or nice about this" The Note block action capsule (#1152; NoteEditorSurface.svelte) over a full-width line renders as (a) an OPAQUE dark pill — no glass material, no backdrop blur — and (b) a solid dark RECTANGLE behind/right of the pill (the intended progressive text fade was implemented as a hard-edged box that extends past the pill's right end), so the pill looks lopsided and heavy. Fix (one shared look, reuse the existing glass Pill/capsule primitive and tokens — the same material as the search palette action pills and the header action capsule): 1. The capsule uses the shared glass capsule material: translucent fill token + backdrop-filter blur/saturate token + hairline border, fully rounded; NO opaque background. It must look identical to the header action capsule (compare side by side at actual size). 2. Text behind the capsule: no box. If the line runs under the capsule, the capsule's own backdrop blur is the softening; optionally a short horizontal mask-gradient on the TEXT LINE only (fading the last ~24px of glyphs under the capsule), never a filled rectangle. Nothing may extend beyond the capsule's rounded shape. 3. The capsule's left and right padding are equal; icons optically centred; total width = content + equal padding. 4. Reduced transparency (prefers-reduced-transparency / the app's setting): use the shared opaque fallback token, still rounded, no extra rectangle. 5. Design-rule test: the capsule's computed backdrop-filter is the shared glass token; no element under/around the capsule has an opaque background larger than the capsule's border box. Screenshots at ACTUAL size (1x and 2x, not only 4x) over: a short line, a full-width line, a long paragraph, in light and dark, next to the header capsule for comparison. Merge origin/dev; `bun run check` + `bun run test` pass. Commit; comment on this issue with head SHA. Do not push.
Author
Owner

Starting issue #1189 on branch job/pillglass-1189. Base SHA: 5301e02085 (origin/dev). I confirmed the toolbar uses the shared frosted PillGroup, with an extra ProgressiveBlur wash and a bespoke overflow button; I am removing those feature-local layers and extending regression coverage.

Starting issue #1189 on branch job/pillglass-1189. Base SHA: 5301e020859c1d61b4a7f812a5049a64241203e8 (origin/dev). I confirmed the toolbar uses the shared frosted PillGroup, with an extra ProgressiveBlur wash and a bespoke overflow button; I am removing those feature-local layers and extending regression coverage.
Author
Owner

Completed

Removed the ProgressiveBlur wash behind the Note block action capsule. The shared frosted PillGroup now owns the capsule surface and blur. The ⋯ action uses the shared FormatButton menu control. This removes the opaque slab and keeps paint within the capsule's rounded bounds. The shared glass tokens also supply the reduced-transparency fallback.

The guard test checks that BlockHoverActions.svelte has no ProgressiveBlur, background/backdrop declarations, or pixel size literals. The live E2E asserts the capsule matches the shared glass material and that no child or scrim spills outside it.

Branch: job/pillglass-1189
Base: 5301e020859c1d61b4a7f812a5049a64241203e
Head: 0dc3cb1cc97e815edbb7b30e639106ddfeb5f939 (fix(notes): keep block action capsule as shared glass)

Files

  • apps/web/src/lib/editor/format/BlockHoverActions.svelte
  • apps/web/src/lib/editor/format/BlockHoverActions.svelte.test.ts
  • apps/web/e2e/notes.mjs
  • contracts/perf/adoption-1058.json
  • contracts/perf/exceptions.json (refreshed exact-live scope hashes; no limits or ratchets changed)

Shared controls

The visible actions use PillGroup frosted and FormatButton; the comparison capsule uses the existing header glass capsule. No feature-local colors, surface, blur, or size styling was added.

UX gaps closed

  • Removed the hard-edged wash that extended beyond the capsule and hid the shared blur.
  • Routed ⋯ through the same shared format button and menu behavior as the other actions.
  • Kept the transparent hit bridge for pointer continuity without painting a larger surface.
  • Checked desktop, tablet and phone states in light and dark with macOS shortcut/platform emulation. Phone screenshots show the touch action bar.

UX gaps left: none found for this capsule.

Screenshot evidence

Real Notes app production build, macOS platform emulation. Screenshots are actual 1× and 2× captures (not 4× crops); desktop/tablet rows show short, long and full-width text, and the 390px rows show touch controls. Mono is light theme; Noir is dark theme.

Scale Viewport Theme States
1× 1440px Mono short · long · full-width
1× 1440px Noir short · long · full-width
1× 820px Mono short · long · full-width
1× 820px Noir short · long · full-width
1× 390px touch Mono touch toolbar
1× 390px touch Noir touch toolbar
2× 1440px Mono short · long · full-width
2× 1440px Noir short · long · full-width
2× 820px Mono short · long · full-width
2× 820px Noir short · long · full-width
2× 390px touch Mono touch toolbar
2× 390px touch Noir touch toolbar

Gate output

cargo fmt --check:

cargo fmt --check exit: 0

bun run check:

perf-lint: PASS; 0 violations; 22359 scoped exceptions
svelte-check found 0 errors and 2 warnings in 2 files

The two Svelte warnings are existing empty CSS rulesets in AttachmentDeck.svelte and AgendaList.svelte.

Focused component test (bunx vitest run src/lib/editor/format/BlockHoverActions.svelte.test.ts --maxWorkers=2):

 Test Files  1 passed (1)
      Tests  9 passed (9)
    Duration  25.02s (transform 72%, environment 13%, import 7%, tests 5%, setup 3%)

Full web tests (bun run test -- --maxWorkers=2):

 Test Files  274 passed (274)
      Tests  1908 passed (1908)
   Start at  13:02:46
   Duration  418.17s (transform 29%, environment 26%, import 22%, tests 17%, setup 5%)

1× and 2× production E2E runs:

notes #1152 capsule e2e: 1440, 820, 390 px states in Mono/Noir passed
CSP REPORTS notes: 0 across 2 pages

node --check apps/web/e2e/notes.mjs and git diff --check passed. cargo build -p calternal-server completed for the local E2E server; no Rust source changed, so Rust clippy/test gates were not applicable. The E2E script ran under Node 22 because Bun could not load the host's sharp native module (libstdc++.so.6); both Node runs passed.

Performance: this visual correction adds no operation or IO. It removes the separate ProgressiveBlur layer; no benchmark profile or measurement was added for this non-performance issue.

Decisions

  • The existing PillGroup frosted is the sole owner of the capsule's glass material, including its reduced-transparency fallback.
  • Existing FormatButton already supports the menu action, so no new shared variant was needed.
  • Kept the --hit-outset transparent pointer bridge; it preserves hit continuity without adding a painted box.
  • No text mask was needed: the capsule's shared backdrop blur provides the requested softening.

Known gaps

  • The host's Bun + sharp integration could not start the screenshot script; Node 22 ran the same script and passed at both scales.
  • No API or route changed, so no adversarial API matrix applies.

Cleanup: cargo clean completed (Removed 8633 files, 7.3GiB total); apps/web/build was removed. The working tree is clean. No push or deploy was performed. git merge origin/dev reported Already up to date.

## Completed Removed the `ProgressiveBlur` wash behind the Note block action capsule. The shared frosted `PillGroup` now owns the capsule surface and blur. The ⋯ action uses the shared `FormatButton` menu control. This removes the opaque slab and keeps paint within the capsule's rounded bounds. The shared glass tokens also supply the reduced-transparency fallback. The guard test checks that `BlockHoverActions.svelte` has no `ProgressiveBlur`, background/backdrop declarations, or pixel size literals. The live E2E asserts the capsule matches the shared glass material and that no child or scrim spills outside it. **Branch:** `job/pillglass-1189` **Base:** `5301e020859c1d61b4a7f812a5049a64241203e` **Head:** `0dc3cb1cc97e815edbb7b30e639106ddfeb5f939` (`fix(notes): keep block action capsule as shared glass`) ### Files - `apps/web/src/lib/editor/format/BlockHoverActions.svelte` - `apps/web/src/lib/editor/format/BlockHoverActions.svelte.test.ts` - `apps/web/e2e/notes.mjs` - `contracts/perf/adoption-1058.json` - `contracts/perf/exceptions.json` (refreshed exact-live scope hashes; no limits or ratchets changed) ### Shared controls The visible actions use `PillGroup frosted` and `FormatButton`; the comparison capsule uses the existing header glass capsule. No feature-local colors, surface, blur, or size styling was added. ### UX gaps closed - Removed the hard-edged wash that extended beyond the capsule and hid the shared blur. - Routed ⋯ through the same shared format button and menu behavior as the other actions. - Kept the transparent hit bridge for pointer continuity without painting a larger surface. - Checked desktop, tablet and phone states in light and dark with macOS shortcut/platform emulation. Phone screenshots show the touch action bar. **UX gaps left:** none found for this capsule. ### Screenshot evidence Real Notes app production build, macOS platform emulation. Screenshots are actual 1× and 2× captures (not 4× crops); desktop/tablet rows show short, long and full-width text, and the 390px rows show touch controls. `Mono` is light theme; `Noir` is dark theme. | Scale | Viewport | Theme | States | |---|---:|---|---| | 1× | 1440px | Mono | [short](https://git.kayg.org/attachments/b7c2898c-0ffa-4a8a-b547-8bbe68cc8d9e) · [long](https://git.kayg.org/attachments/f53704ee-5770-4800-939f-ceb2f9e1f4e4) · [full-width](https://git.kayg.org/attachments/eaa9a26d-ecfc-4a17-9da1-b541fb5328c4) | | 1× | 1440px | Noir | [short](https://git.kayg.org/attachments/e7439165-1927-4c55-8fb9-e5fdbd38da71) · [long](https://git.kayg.org/attachments/874fe9a3-1d3e-4cee-8f37-2d433f85e33b) · [full-width](https://git.kayg.org/attachments/07b8a4b4-42d8-4b2b-973b-79e855cb8bbc) | | 1× | 820px | Mono | [short](https://git.kayg.org/attachments/fc667f8f-1e08-4b56-88d1-f9118724a143) · [long](https://git.kayg.org/attachments/2e1cc633-bf37-44ef-9510-e4c4a9c17d7b) · [full-width](https://git.kayg.org/attachments/bdf2f2ea-56c8-4cc7-aa17-358c1b297f5f) | | 1× | 820px | Noir | [short](https://git.kayg.org/attachments/470adec4-87ac-49e3-8368-abe389a63fe8) · [long](https://git.kayg.org/attachments/e4b7b5d8-7010-4c5c-ac2a-cd2c3b7f5c8a) · [full-width](https://git.kayg.org/attachments/bc39e71c-e358-400b-8ed2-b6a03650b786) | | 1× | 390px touch | Mono | [touch toolbar](https://git.kayg.org/attachments/56c7731b-e79d-47a4-8611-94194c3a5a43) | | 1× | 390px touch | Noir | [touch toolbar](https://git.kayg.org/attachments/8b9aaa4e-8b75-489f-9ada-d8a27deed94f) | | 2× | 1440px | Mono | [short](https://git.kayg.org/attachments/751282f9-6350-4f5d-9037-69d189642b5a) · [long](https://git.kayg.org/attachments/348ac0c4-d3a6-4134-9013-8f5426fc8891) · [full-width](https://git.kayg.org/attachments/16eed089-bf94-47d7-befc-f83fb330e87e) | | 2× | 1440px | Noir | [short](https://git.kayg.org/attachments/54e970c0-1471-456f-9164-62e7569df3fc) · [long](https://git.kayg.org/attachments/7c4e5e20-6dd1-45fd-9a76-743412c26df3) · [full-width](https://git.kayg.org/attachments/f824c58d-a3ab-479c-8139-50dc5d8cb5f3) | | 2× | 820px | Mono | [short](https://git.kayg.org/attachments/89d0a30d-fa60-4644-8ad3-bd3dbd7a5b86) · [long](https://git.kayg.org/attachments/610b48f4-d5b2-4b65-8307-4173d8d49d72) · [full-width](https://git.kayg.org/attachments/a8c07cb1-f64f-4a8c-bc1a-38e4d66af7a5) | | 2× | 820px | Noir | [short](https://git.kayg.org/attachments/7bc5d3ae-e403-4e91-be32-d8a51e846081) · [long](https://git.kayg.org/attachments/b84c7138-84cf-44e3-b8aa-b462954ddd35) · [full-width](https://git.kayg.org/attachments/69b0fe9a-e146-4fb2-9997-4b36ae329bc4) | | 2× | 390px touch | Mono | [touch toolbar](https://git.kayg.org/attachments/0567c99d-4f1c-4540-92f8-1238f62a6034) | | 2× | 390px touch | Noir | [touch toolbar](https://git.kayg.org/attachments/db3d0e3b-e974-46f1-8acb-caa1865273df) | ### Gate output `cargo fmt --check`: ```text cargo fmt --check exit: 0 ``` `bun run check`: ```text perf-lint: PASS; 0 violations; 22359 scoped exceptions svelte-check found 0 errors and 2 warnings in 2 files ``` The two Svelte warnings are existing empty CSS rulesets in `AttachmentDeck.svelte` and `AgendaList.svelte`. Focused component test (`bunx vitest run src/lib/editor/format/BlockHoverActions.svelte.test.ts --maxWorkers=2`): ```text Test Files 1 passed (1) Tests 9 passed (9) Duration 25.02s (transform 72%, environment 13%, import 7%, tests 5%, setup 3%) ``` Full web tests (`bun run test -- --maxWorkers=2`): ```text Test Files 274 passed (274) Tests 1908 passed (1908) Start at 13:02:46 Duration 418.17s (transform 29%, environment 26%, import 22%, tests 17%, setup 5%) ``` 1× and 2× production E2E runs: ```text notes #1152 capsule e2e: 1440, 820, 390 px states in Mono/Noir passed CSP REPORTS notes: 0 across 2 pages ``` `node --check apps/web/e2e/notes.mjs` and `git diff --check` passed. `cargo build -p calternal-server` completed for the local E2E server; no Rust source changed, so Rust clippy/test gates were not applicable. The E2E script ran under Node 22 because Bun could not load the host's `sharp` native module (`libstdc++.so.6`); both Node runs passed. **Performance:** this visual correction adds no operation or IO. It removes the separate `ProgressiveBlur` layer; no benchmark profile or measurement was added for this non-performance issue. ### Decisions - The existing `PillGroup frosted` is the sole owner of the capsule's glass material, including its reduced-transparency fallback. - Existing `FormatButton` already supports the menu action, so no new shared variant was needed. - Kept the `--hit-outset` transparent pointer bridge; it preserves hit continuity without adding a painted box. - No text mask was needed: the capsule's shared backdrop blur provides the requested softening. ### Known gaps - The host's Bun + `sharp` integration could not start the screenshot script; Node 22 ran the same script and passed at both scales. - No API or route changed, so no adversarial API matrix applies. **Cleanup:** `cargo clean` completed (`Removed 8633 files, 7.3GiB total`); `apps/web/build` was removed. The working tree is clean. No push or deploy was performed. `git merge origin/dev` reported `Already up to date.`
Author
Owner

Head update

Final review restored the existing indentation in the Notes E2E card-stack helper. This is formatting-only; it does not change behavior.

Current head: eca90f0b305b32c566190ad87f9be50963ee0769 (style(notes): restore e2e helper indentation). The screenshot evidence and gates in the report above apply to the feature commit and remain unchanged.

 Test Files  1 passed (1)
      Tests  9 passed (9)
   Duration  18.62s (transform 76%, environment 10%, import 6%, tests 5%, setup 3%)

node --check e2e/notes.mjs and git diff --check passed. The working tree is clean.

## Head update Final review restored the existing indentation in the Notes E2E card-stack helper. This is formatting-only; it does not change behavior. **Current head:** `eca90f0b305b32c566190ad87f9be50963ee0769` (`style(notes): restore e2e helper indentation`). The screenshot evidence and gates in the report above apply to the feature commit and remain unchanged. ```text Test Files 1 passed (1) Tests 9 passed (9) Duration 18.62s (transform 76%, environment 10%, import 6%, tests 5%, setup 3%) ``` `node --check e2e/notes.mjs` and `git diff --check` passed. The working tree is clean.
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#1189
No description provided.