motif svelteArc

Notification Center ported

Every update in one calm, anchored surface.

A bell-anchored panel that lists updates, with unread counts, an all/unread filter and per-row actions.

Live specimen

Loading demo…

Guidance

When to use

  • When the product produces asynchronous updates the user should be able to catch up on.
  • When updates need triage: mark read, mark all read, dismiss, clear read.
  • When the count matters — the badge and the panel share one source of truth.

When not to use

  • For a single, time-critical alert — use a toast or an inline banner.
  • For actions that must block — use a dialog.
  • When there is nothing to accumulate — a static list is simpler.

Installation

npx shadcn-svelte@latest add @arcui/notification-center

Usage

<script lang="ts">
  import NotificationCenter from '$lib/components/notification-center/notification-center.svelte';

  const items = [
    { id: '1', title: 'New comment', description: 'Ada replied.', time: '2m', tone: 'info' },
    { id: '2', title: 'Deploy finished', time: '1h', tone: 'success', read: true }
  ];
</script>

<NotificationCenter notifications={items} />

Variants & Examples

Default with Status Tones

Includes unread items with info, success, and warning tones.

<NotificationCenter notifications={items} />

With Avatar Actor

Shows user portrait avatar instead of generic status icon.

<NotificationCenter
  notifications={[
    { id: '1', title: 'Ana commented', time: '1h', actor: { name: 'Ana', photo: '/ana.jpg' } }
  ]}
/>

Controlled Open State

Synchronizes panel visibility externally via open and onOpenChange.

<NotificationCenter open={panelOpen} onOpenChange={(o) => panelOpen = o} {notifications} />

API Reference

PropTypeDefaultDescription
notificationsNotificationItem[]—The update list. Captured once (ARC uses useState(initial)).
labelstring'Notifications'Panel and trigger label.
onReadChange(item, read) => void—Fired when a row is marked read or unread.
onDismiss(item) => void—Fired when a row is dismissed or cleared.
openboolean—Controlled open state; omit for uncontrolled.
onOpenChange(open: boolean) => void—Open-state callback.
avoidCollisionsbooleantrueLet the panel flip to stay in view.

Keyboard Interactions

KeyAction
Enter / SpaceOpen or close the panel from the trigger.
EscapeClose the panel.
TabMove between the view switch, mark-all, rows and footer actions.

Accessibility

  • The trigger is a labelled button whose aria-label includes the unread count.
  • Unread state is conveyed by a dot plus the row label (", unread"), not colour alone.
  • The summary line is aria-live="polite" so the count is announced when it changes.
  • Rows are role="listitem" inside a labelled role="list".

Motion

Phase 1 still-state: no animation. Phase 2 wires the badge pop, the odometer count, the view-highlight layoutId slide, the row cascade (--index), the height-morphing rows and the panel enter/exit.

Notes for AI

  • `notifications` is captured on mount — later prop changes are ignored, matching ARC.
  • Do not add colour-only unread signalling; keep the dot and the aria-label.
  • Avatar rows require the avatar component; the tone icons are Lucide.

Source of truth: registry/components/notification-center/notification-center.tsx