Pular para o conteúdo

Notification-center component

components specs/components/notification-center.kmd

The in-app notification inbox: a header trigger (a bell + an unread counter badge) that opens a panel listing past notifications — grouped into sections/tabs, each item read or unread, with mark-all-read and an empty state. It is the PERSISTENT counterpart to the transient snackbar: a snackbar flashes once and is gone; the center keeps the history so a user who missed the flash can still find it. gov.br ships a header `notification` dropdown; KDS had snackbars but no inbox.

Quando esta spec se aplica

Triggers primários

Todos os triggers

Corpo da especificação

Notification center

Design-forward spec. Unlike the sibling gov.br-pack specs, this one is written ahead of its Kroma widget — the in-app inbox component does not exist yet (engines/sdk/kroma/src/notify_center.rs is the desktop daemon's history store + DND, kroma#152, not this UI). The widget is tracked as kroma#312 and MUST conform to this contract.

R1 — What it is, and what it is NOT

The notification center is the persistent notification surface: an inbox of notifications the app has raised, reachable any time from a header trigger. It is the counterpart to the snackbar (snackbars.kmd), which is transient — a snackbar appears briefly, is not stackable history, and auto-dismisses. A notification worth keeping is shown as a snackbar and recorded in the center; an ephemeral confirmation is a snackbar only. The center is also distinct from the OS desktop-notification daemon (notify_center.rs): in a desktop Koder app the center MAY render that daemon's history store as its data source; a web/mobile app supplies its own.

R2 — Anatomy

  • Trigger — a bell icon button in the top bar (topbar-placement.kmd) carrying an unread counter badge (badges.kmd) — the count of unread items, hidden at 0, capped (e.g. 9+). Activating it toggles the panel.
  • Panel — a dropdown/sheet anchored to the trigger, holding:
    • an optional section/tab row (e.g. All / Unread, or by category/urgency),
    • a list of notification items (newest first),
    • a mark-all-read action,
    • and, when empty, an empty state.

R3 — Notification item

Each row shows: an optional category/app icon, the summary (title), an optional body snippet, and a relative timestamp. Its data mirrors the store's HistoryEntry (id, app_name, summary, body, urgency, ts_unix, category). An unread item is visually distinguished (a leading dot / accent rail / weight) from a read one. Opening/activating an item marks it read and MAY route to its target.

R4 — Read/unread state

Read/unread is the center's own UI-layer state — the desktop HistoryEntry store does not carry a read flag (it is a log). The center tracks which ids are read, derives the unread counter (R2) from it, and mark-all-read clears every unread in the current view. Read state persists across sessions (alongside the history).

R5 — Ordering & urgency

Items list newest-first. Urgency (freedesktop 0 low / 1 normal / 2 critical, per the store) MAY raise a critical item's prominence (accent/pin) but does not reorder past the newest-first rule within a section. A Do-Not-Disturb mode (the daemon's is_dnd, desktop) suppresses the transient snackbar but the item is still recorded and appears in the center — the center is how you catch up on what DND muted.

R6 — Accessibility

  • The trigger is a labelled button; its accessible name includes the unread count ("Notifications, 3 unread").
  • New notifications announce via an ARIA live region (polite for normal, assertive/alert only for critical) — the transient-announce path, separate from the panel.
  • The open panel is a focus-managed dropdown (like the account menu): Escape closes, focus is trapped while open, list items are arrow-navigable, Enter/Space activates.
  • Each item's read/unread state and the counter are exposed to AT.

R7 — gov.br parity

Satisfies the gov.br notification header dropdown (govbr-ds-parity, GVB-08): bell + counter trigger, a panel listing items, mark-all-read, read/unread.

Não-escopo

  • The transient toast/snackbar surface — snackbars.kmd.
  • The desktop notification daemon (delivery, DND enforcement, freedesktop protocol) — notify_center.rs / kroma-notifyd; the center is a reader of its history, not the daemon.
  • The Kroma widget implementation — kroma#312 (this spec is its contract).

Requirements (testable)

Requirement: Header trigger with unread counter badge {#req-notification-center-trigger-badge}

The notification center SHALL be reachable from a bell icon button in the top bar carrying an unread counter badge; the badge SHALL show the count of unread items, SHALL be hidden when the count is zero, and SHALL cap the displayed count (e.g. 9+). Activating the trigger SHALL toggle the panel open and closed.

Scenario: Badge reflects unread count and hides at zero

  • GIVEN a notification center with 3 unread items
  • WHEN the trigger bell is rendered
  • THEN the counter badge reads "3"
  • WHEN every item is marked read so the unread count reaches 0
  • THEN the counter badge is hidden

Scenario: Badge caps the displayed count

  • GIVEN a notification center with 12 unread items
  • WHEN the trigger is rendered
  • THEN the counter badge displays the capped value "9+" rather than "12"

Scenario: Trigger toggles the panel

  • GIVEN the notification panel is closed
  • WHEN the user activates the bell trigger
  • THEN the panel opens anchored to the trigger
  • WHEN the user activates the bell trigger again
  • THEN the panel closes

Requirement: Notification item read/unread distinction and activation {#req-notification-center-item-read-state}

Each notification item SHALL be visually distinguished as unread (a leading dot / accent rail / weight) versus read, and activating an item SHALL mark it read; read/unread state SHALL be the center's own UI-layer state and SHALL persist across sessions.

Scenario: Unread item is visually distinguished

  • GIVEN a list containing one unread item and one read item
  • WHEN the panel renders the list
  • THEN the unread item carries its unread indicator (leading dot / accent rail / weight)
  • AND the read item does not carry the unread indicator

Scenario: Activating an item marks it read

  • GIVEN an unread notification item and an unread counter of 1
  • WHEN the user opens/activates that item
  • THEN the item becomes read and loses its unread indicator
  • AND the unread counter decrements to 0

Scenario: Read state persists across sessions

  • GIVEN an item that was marked read in a previous session
  • WHEN the center is reloaded in a new session
  • THEN that item is still shown as read

Requirement: Mark-all-read action {#req-notification-center-mark-all-read}

The panel SHALL provide a mark-all-read action that clears every unread item in the current view and drives the unread counter to zero.

Scenario: Mark-all-read clears the current view

  • GIVEN the panel showing 4 unread items in the current view
  • WHEN the user activates the mark-all-read action
  • THEN all 4 items become read
  • AND the unread counter badge is hidden

Requirement: Newest-first ordering with urgency and DND recording {#req-notification-center-ordering-dnd}

Items SHALL list newest-first within a section; critical urgency MAY raise an item's prominence but SHALL NOT reorder it past the newest-first rule; and an item suppressed by Do-Not-Disturb SHALL still be recorded and appear in the center.

Scenario: Items list newest-first

  • GIVEN three notifications recorded at ascending timestamps t1 < t2 < t3
  • WHEN the panel renders the list
  • THEN the item with timestamp t3 appears first and the item with t1 appears last

Scenario: Critical urgency does not break newest-first

  • GIVEN a critical (urgency 2) item recorded before a newer normal (urgency 1) item
  • WHEN the list renders within the section
  • THEN the newer normal item is ordered ahead of the older critical item

Scenario: DND-suppressed notification is still recorded

  • GIVEN Do-Not-Disturb mode is active
  • WHEN a notification is raised and its transient snackbar is suppressed
  • THEN the notification is recorded and appears as an item in the center

Requirement: Accessibility and keyboard obligations {#req-notification-center-a11y}

The trigger SHALL be a labelled button whose accessible name includes the unread count; new notifications SHALL announce via an ARIA live region (polite for normal, assertive only for critical); and the open panel SHALL be a focus-managed dropdown where Escape closes, focus is trapped while open, list items are arrow-navigable, and Enter/Space activates.

Scenario: Trigger accessible name includes unread count

  • GIVEN a notification center with 3 unread items
  • WHEN assistive technology queries the trigger button
  • THEN its accessible name includes the unread count (e.g. "Notifications, 3 unread")

Scenario: New notifications announce at the correct live-region politeness

  • GIVEN the ARIA live region is present
  • WHEN a normal-urgency notification arrives
  • THEN it is announced via the polite live region
  • WHEN a critical-urgency notification arrives
  • THEN it is announced via the assertive/alert live region

Scenario: Open panel is keyboard focus-managed

  • GIVEN the notification panel is open with focus inside it
  • WHEN the user presses Escape
  • THEN the panel closes
  • WHEN the panel is reopened and the user presses Arrow keys
  • THEN focus moves between list items
  • AND pressing Enter or Space activates the focused item

Referências