SIG—10 Signature system
Annotator
Review pins on anything: drop numbered pins, discuss in threads, resolve and reopen, and find every conversation in the list beside the work.
Live — this one is real
Illustrative review. Press C (or the Comment button), then click the draft to drop a pin; ⌘/Ctrl+Enter posts. Drag pins to move them, or use arrow keys on a focused pin.
Pricing page
3 open · 1 resolved
Pricing · draft 3
Plans for every team.
Start free, invite your team when the work needs it, and keep every draft.
Press C for comment mode, then click the work or press Enter to place a pin. Arrow keys move a focused pin.
Usage
"use client";
import { useState } from "react";
import { Annotator, type Annotation } from "mizu-ui";
export function Example() {
const field = (ground: string, mark: string) =>
`data:image/svg+xml,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" width="720" height="360"><pattern id="d" width="36" height="36" patternUnits="userSpaceOnUse"><path d="M18 13l5 5-5 5-5-5z" fill="${mark}"/></pattern><rect width="720" height="360" fill="${ground}"/><rect width="720" height="360" fill="url(#d)"/></svg>`)}`;
const [notes, setNotes] = useState<Annotation[]>([
{
id: "a",
x: 0.3,
y: 0.4,
author: "Rin",
body: "Can this headline be shorter?",
},
]);
return (
<Annotator
label="Homepage"
author="You"
annotations={notes}
onAnnotationsChange={setNotes}
>
<img src={field("#141413", "#7e776b")} alt="Homepage draft" />
</Annotator>
);
}
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
Homepage
1 open
Press C for comment mode, then click the work or press Enter to place a pin. Arrow keys move a focused pin.
Keyboard
- C
- Toggle comment mode
- Enter
- In comment mode: place a pin at the centre
- Alt+←→↑↓
- While writing: move the new pin (Shift: further)
- ⌘/Ctrl+Enter
- Post the comment or reply
- ←→↑↓
- On a pin: move it
- Esc
- Close the thread or discard the draft
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/annotatorEvery component imports from its own path, its name in kebab case. To paste the example, replace its mizu-ui import with:
import { Annotator, type Annotation } from "@/components/ui/mizu/annotator";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/Annotator.tsx
Source is included in the npm package under src/signature/Annotator.tsx.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label / children | string / ReactNode | — | Region name and the work under review: an image, a page or a live composition. |
| annotations / defaultAnnotations / onAnnotationsChange | Annotation[] | — | id, x and y (0–1 of the surface), author, body, time, resolved and replies. |
| author / now | string | — | Name and time label for new comments and replies. Omit author for read-only review. |
| selected / defaultSelected / onSelectedChange | string | null | — | The open thread. |
| createId | () => string | — | Id factory for new comments and replies. |
Accessibility
Pins are buttons named with their number, author, state and text, in reading order; threads are labelled dialogs that return focus to their pin. C toggles comment mode, where Enter places a pin that arrow keys (or Alt+arrows while typing) move before posting. Every action is announced; the comment list offers the same threads without the surface.
Motion
Pins drop in on a spring and send out a single impact ring. Threads unfold from the side of their pin; a pin being placed breathes until it is posted. Resolving settles the pin into a quiet outline. Reduced motion places and opens everything directly.