comparison design_system/NOTIFICATIONS.md @ 252:7a7581f040e8

[ui] Add scoped notification component Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Tue, 04 Aug 2026 11:57:16 -0700
parents
children fdf3816959cb
comparison
equal deleted inserted replaced
251:117c4d53c9a4 252:7a7581f040e8
1 # Zenbu UI notifications
2
3 `zen-notifications` is a light-DOM, framework-free notification scope. Its
4 stack is fixed to the bottom-right by default and styled only with `--zen-*`
5 tokens.
6
7 ```html
8 <zen-notifications aria-label="Notifications">
9 <main id="application"></main>
10 </zen-notifications>
11 ```
12
13 ## Notify
14
15 Descendants dispatch a bubbling, non-composed event:
16
17 ```js
18 source.dispatchEvent(new CustomEvent("zen-notify", {
19 bubbles: true,
20 composed: false,
21 detail: {
22 version: 1,
23 id: "repository-removed",
24 tone: "success",
25 message: "Repository removed",
26 description: "The local checkout was preserved.",
27 announcement: "polite",
28 durationMs: 5000,
29 action: {
30 token: "opaque-action-token",
31 label: "Undo",
32 },
33 },
34 }));
35 ```
36
37 Fields:
38
39 | Field | Values |
40 | --- | --- |
41 | `version` | must be `1` |
42 | `id` | stable non-empty string |
43 | `tone` | `info`, `success`, `warning`, or `error` |
44 | `message` | required text |
45 | `description` | optional text |
46 | `announcement` | `none`, `polite`, or `assertive` |
47 | `durationMs` | 250–120000; defaults to 5000 |
48 | `persistent` | disables timeout when `true` |
49 | `action` | opaque token and visible label |
50
51 The same ID updates and moves the existing record instead of creating a
52 duplicate. An ID is announced once during its current lifetime.
53
54 ## Dismiss and actions
55
56 Dismiss through the public method or a scoped event:
57
58 ```js
59 notifications.dismiss("repository-removed");
60
61 source.dispatchEvent(new CustomEvent("zen-dismiss-notification", {
62 bubbles: true,
63 composed: false,
64 detail: { version: 1, id: "repository-removed" },
65 }));
66 ```
67
68 Action buttons dispatch `zen-notification-action` with
69 `{ version, id, token }`. The opaque token remains in private component state
70 and is never written into DOM attributes or source markup.
71
72 Removal dispatches `zen-notification-removed` with `{ version, id, reason }`.
73
74 ## Bounds and timing
75
76 - Three records are rendered.
77 - Twenty additional records may remain queued.
78 - Overflow evicts the oldest finite record; all-persistent queues reject new
79 records.
80 - Queued records do not count down.
81 - Finite timers pause while hovered, while focus remains inside, and while the
82 document is hidden.
83 - Disconnecting clears records, timers, announcements, and internal DOM.
84
85 ## Accessibility
86
87 - Polite and assertive announcements use separate persistent live regions.
88 - Announcement priority is explicit and independent of tone.
89 - Announcement messages are serialized so bursts remain observable.
90 - Actions and dismissal use native buttons.
91 - New notifications never move focus.
92 - Keyed rendering preserves focused controls when unrelated records change.
93 - Forced-colors and reduced-motion modes are supported by component CSS.
94
95 Events with `composed: true` are rejected. This keeps nested application scopes
96 isolated; adapters crossing a ShadowRoot must validate and redispatch a new
97 non-composed event deliberately.