Skip to content

SIG—17 Signature system

SplitFlap

A mechanical flip display: every character falls forward through the drum to its new value, cell after cell, and lands with a slap.

Live — this one is real

Board

Every character falls forward through the drum, cell after cell, and lands with a slap. Screen readers get the settled text. The timetable is invented.

Departures
TimeDestinationPlat.Status
08:42KANAZAWA4ON TIME
08:50NAGANO11ON TIME
09:03SENDAI7DELAYED
09:15NIIGATA2ON TIME
09:21HAKODATE9ON TIME

Usage

tsx
import { useState } from "react";
import { SplitFlap, Button } from "mizu-ui";

export function Example() {
  const [n, setN] = useState(0);
  const states = ["ON TIME", "BOARDING", "DEPARTED"];
  return (
    <div style={{ display: "flex", gap: 16, alignItems: "center" }}>
      <SplitFlap value={states[n % 3]!} length={8} label="Status" live />
      <Button variant="ghost" onClick={() => setN(n + 1)}>
        Advance
      </Button>
    </div>
  );
}

This code, running — no configuration beyond the data

Status: ON TIME

Source & setup

Add this component to your project with the shadcn CLI. The command copies its source and styles into your configured UI directory.

sh
npx shadcn@latest add 0xuser64bit/mizu/split-flap

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.

Inspect signature/SplitFlap.tsx

Source is included in the npm package under src/signature/SplitFlap.tsx.

Open full source ↗

Props

PropTypeDefaultDescription
value / length / alignstring / number / left | right—The text, padded or trimmed to `length` cells (its own length by default).
charactersstring—The drum in falling order — FLAP_CHARACTERS by default. Characters outside it arrive in one flip.
flip / staggernumber—Milliseconds for one flap to fall (70) and between neighbouring cells starting (35).
label / livestring / boolean—Read before the value; live displays announce each new value politely.
tone / onSettleSignatureTone / (value) => void—Glyph colour, and a call when every cell has arrived.
flapPath()(from, to, characters, most?) => string[]—The flaps a cell passes on its way, for displays of your own.

Accessibility

The cells are hidden from assistive technology; the value and its label sit beside them as plain text, so a display reads as words rather than letters, inside a table cell or a button alike. A live display announces each new value once, not every flap.

Motion

Each cell falls forward through the drum like the real mechanism: the top leaf folds down and darkens while the next character lands with a small overshoot, and neighbouring cells start a beat apart so a change ripples along the line. Long journeys skip ahead to the last dozen flaps. Reduced motion shows the new value at once.

Works alongside