suiss/uim · Interaction

Island

A floating pill at the top of the screen that shows ongoing activity and short notices, and expands on press.

Island is an open-source mobile interaction from suiss/uim for React Native and Expo, with SwiftUI and Jetpack Compose versions built from the same design tokens. The @esuiss/uim CLI copies its source into your project, so you own the code.

iOS
Island (compact) on iOS in light mode

Island floats below the top safe area and morphs between views: hidden, compact, minimal, expanded and notice. Its width, height and corner radius spring to the measured size of the active view, and views cross-fade as it changes.

Content is passed through slots. compactLeading and compactTrailing sit at the two ends of the compact pill, minimal is a single small circle, and IslandExpandedRegion children fill the expanded view. The island appears while it has compact or minimal content and hides when it has none. Pressing it expands; pressing outside, swiping up or the Android back button collapses it. notify shows a notice that replaces the current view and dismisses itself after 4 seconds, on press or on swipe up. Use it for ongoing activity such as a download, timer or call.

Installation

$ pnpm dlx @esuiss/uim@latest add island

The CLI also adds what it builds on: Icon, Text.

Manual

  1. Install the dependencies:
npx expo install cn phosphor-react-native react-native-gesture-handler react-native-reanimated react-native-safe-area-context react-native-svg react-native-worklets
  1. Add the pieces it builds on: Icon, Text, and the primitives components/primitives/hooks.tsx, components/primitives/portal.tsx.

  2. Copy the source into components/interactions/island.tsx:

uniwind/components/interactions/island.tsx
import { cn } from "cn"
import type { IconProps as PhosphorIconProps } from "phosphor-react-native"
import * as React from "react"
import {
  type AccessibilityActionEvent,
  AccessibilityInfo,
  Pressable,
  useWindowDimensions,
  View,
} from "react-native"
import { Gesture, GestureDetector } from "react-native-gesture-handler"
import {
  ReduceMotion,
  runOnJS,
  type SharedValue,
  useAnimatedStyle,
  1. Update the import paths to match your project.

Usage

import { IslandProvider, IslandExpandedRegion, Island, IslandRing } from "@/components/interactions/island"
<IslandProvider>
  <DownloadsExample initial={0.42} />
</IslandProvider>

Wrap the app in IslandProvider and render one Island. Pass compactLeading, compactTrailing and minimal only while there is activity to show, and add IslandExpandedRegions as children with a region of leading, trailing, center or bottom. Leading, center and trailing form a top row; bottom regions stack below it at full width. If only one of the compact and minimal slots is given, the island uses it for both views.

useIsland returns view, notice, expand, collapse, minimize, notify and dismiss. A notice is { icon?, title, description?, duration? }; pass a duration of 0 or Infinity to keep it until dismissed. Dragging the island pauses the notice timer. Use IslandRing for circular progress. The IslandNotice, IslandRegion and IslandView types are exported.

Examples

Expanded

Starts with the island expanded to show download details.

iOS
Island (expanded) on iOS in light mode

Minimal

Starts in the minimal view with only the progress ring.

iOS
Island (minimal) on iOS in light mode

Notice

Shows a notice on mount.

iOS
Island (notice) on iOS in light mode

Live activity

A download that starts on press, with buttons that minimize the island and send notices over it.

iOS
Island (live activity) on iOS in light mode

API Reference

IslandProvider

Holds the island mode, the notice and the notice timer.

PropType
Type
React.ReactNode
Default
—

IslandExpandedRegion

A region of the expanded view.

ExtendsIslandExpandedRegionProps

Island

The floating pill, rendered in a portal, with slots for the compact and minimal views.

ExtendsReact.ComponentProps<typeof View>
PropType
Type
string
Default
—
About
Content at the start of the compact pill.
Type
React.ReactNode
Default
—
About
Content at the end of the compact pill.
Type
React.ReactNode
Default
—
About
Content of the minimal view.
Type
React.ReactNode
Default
—
About
Name of a portal host to render into.
Type
string
Default
—

IslandRing

A small circular progress ring.

PropType
About
Progress from 0 to 1.
Type
number
Default
required
Type
string
Default
—

Accessibility

  • Notices are announced with their title and description when they are text.
  • The compact and minimal views are buttons that expose expanded and have activate and expand actions.
  • The expanded view has the group role, a collapse action and closes on the escape gesture.
  • The notice is a button with activate and dismiss actions.
  • Inactive views and the progress ring are hidden from screen readers.

Notes

  • The island uses bg-media with text-on-media.
  • Expanded and notice views are up to 384 points wide.
  • Size and fade animations respect the system reduce motion setting.