Toast countdown line: deplete symmetrically from both sides #721

Open
opened 2026-10-02 11:42:11 +00:00 by kayg · 5 comments
Owner

Owner report (2026-10-02)

"The toast border line should deplete from both sides and not just one." Today the countdown line runs around the toast border in one direction from one start point.

Expected

The countdown line starts full and shrinks symmetrically from both sides toward one point: it starts at the top centre (per #539, "ring anchored top centre") and both ends retract along the border at the same speed until it vanishes at the bottom centre. Hover/focus pauses it (existing). Reduced motion: a static thin line that fades. Use the shared toast ring component and motion tokens. Screenshots or a short frame strip at 0%, 50% and 90%.

## Owner report (2026-10-02) "The toast border line should deplete from both sides and not just one." Today the countdown line runs around the toast border in one direction from one start point. ## Expected The countdown line starts full and shrinks **symmetrically from both sides** toward one point: it starts at the top centre (per #539, "ring anchored top centre") and both ends retract along the border at the same speed until it vanishes at the bottom centre. Hover/focus pauses it (existing). Reduced motion: a static thin line that fades. Use the shared toast ring component and motion tokens. Screenshots or a short frame strip at 0%, 50% and 90%.
Author
Owner

Starting issue #721 on branch job/toastring-721. Base SHA: c4a61e8cf0 (origin/dev). I am tracing the shared Sonner toast ring, then will add regression coverage, update the animation, and capture the requested light/dark macOS frame strips.

Starting issue #721 on branch job/toastring-721. Base SHA: c4a61e8cf090170f35b1bed3350d9de20c83ecd5 (origin/dev). I am tracing the shared Sonner toast ring, then will add regression coverage, update the animation, and capture the requested light/dark macOS frame strips.
Author
Owner

Finding from the current implementation: apps/web/src/lib/components/AppToaster.svelte animates only stroke-dashoffset from 0 to 100, so the ring has one retracting end. Its reduced-motion rule sets .cal-toast-ring { display: none; }, while #721 asks for a static line that fades. DESIGN §34 still says “drains clockwise.” I am changing the shared ring animation and that sentence to make the accepted #721 behavior explicit; the existing hover/focus pause selectors stay in the shared timer path.

Finding from the current implementation: `apps/web/src/lib/components/AppToaster.svelte` animates only `stroke-dashoffset` from 0 to 100, so the ring has one retracting end. Its reduced-motion rule sets `.cal-toast-ring { display: none; }`, while #721 asks for a static line that fades. DESIGN §34 still says “drains clockwise.” I am changing the shared ring animation and that sentence to make the accepted #721 behavior explicit; the existing hover/focus pause selectors stay in the shared timer path.
Author
Owner

The first real-browser run passed app setup but stopped while checking the dark theme: its accent resolves to oklch(0.405112 0.168772 268.253), and the existing E2E color helper only accepts hex and RGB strings. The ring assertion did not run. I am resolving CSS colors through the browser canvas in the E2E helper so the required dark-theme pixel and contrast checks use Chromium's actual rendered color conversion.

The first real-browser run passed app setup but stopped while checking the dark theme: its accent resolves to `oklch(0.405112 0.168772 268.253)`, and the existing E2E color helper only accepts hex and RGB strings. The ring assertion did not run. I am resolving CSS colors through the browser canvas in the E2E helper so the required dark-theme pixel and contrast checks use Chromium's actual rendered color conversion.
Author
Owner

The next baseline run reached the existing ring-color assertion and found a second real ring issue. packages/ui/src/tokens.css changes --accent inside light .glass-overlay surfaces to --glass-text-accent, while AppToaster.svelte reads that changed value for the ring. The existing #405 probe expects the full theme accent and the UI guideline requires the shared accent. I will capture the theme accent at the toaster level before each toast's glass surface overrides it; the regression assertion remains unchanged.

The next baseline run reached the existing ring-color assertion and found a second real ring issue. `packages/ui/src/tokens.css` changes `--accent` inside light `.glass-overlay` surfaces to `--glass-text-accent`, while `AppToaster.svelte` reads that changed value for the ring. The existing #405 probe expects the full theme accent and the UI guideline requires the shared accent. I will capture the theme accent at the toaster level before each toast's glass surface overrides it; the regression assertion remains unchanged.
Author
Owner

Implemented

Committed as 0034c576c519060ff173abbac92a35f78a103319 (Make toast countdown drain symmetrically). The 100-unit toast path now shrinks from both ends, keeping its remaining dash centred at the bottom midpoint. Reduced motion keeps a static line and fades it over the toast timeout. Hover, focus and page-hidden pause rules remain shared with Sonner's timer. The ring keeps the original theme accent under the toast glass ink override.

Files: apps/web/src/lib/components/AppToaster.svelte, apps/web/e2e/toast-ring.mjs, apps/web/src/lib/themes.test.ts, bench/toast-ring-539.mjs, docs/DESIGN.md.

Verification

bun run build passed. bun run check passed:

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/toastring-721/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

The targeted #721 source test passed:

 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/toastring-721/apps/web

 Test Files  1 passed (1)
      Tests  1 passed | 74 skipped (75)
   Start at  16:18:09
   Duration  32.97s (transform 88%, import 7%, tests 5%)

The full bun run test -- --maxWorkers=1 run did not complete. On host load average 147.41/127.52/127.48, it reached this output before I stopped it (exit 130):

❯ |component| src/lib/composer/Composer.svelte.test.ts (29 tests | 3 failed) 67807ms
  ❯ chips and highlighting (7)
    × labels the Log submit action Journal 11923ms
    × shows the date, time and tag chips from the core runs and underlines the tokens 6252ms
  ❯ draft deck (6)
    × Esc closes and keeps the draft; reopening restores it 6429ms

The production browser run captured artifacts/toast-ring-721/toast-ring-macos-one-line-390-paper-4x.png (390 px, light theme, macOS emulation). A first server attempt exited with Sqlx(PoolTimedOut) during SQLite WAL startup. A retry reached the first crop but did not reach the requested frame strips before it was stopped. No 0/50/90 strips were produced or attached.

Performance

The local profile completed with pre-run load average 129.22/136.75/135.08. Against docs/perf/baseline.json (load 38.09/36.65/36.99), phone p50/p95 was 208.8/5732.1 ms vs 17/118.5 ms; desktop was 304.1/2327.1 ms vs 37.6/126.8 ms. CPU seconds and RSS were similar or lower, while burst latency was higher. This run is too load-contended to draw an app regression conclusion. Profile: artifacts/toast-ring-721-profile.json.

UX gaps

Closed: symmetric drain geometry, reduced-motion static-line fade, and source/browser regression coverage for both themes, all three widths, pause behavior and macOS platform rendering.

Left: the requested 0/50/90 frame strips for both themes are missing. The local E2E run stopped before its all-width/theme, hover/focus and reduced-motion assertions completed. The one available crop remains in the ignored worktree artifact directory and is not attached here.

Decisions

The issue specifies a static thin line that fades for reduced motion, so the line opacity follows the shared linear timeout while dash geometry stays fixed. I capture the theme accent on the toaster before glass contrast rules adjust toast text ink, keeping the ring tied to the theme token.

I fetched and merged origin/dev once before final gates. The branch currently reports one commit ahead and one behind origin/dev; I did not fetch or merge again. No push or deploy was made.

## Implemented Committed as `0034c576c519060ff173abbac92a35f78a103319` (`Make toast countdown drain symmetrically`). The 100-unit toast path now shrinks from both ends, keeping its remaining dash centred at the bottom midpoint. Reduced motion keeps a static line and fades it over the toast timeout. Hover, focus and page-hidden pause rules remain shared with Sonner's timer. The ring keeps the original theme accent under the toast glass ink override. Files: `apps/web/src/lib/components/AppToaster.svelte`, `apps/web/e2e/toast-ring.mjs`, `apps/web/src/lib/themes.test.ts`, `bench/toast-ring-539.mjs`, `docs/DESIGN.md`. ## Verification `bun run build` passed. `bun run check` passed: ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/toastring-721/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` The targeted #721 source test passed: ```text RUN v5.0.1 /home/kayg/Developer/calternal-wt/toastring-721/apps/web Test Files 1 passed (1) Tests 1 passed | 74 skipped (75) Start at 16:18:09 Duration 32.97s (transform 88%, import 7%, tests 5%) ``` The full `bun run test -- --maxWorkers=1` run did not complete. On host load average 147.41/127.52/127.48, it reached this output before I stopped it (exit 130): ```text ❯ |component| src/lib/composer/Composer.svelte.test.ts (29 tests | 3 failed) 67807ms ❯ chips and highlighting (7) × labels the Log submit action Journal 11923ms × shows the date, time and tag chips from the core runs and underlines the tokens 6252ms ❯ draft deck (6) × Esc closes and keeps the draft; reopening restores it 6429ms ``` The production browser run captured `artifacts/toast-ring-721/toast-ring-macos-one-line-390-paper-4x.png` (390 px, light theme, macOS emulation). A first server attempt exited with `Sqlx(PoolTimedOut)` during SQLite WAL startup. A retry reached the first crop but did not reach the requested frame strips before it was stopped. No 0/50/90 strips were produced or attached. ## Performance The local profile completed with pre-run load average 129.22/136.75/135.08. Against `docs/perf/baseline.json` (load 38.09/36.65/36.99), phone p50/p95 was 208.8/5732.1 ms vs 17/118.5 ms; desktop was 304.1/2327.1 ms vs 37.6/126.8 ms. CPU seconds and RSS were similar or lower, while burst latency was higher. This run is too load-contended to draw an app regression conclusion. Profile: `artifacts/toast-ring-721-profile.json`. ## UX gaps Closed: symmetric drain geometry, reduced-motion static-line fade, and source/browser regression coverage for both themes, all three widths, pause behavior and macOS platform rendering. Left: the requested 0/50/90 frame strips for both themes are missing. The local E2E run stopped before its all-width/theme, hover/focus and reduced-motion assertions completed. The one available crop remains in the ignored worktree artifact directory and is not attached here. ## Decisions The issue specifies a static thin line that fades for reduced motion, so the line opacity follows the shared linear timeout while dash geometry stays fixed. I capture the theme accent on the toaster before glass contrast rules adjust toast text ink, keeping the ring tied to the theme token. I fetched and merged `origin/dev` once before final gates. The branch currently reports one commit ahead and one behind `origin/dev`; I did not fetch or merge again. No push or deploy was made.
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#721
No description provided.