Island

Pro

A floating pill that changes with what the viewer is doing. Each state is a view, and switching views morphs the pill to its new size while the contents crossfade in place.

Koala journal

Notes from building a design system

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add island

Usage#

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

Mount it once, in the layout, and drive view from the route. An island that each page renders for itself is a new island on every navigation, so it can only appear, never morph. Pin it yourself, the way you would any floating surface: fixed and centred, so it grows out from its middle, above env(safe-area-inset-bottom) on a phone.

Anatomy#

import {  Island,  IslandView,  IslandSwap,  IslandButton,  IslandIcon,  IslandSeparator,  IslandTitle,  IslandProgress,} from "@/components/ui/island"<Island view="…">  <IslandView value="…">    <IslandButton />    <IslandIcon />    <IslandSeparator />    <IslandSwap view="…">      <IslandView value="…">        <IslandTitle />      </IslandView>      <IslandView value="…">        <IslandProgress />      </IslandView>    </IslandSwap>  </IslandView></Island>

Variants#

Floating is the default: the elevated popover surface, with the controls resting muted until one of them holds a state. Solid is the inverted slab, the closest thing to the hardware island and the one to reach for over a photograph, a video or a canvas. It flips foreground and background instead of hardcoding black, so it stays legible in all four themes.

floating
solid

Sizes#

size is the island's own density axis, not the page's comfortable/compact knob: a floating control bar that shrinks to form-compact becomes hard to hit. md is the original's 44px pill and lg the touch step. At every size, padding plus half a control is half the pill, so the round controls sit concentric in its ends.

sm
md
lg

Views#

Declare every state as an IslandView and name the one on screen with view. Only that view is mounted, plus, for one beat, the one leaving. The pill eases to the new width and height while the old contents blur out and the new ones blur in. A view is a row; give it flex-col and the pill grows into a card, its corner easing from half the height to the card's radius on the same clock. IslandIcon holds a status glyph at a control's size, and IslandProgress is the readout and bar for a task that is running.

Swap#

IslandSwap is a region of a view that changes on its own, with the same crossfade and a small morph of its own: the title that gives way to the progress readout once you scroll. Name a view no child answers to and the region empties and collapses to nothing, which is how a “back to top” grows into the pill past the fold and out of it again at the top.

Buttons and menus#

One glyph makes an IslandButton a circle, a word makes it a pill. pressed lights the chip for a toggle that is on, shortcut puts a keycap in the tooltip, and caret turns it into a menu trigger whose caret points at the menu while it is open. Open menus upward, away from the edge the island sits on, and keep the Tooltip outside the trigger so the menu keeps its own hold on the button.

API reference#

Island

The morphing pill and the roving-focus root, role="toolbar", over Radix Toolbar.Root (forwards dir, loop and the rest; orientation is fixed horizontal). Every part accepts className, merged last.

PropTypeDefaultDescription
viewstring-The value of the IslandView on screen. Unset, the children are simply the contents and the pill still eases when they resize.
variant"floating" | "solid""floating"floating is the popover surface; solid inverts to the foreground ink.
size"sm" | "md" | "lg""md"Control height 32, 36 or 44px; the island's own axis, not the page density.
tooltipPlacementTooltipProps["placement"]"top"Where every control's tooltip opens.
aria-labelstring"Island"Names the toolbar for assistive tech.
Data attributeDescription
data-morph-readyPresent once the pill has measured, which arms its width, height and corner transition.
CSS variableDescription
--island-heightThe measured height, written by the morph. The corner is half of it, capped at --radius-4xl once the island grows into a card.

IslandView

One state of the island. Mounted while on screen; while leaving it is out of flow, inert and hidden from assistive tech. A row by default; flex-col turns the pill into a card. Forwards native <div> props.

PropTypeDefaultDescription
value*string-The name the nearest Island or IslandSwap shows with view={value}.
Data attributeDescription
data-stateenter while the view blurs in, exit while it blurs out.

IslandSwap

A region inside a view that swaps on its own, with its own crossfade and morph. Declare its states as IslandView children. Forwards native <div> props.

PropTypeDefaultDescription
viewstring-The IslandView on screen inside this region. A value no child answers to collapses the region to nothing.
Data attributeDescription
data-morph-readyPresent once the region has measured.

IslandButton

A control: one glyph makes a circle, a word a pill. Over Radix Toolbar.Button; forwards its props, including disabled and asChild.

PropTypeDefaultDescription
tooltipReact.ReactNode-Hover and focus hint. A string also becomes the aria-label when none is set.
shortcutstring-A keycap shown in the tooltip, e.g. "L". Display only.
pressedboolean-A toggle that is on: sets aria-pressed and lights the chip.
caretbooleanfalseA trailing caret that turns while its menu is open: the menu-trigger look.
Data attributeDescription
data-pressedPresent when pressed.
data-stateopen while a menu it triggers is open (set by the menu trigger).

IslandIcon

A status glyph (a spinner, a check, a live dot) on a control's square, so it lines up with the buttons. Renders a <span>.

IslandSeparator

A hairline between clusters of controls. Over Radix Toolbar.Separator.

IslandTitle

What you are looking at, in one line that truncates before it stretches the pill. Renders a <span>.

IslandProgress

A tabular readout and a thin Progress bar (a real role="progressbar"); the readout is hidden from assistive tech. Forwards native <div> props.

PropTypeDefaultDescription
value*number | null-0 to 100, or null for an unknown length: the readout goes and the bar sweeps.
transition"smooth" | "none""smooth"The fill's easing. Use none when the value follows the scroll, or the bar trails the scrollbar.
aria-labelstring"Progress"Names the bar.

FAQ#

Toolbar is a band of controls that keeps one shape. Dock is navigation that changes shape on demand: a rail you expand into a labelled menu. Island changes with context rather than on demand: its views follow what the viewer is doing (browsing, reading, waiting on a task), and it swaps them in place. All three sit on Radix Toolbar, so they share the roving focus and the arrow keys.