view design_system/NOTIFICATIONS.md @ 271:13d61401c57d

redirect Copilot cache to service state Set XDG_CACHE_HOME from the configured inference state so the systemd service can extract Copilot outside its protected home directory. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Fri, 07 Aug 2026 13:24:05 -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.