One pill that shows what's going on: small while something runs, wider for a moment when something new happens (with Undo or Reply), open into the whole list on a click.
Installation
When to use
- Things that run in the background and finish later: uploads, exports, syncs, with progress while small.
- A quick way back from what just happened: a deleted row with Undo, a message with Reply, without a toast stack.
When not to use
- Something the person must answer before going on: a dialog asks it.
- Long messages or many at once: a notifications page or inbox holds them better.
Usage
Status Island with your own data: a whole component you can paste as a page.
"use client"
import { StatusIsland } from "@/components/dashral/pro/status-island"
import { useState } from "react"
import { Trash2, Upload } from "lucide-react"
export default function App() {
const [deleted, setDeleted] = useState(true)
const items = [
{ id: "upload", title: "Uploading 3 files", icon: <Upload />, progress: 0.4 },
...(deleted
? [{ id: "deleted", title: "Invoice deleted", detail: "#1042 · Halcyon Studio", icon: <Trash2 />, actions: [{ label: "Undo", onClick: () => setDeleted(false) }] }]
: []),
]
return <StatusIsland items={items} idle="All synced" />
}API reference
StatusIsland
A status island: one pill that shows what's going on. Small while something runs, wider for a moment when something new happens (with its first action), open into the whole list on a click; Escape or a click outside closes it.
| Prop | Type | Default |
|---|---|---|
items | IslandItem[]Required. What's going on, oldest first: the newest is the one shown while small. | — |
idle | React.ReactNodeShown while nothing's going on (a clock, a brand); without it the island hides. | — |
peek | numberHow long a new item (or one turning success) stays wide, in ms. | 3200 |
width | numberIts width while open, in px (never wider than its container). | 360 |
label | stringNamed for screen readers; "Activity" by default. | "Activity" |
className | string | — |
classNames | StatusIslandClassNames | — |
type IslandAction
| Prop | Type | Default |
|---|---|---|
label | string | — |
onClick | () => void | — |
primary | booleanThe one that stands out (filled). | — |
type IslandItem
| Prop | Type | Default |
|---|---|---|
id | string | — |
title | string | — |
detail | string | — |
icon | React.ReactNode | — |
progress | number0 to 1: a ring while small, a bar while open. | — |
tone | "default" | "success" | "danger"success shows a check (and peeks once more); danger tints its icon. | — |
actions | IslandAction[] | — |
onDismiss | () => voidAdds × to its row while open. | — |
type StatusIslandClassNames
| Prop | Type | Default |
|---|---|---|
root | string | — |
island | string | — |
Keyboard
| Keys | Action |
|---|---|
| Tab | Reach the island |
| EnterorSpace | Open it into the whole list |
| ↑or↓ | Along the items while open |
| Tab | To an item's actions and × |
| Esc | Close; the focus returns to the island |
Accessibility
- A group with the role description "status island", named with its newest item ("Activity: Uploading 3 files. Press Enter to open"); open, it is a dialog.
- A live region reads the newest item's words as they change, so progress and results are heard without opening it.
- An action is a real button inside the island; × on an item is named "Dismiss <title>".
Motion
- One surface: its width and height move from the small pill to the wider peek and the open list on a long, barely bouncing spring (0.8 s, bounce 0.2), so a change mid-move carries on smoothly instead of starting again; its corner stays the same and the resize alone turns the pill into a panel.
- Its content unfurls down out of the pill line (from 90%, 8px up, a 5px blur) with the shell, and leaves in 0.08 s, before the shrinking shell can clip it; something new while it's wide gathers it back into the small pill first, then widens it again. Progress fills a ring while small and a bar while open, then turns to a check.
- The island's spring is the one spring in the Pro components, chosen because events reshape it mid-move; everything else in it is a smooth tween. With reduced motion it only fades.