view design_system/NOTIFICATIONS.md @ 279:b3b547563ec7

Add Google connector service and agent wiki Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code. Co-authored-by: Copilot <[email protected]> Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 22:22:36 -0700
parents 60a876c4587a
children
line wrap: on
line source

# Zenbu UI notifications

`zen-notifications` is a light-DOM, framework-free notification scope. Its
stack is fixed to the bottom-right by default and styled only with `--zen-*`
tokens.

```html
<zen-notifications aria-label="Notifications">
  <main id="application"></main>
</zen-notifications>
```

## Notify

Descendants dispatch a bubbling, non-composed event:

```js
source.dispatchEvent(new CustomEvent("zen-notify", {
  bubbles: true,
  composed: false,
  detail: {
    version: 1,
    id: "repository-removed",
    tone: "success",
    message: "Repository removed",
    description: "The local checkout was preserved.",
    announcement: "polite",
    durationMs: 5000,
    action: {
      token: "opaque-action-token",
      label: "Undo",
    },
  },
}));
```

Fields:

| Field | Values |
| --- | --- |
| `version` | must be `1` |
| `id` | stable non-empty string |
| `tone` | `info`, `success`, `warning`, or `error` |
| `message` | required text |
| `description` | optional text |
| `announcement` | `none`, `polite`, or `assertive` |
| `durationMs` | 250–120000; defaults to 5000 |
| `persistent` | disables timeout when `true` |
| `action` | opaque token and visible label |

The same ID updates and moves the existing record instead of creating a
duplicate. An ID is announced once during its current lifetime.

## Dismiss and actions

Dismiss through the public method or a scoped event:

```js
notifications.dismiss("repository-removed");

source.dispatchEvent(new CustomEvent("zen-dismiss-notification", {
  bubbles: true,
  composed: false,
  detail: { version: 1, id: "repository-removed" },
}));
```

Action buttons dispatch `zen-notification-action` with
`{ version, id, token }`. The opaque token remains in private component state
and is never written into DOM attributes or source markup.

Removal dispatches `zen-notification-removed` with `{ version, id, reason }`.

## Bounds and timing

- Three records are rendered.
- Twenty additional records may remain queued.
- The idle stack layers older records behind the newest with depth and scale.
- Hovering the stack or focusing an action expands all visible records using
  their measured heights and first-party component code.
- Overflow evicts the oldest finite record; all-persistent queues reject new
  records.
- Queued records do not count down.
- Finite timers pause while hovered, while focus remains inside, and while the
  document is hidden.
- Disconnecting clears records, timers, announcements, and internal DOM.

## Accessibility

- Polite and assertive announcements use separate persistent live regions.
- Announcement priority is explicit and independent of tone.
- Announcement messages are serialized so bursts remain observable.
- Actions and dismissal use native buttons.
- New notifications never move focus.
- Keyed rendering preserves focused controls when unrelated records change.
- Forced-colors and reduced-motion modes are supported by component CSS.

Events with `composed: true` are rejected. This keeps nested application scopes
isolated; adapters crossing a ShadowRoot must validate and redispatch a new
non-composed event deliberately.