# Live Activity Pill

> A floating status pill for live activities — idle, now playing, timer, incoming call and upload — whose dark shell springs between sizes while content crossfades through a blur.

- Collection: Components
- Tier: pro
- Docs: https://geturui.com/docs/dynamic-island
- Install: `npx shadcn@latest add @geturui-pro/dynamic-island`
- Dependencies: motion, lucide-react

## Usage

```tsx
"use client";
import * as React from "react";
import { DynamicIsland, type IslandView } from "@/components/ui/dynamic-island";

export function NowPlaying() {
  const [view, setView] = React.useState<IslandView>("music");
  const [playing, setPlaying] = React.useState(true);
  return (
    <DynamicIsland
      view={view}
      music={{ title: "Spring Physics", artist: "Damping Ratio", playing }}
      timer={{ seconds: 120, label: "Tea timer" }}
      call={{ name: "Maya Chen", initials: "MC" }}
      upload={{ name: "release-notes.pdf", progress: 64 }}
      onTogglePlay={() => setPlaying((p) => !p)}
      onAcceptCall={() => setView("idle")}
      onDeclineCall={() => setView("idle")}
      onTimerEnd={() => setView("idle")}
    />
  );
}
```

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| view | `"idle" | "music" | "timer" | "call" | "upload"` | — | Which live activity to show. Changing it morphs the shell. |
| music | `{ title: string; artist: string; playing?: boolean; coverInitials?: string }` | — | Now-playing data. Waveform bars bounce while playing is true. |
| timer | `{ seconds: number; label?: string }` | — | Countdown that starts each time the timer view appears. |
| call | `{ name: string; subtitle?: string; initials?: string }` | — | Incoming caller shown with accept and decline buttons. |
| upload | `{ name: string; progress: number }` | — | File name and 0–100 progress drawn as a ring. |
| expanded | `boolean` | — | Music view only: show the full player. Uncontrolled (tap to toggle) when omitted. |
| onExpandedChange | `(expanded: boolean) => void` | — | Called when the player expands or collapses. |
| onTogglePlay | `() => void` | — | Play / pause pressed in the expanded player. |
| onSkip | `(direction: -1 | 1) => void` | — | Previous or next track pressed. |
| onAcceptCall | `() => void` | — | Accept pressed in the call view. |
| onDeclineCall | `() => void` | — | Decline pressed in the call view. |
| onTimerEnd | `() => void` | — | Fired when the countdown reaches zero. |
| className | `string` | — | Classes for the outer wrapper. |

## Accessibility

- The island is a labelled region with a polite live region that announces activity changes (not every timer tick).
- Every control is a real button with a descriptive label, e.g. "Accept call from Maya Chen"; the compact player is a button with aria-expanded.
- With reduced motion the shell resizes with a short tween, content fades without blur and the waveform and ringing wiggle stop.
- The shell is always black, like the hardware cutout it imitates; text and accents keep contrast in both themes.

## Source

Pro item — send `Authorization: Bearer <registry token>` to include the source.
