feat(layout): app-wide toast system + Copy button confirmation #92

Merged
YousifShkara merged 1 commit from feat/bunyip-upgrade-05-toast-system into main 2026-06-10 06:57:58 +02:00
Owner

bunyip had no toast / notification primitive: clicking the Downloads page "Copy" button silently succeeded (the local button-label flash from "Copy" to "Copied" was the only feedback, easy to miss when the user's eyes are on the command text), and form submits across Settings / 2FA had no path to surface a "Saved" / "Email updated" confirmation. This adds a tiny global toast root and the JS plumbing that fires it from anywhere on the page.

Mechanics:

TOAST_JS is a third inline script in views/layout.rs::document, sibling to THEME_FLASH and THEME_TOGGLE. It exposes window.bunyipToast(msg, kind) where kind is success | error | info (default info). Each call appends an auto-dismissing pill (200ms fade-in + 2.5s visible + 250ms fade-out) into the #bunyip-toast-root div mounted at the end of <body>. The root is pointer-events-none so non-pill regions never block clicks; each pill is pointer-events-auto and carries role="status" for AT semantics. The root itself is aria-live="polite" aria-atomic="true" so screen readers announce each message in full without interrupting the user.

The same script drains ?toast_ok= / ?toast_err= from the URL on page load and strips them via history.replaceState. This means any handler can surface a confirmation via a 302 redirect: Location: /settings?toast_ok=Email%20updated, and the layout fires the toast on the next render without re-firing on reload. No handler-side wiring has changed yet (deferred per docs/bunyip-upgrade/09-out-of-scope.md), but the bridge is in place so the next pass through Settings handlers gets confirmations for free.

command_block (the Downloads copy-button helper) keeps the existing button-label swap and additionally calls bunyipToast('Copied to clipboard', 'success') so the confirmation is visible regardless of where the user's cursor lands. Failure path mirrors the success path with an error toast. The if(window.bunyipToast) guard keeps the button usable if the toast script ever fails to load.

Closes finding 7 (Copy button no visual confirmation) and finding 10 (no app-wide toast/notification system) from the Claude-for-Chrome audit. See docs/bunyip-upgrade/05-toast-system-and-copy-feedback.md for the full spec.

bunyip had no toast / notification primitive: clicking the Downloads page "Copy" button silently succeeded (the local button-label flash from "Copy" to "Copied" was the only feedback, easy to miss when the user's eyes are on the command text), and form submits across Settings / 2FA had no path to surface a "Saved" / "Email updated" confirmation. This adds a tiny global toast root and the JS plumbing that fires it from anywhere on the page. Mechanics: `TOAST_JS` is a third inline script in `views/layout.rs::document`, sibling to THEME_FLASH and THEME_TOGGLE. It exposes `window.bunyipToast(msg, kind)` where kind is `success | error | info` (default info). Each call appends an auto-dismissing pill (200ms fade-in + 2.5s visible + 250ms fade-out) into the `#bunyip-toast-root` div mounted at the end of `<body>`. The root is `pointer-events-none` so non-pill regions never block clicks; each pill is `pointer-events-auto` and carries `role="status"` for AT semantics. The root itself is `aria-live="polite" aria-atomic="true"` so screen readers announce each message in full without interrupting the user. The same script drains `?toast_ok=` / `?toast_err=` from the URL on page load and strips them via `history.replaceState`. This means any handler can surface a confirmation via a 302 redirect: `Location: /settings?toast_ok=Email%20updated`, and the layout fires the toast on the next render without re-firing on reload. No handler-side wiring has changed yet (deferred per docs/bunyip-upgrade/09-out-of-scope.md), but the bridge is in place so the next pass through Settings handlers gets confirmations for free. `command_block` (the Downloads copy-button helper) keeps the existing button-label swap and additionally calls `bunyipToast('Copied to clipboard', 'success')` so the confirmation is visible regardless of where the user's cursor lands. Failure path mirrors the success path with an error toast. The `if(window.bunyipToast)` guard keeps the button usable if the toast script ever fails to load. Closes finding 7 (Copy button no visual confirmation) and finding 10 (no app-wide toast/notification system) from the Claude-for-Chrome audit. See docs/bunyip-upgrade/05-toast-system-and-copy-feedback.md for the full spec.
feat(layout): app-wide toast system + Copy button confirmation
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt / clippy / build / test (pull_request) Successful in 1m34s
5d5babea42
bunyip had no toast / notification primitive: clicking the Downloads page "Copy" button silently succeeded (the local button-label flash from "Copy" to "Copied" was the only feedback, easy to miss when the user's eyes are on the command text), and form submits across Settings / 2FA had no path to surface a "Saved" / "Email updated" confirmation. This adds a tiny global toast root and the JS plumbing that fires it from anywhere on the page.

Mechanics:

`TOAST_JS` is a third inline script in `views/layout.rs::document`, sibling to THEME_FLASH and THEME_TOGGLE. It exposes `window.bunyipToast(msg, kind)` where kind is `success | error | info` (default info). Each call appends an auto-dismissing pill (200ms fade-in + 2.5s visible + 250ms fade-out) into the `#bunyip-toast-root` div mounted at the end of `<body>`. The root is `pointer-events-none` so non-pill regions never block clicks; each pill is `pointer-events-auto` and carries `role="status"` for AT semantics. The root itself is `aria-live="polite" aria-atomic="true"` so screen readers announce each message in full without interrupting the user.

The same script drains `?toast_ok=` / `?toast_err=` from the URL on page load and strips them via `history.replaceState`. This means any handler can surface a confirmation via a 302 redirect: `Location: /settings?toast_ok=Email%20updated`, and the layout fires the toast on the next render without re-firing on reload. No handler-side wiring has changed yet (deferred per docs/bunyip-upgrade/09-out-of-scope.md), but the bridge is in place so the next pass through Settings handlers gets confirmations for free.

`command_block` (the Downloads copy-button helper) keeps the existing button-label swap and additionally calls `bunyipToast('Copied to clipboard', 'success')` so the confirmation is visible regardless of where the user's cursor lands. Failure path mirrors the success path with an error toast. The `if(window.bunyipToast)` guard keeps the button usable if the toast script ever fails to load.

Closes finding 7 (Copy button no visual confirmation) and finding 10 (no app-wide toast/notification system) from the Claude-for-Chrome audit. See docs/bunyip-upgrade/05-toast-system-and-copy-feedback.md for the full spec.
YousifShkara deleted branch feat/bunyip-upgrade-05-toast-system 2026-06-10 06:57:58 +02:00
Sign in to join this conversation.
No reviewers
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
psa-systems/bunyip!92
No description provided.