Skip to content
islandcn
Esc
↑↓navigate↵open⌘Jpreview
On this page

Island

Draws the activity stack from the nearest IslandProvider at the top centre of its container or of the window.

import { Island } from "@/lib/island";

<Island>

Renders the pill. Put one inside the provider; it shows whatever the stack holds.

import { Island, IslandProvider } from "@/lib/island";

import { App } from "./app";

export const Root = () => (
  <IslandProvider>
    <Island placement="viewport" />
    <App />
  </IslandProvider>
);

Props

import type { CSSProperties } from "react";

interface IslandProps {
  placement?: "container" | "viewport";      // default "container"
  label?: string;                            // region name, default "Live activities"
  className?: string;
  style?: CSSProperties;
  keyline?: "auto" | "on" | "off";           // default "auto"
  backdrop?: string;                         // page colour behind the island, sets keyline strength
}
PropType
placement?"container" | "viewport"

"container" draws at the top centre of the nearest positioned ancestor, like a phone screen. "viewport" pins it to the top centre of the window.

Type"container" | "viewport"
Default"container"
label?string

Name of the island region for screen readers.

Typestring
Default"Live activities"
className?string

Class on the region element.

Typestring
style?CSSProperties

Style on the region element.

TypeCSSProperties
keyline?"auto" | "on" | "off"

The thin grey band outside the edge that keeps the island visible on a dark page. "auto" draws it when backdrop is dark, or in dark mode when no backdrop is given. See Keyline.

Type"auto" | "on" | "off"
Default"auto"
backdrop?string

The page colour behind the island, hex or rgb(). Its brightness sets the keyline's strength.

Typestring

Size

Sizes are CSS pixels, one per point on the reference phone. Containers wider than a phone clamp the island to phone width (402), so the expanded view is 374 wide everywhere and the pill never stretches across a desktop window.

Interaction and accessibility

  • The island is a region with the label above. The pill is a real button named after the activity, with aria-expanded. A polite live region reads activity labels when they change.
  • Every press swells the pill from touch-down. Still held at 0.1 s, it is a long-press and the island expands at motion.longPress (0.22 s after touch-down). Released sooner, it is a tap: the activity’s onPress runs and the swell springs back. Hold the detached capsule to expand the second activity. See Long-press and taps.
  • Enter or Space toggles expanded. Escape, or a press outside the island, collapses it. Focus inside content that goes away moves back to the pill.
  • Expanded content takes pointer events and focus, so buttons inside it work.

<Island> throws if it has no IslandProvider above it.

IslandView

A controlled island: you say which presentation to show and what goes in it, and it springs there when either changes. It ignores the activity stack and isn’t pressable, which makes it the right tool for a preview, a design tool or a frame-by-frame capture.

import { TitleStack } from "@/components/islandcn/island-text";
import { LinearProgress } from "@/components/islandcn/linear-progress";
import { ProgressRing } from "@/components/islandcn/progress-ring";
import { IslandView } from "@/lib/island";

export const UploadPreview = () => (
  <div style={{ position: "relative", height: 240 }}>
    <IslandView
      presentation={{
        kind: "expanded",
        leading: <ProgressRing value={0.62} size={44} />,
        center: <TitleStack title="Uploading report.pdf" subtitle="3.1 of 5 MB" />,
        bottom: <LinearProgress value={0.62} />,
      }}
    />
  </div>
);

Props

import type { CSSProperties, ReactNode } from "react";

import type { CompactContent, IslandClock, IslandMetricsOverrides, IslandMotionOverrides } from "@/lib/island";

interface IslandViewProps {
  presentation:
    | { kind: "idle" }
    | { kind: "compact"; leading?: ReactNode; trailing?: ReactNode }
    | { kind: "minimal"; attached: ReactNode; detached?: ReactNode; split?: { attached: CompactContent; detached: CompactContent } }
    | { kind: "expanded"; leading?: ReactNode; trailing?: ReactNode; center?: ReactNode; bottom?: ReactNode };
  contentKey?: string;                       // new key crossfades, same key updates in place
  pressed?: boolean;                         // the hold squish
  placement?: "container" | "viewport";
  label?: string;
  liveLabel?: string;
  clock?: IslandClock;
  metrics?: IslandMetricsOverrides;
  motion?: IslandMotionOverrides;
  reducedMotion?: "user" | "always" | "never";
  className?: string;
  style?: CSSProperties;
  keyline?: "auto" | "on" | "off";
  backdrop?: string;
}
PropType
presentationIslandPresentation

The shape and its content. minimal with split draws the split as the reference phone shows it: each shape shows its { leading, trailing } pair, the capsule at its fixed 62.18 width. attached and detached are the fallback views for content too wide for that, and the whole view when there is no split.

TypeIslandPresentation
contentKey?string

A new key crossfades the content; the same key updates it in place.

Typestring
Defaultpresentation.kind
pressed?boolean

Draws the hold swell, as if a finger were on the pill.

Typeboolean
Defaultfalse
placement?"container" | "viewport"

Same as on Island.

Type"container" | "viewport"
Default"container"
label?string

Region name for screen readers.

Typestring
Default"Live activity"
liveLabel?string

Text the polite live region reads.

Typestring
clock?IslandClock

Where time comes from.

TypeIslandClock
Defaultthe provider's, or a requestAnimationFrame clock
metrics?IslandMetricsOverrides

Geometry overrides.

TypeIslandMetricsOverrides
Defaultthe provider's, or none
motion?IslandMotionOverrides

Spring overrides.

TypeIslandMotionOverrides
Defaultthe provider's, or none
reducedMotion?"user" | "always" | "never"

Reduced-motion setting.

Type"user" | "always" | "never"
Defaultthe provider's, or "user"
className, style?

On the region element.

Type
keyline?"auto" | "on" | "off"

The thin grey band outside the edge that keeps the island visible on a dark page. "auto" draws it when backdrop is dark, or in dark mode when no backdrop is given. See Keyline.

Type"auto" | "on" | "off"
Default"auto"
backdrop?string

The page colour behind the island, hex or rgb(). Its brightness sets the keyline's strength.

Typestring

IslandView works inside or outside a provider. Inside one, it uses the provider’s clock, metrics and motion.

Was this page helpful?