SIG—04 Signature system
Plane
An infinite, zoomable surface for boards, maps and diagrams — inertia, pinch, a minimap and a camera that follows keyboard focus.
Live — this one is real
Drag cards to rearrange. Drag the background to travel, pinch or Ctrl-scroll to zoom, tab between cards and the view follows. Illustrative content.
Arrow keys move the view, plus and minus zoom, 0 fits everything, 1 returns to 100%. Tab moves between items and the view follows.
Usage
"use client";
import { useState } from "react";
import { Plane, PlaneItem } from "mizu-ui";
export function Example() {
const [notes, setNotes] = useState([
{ id: "a", x: 0, y: 0, text: "Research" },
{ id: "b", x: 260, y: 90, text: "Prototype" },
{ id: "c", x: 90, y: 260, text: "Ship it" },
]);
return (
<Plane label="Planning board">
{notes.map((n) => (
<PlaneItem
key={n.id}
id={n.id}
x={n.x}
y={n.y}
width={200}
label={n.text}
onMove={(x, y) =>
setNotes((all) =>
all.map((a) => (a.id === n.id ? { ...a, x, y } : a)),
)
}
>
<p
style={{
margin: 0,
padding: 18,
background: "var(--mizu-ink-2)",
border: "1px solid var(--mizu-line-bright)",
}}
>
{n.text}
</p>
</PlaneItem>
))}
</Plane>
);
}
It starts with "use client" because it keeps state or passes functions to components, so it pastes into a Next.js page as it is. Other React setups ignore the directive.
This code, running — no configuration beyond the data
Arrow keys move the view, plus and minus zoom, 0 fits everything, 1 returns to 100%. Tab moves between items and the view follows.
Keyboard
- ←→↑↓
- Travel (Shift for larger steps)
- +−
- Zoom
- 0·1
- Fit everything · 100%
- Tab
- Next item — the view follows
- ←→↑↓
- On a draggable item: nudge it (Shift: 48px)
- Ctrl+scroll
- Zoom at the pointer; double-click zooms in
Source & setup
Add this component to your project with the shadcn CLI. The command copies its source into your configured UI directory, with base.css, Mizu’s tokens, and the stylesheets that hold its rules. Each rule lives in one stylesheet, so components you add together never repeat CSS. In a project without a components.json, run npx shadcn@latest init 0xuser64bit/mizu/preset first: plain init installs shadcn’s theme into your global CSS. Then load the fonts.
npx shadcn@latest add 0xuser64bit/mizu/planeEvery component imports from its own path, its name in kebab case. To paste the example, replace its mizu-ui import with:
import { Plane, PlaneItem } from "@/components/ui/mizu/plane";For npm imports, import mizu-ui/styles.css once. Fonts are optional via mizu-ui/fonts.css. Override --mizu-* tokens or use className for local styling. Requires React and Motion. A Client Component: its source starts with “use client”.
Inspect signature/Plane.tsx
Source is included in the npm package under src/signature/Plane.tsx.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label / children | string / ReactNode | — | Accessible name and world content (usually PlaneItems). |
| view / defaultView / onViewChange | { x, y, zoom } | — | Camera: the world point at the centre and the zoom. Frames the content by default. |
| minZoom / maxZoom | number | 0.15 / 4 | Zoom limits. |
| grid / minimap / coordinates | "dots" | "lines" | "none" / boolean / boolean | "dots" / true / true | Surface texture and instruments. |
| tools | ReactNode | — | Extra toolbar controls beside zoom and Fit. |
| ref | PlaneHandle | — | fit(), zoomBy(factor), flyTo(box | point) and toWorld(clientX, clientY). |
| PlaneItem: id / x / y / width / height / label | string / number | — | World position and size; measured when width or height is omitted. |
| PlaneItem: onMove / onMoveEnd | (x, y) => void | — | Makes the item draggable and nudgeable. |
Accessibility
The viewport is a focusable canvas group with keyboard travel and zoom. Items are named groups in reading order; tabbing to one flies the camera to it. Draggable items nudge with arrow keys. Plain scrolling only pans once the canvas is active, so the page never traps the wheel; a hint explains Ctrl-scroll.
Motion
The camera glides in log space and pulls back on long flights before settling in; drags coast to rest with inertia. The zoom-adaptive dot grid re-densifies as you scale. Reduced motion jumps directly and removes coasting.