Skip to content

No.71 · Buttons· New

Koi Pond

The button is a little pond with fish hanging deep and still. Point at it and they rise and swim laps just inside the rim, bending towards your pointer while rings spread where you move; press to drop a pellet and they dart for it.

The prompt

Copy this text

Automatic copying is unavailable. Copy the selected text using your device’s copy command.

Written for the variant you picked above: Koi

Build a pill button that is a small pond with fish in it. Props: children, pond (one of three looks), fish (how many, default 3), onClick, type and motion. The button is 64px tall and at least 232px wide, its face a water gradient (a deep colour pooled at the bottom, a lighter mid tone lit from the top left), an inset rim, an inner top shadow so it reads as a recess, and a surface layer over the fish: a slowly drifting pattern of faint concentric caustic rings, a top sheen and a soft dark bed under the label. The label is light, with a dark text shadow so it reads over any fish. The fish live on a canvas filling the button (DPR capped at 2). Each fish is a chain of 8 joints 3.6px apart, the head steering and the rest following like a rope (each joint pulled to sit one segment behind the one before), so the body bends naturally. Draw it as round-capped strokes between joints whose widths follow a koi profile (widest a third of the way back) and whose colours are the fish's coat, patch by patch — that's what gives the koi its blotches. Add two sculling pectoral fins, a fan tail made of two curves that wags with the swim phase, a faint highlight down the back, and a soft shadow offset onto the pond floor. Swimming: the lane is the button's outline pulled 13px in. Each fish has a position along the lane that advances with its speed; its goal is the lane point 16px ahead, blended 28% towards the pointer while hovered. The head turns towards the goal at a limited rate (5.5 rad/s) and moves at the current speed, with a little sway from the swim phase. At rest the fish are deep — 32% opacity and still; hovering raises them to full opacity and 56px/s over about a third of a second. They swim the way you came in. Moving the pointer leaves pairs of thin elliptical ripples every 22px. Pressing drops a pellet with a big ripple: every fish turns faster and darts at twice the speed; the first to reach it eats it (a small ring) and they all take up their laps again from where they are. The animation loop runs only while hovered or settling, and each frame draws all fish, the pellet and the ripples. Keyboard focus wakes the fish; touch gets a pellet on press and a 2.6s swim. Reduced motion draws the fish risen and still, with no ripples. Selected variant — Koi (koi): A jade pond (#06313a deep, #0f5b5f mid, #9fe0d0 glint) with three koi: a white fish with red-orange saddles, a black, white and red one, and a butter-gold one. Pale green ground #e6efe9, label #f2fbf7. This selection takes precedence over references to other themes or options in the general brief. Implement this selected appearance; do not add a variant picker or alternative appearances unless requested. Implementation requirements: Use React and TypeScript. Keep the component self-contained, with uniquely scoped CSS and no animation libraries. Include required imports, styles and font setup in the result. Interaction: Point at the button and the fish rise and swim laps, bending towards the pointer; moving leaves ripples; press to feed them. Motion: Fish rise over about 300ms and swim at 56px/s with a swaying body and wagging tail; ripples spread over 0.9s. Reduced motion shows the fish still. Accessibility: A real button with its text label; the canvas and the surface layer are decorative and hidden. A dark bed and text shadow keep the label readable over the fish. Responsive behavior: 64px tall and at least 232px wide; the lane follows whatever width the label needs. Touch: A press drops a pellet and the fish swim for a couple of seconds. Preserve keyboard access, visible focus and reduced-motion behavior. Keep fictional demo data and simulated service calls in a separate usage example; expose callbacks for real actions and recover from rejected requests. Keep usage-example links inside the demo: use local buttons or prevent navigation. Demonstration links must not navigate to Vitrine routes or open other pages; preserve real URL support in the reusable component itself. Verify the result at narrow and wide viewport sizes, with keyboard input and reduced motion enabled.

Notes

Interaction
Point at the button and the fish rise and swim laps, bending towards the pointer; moving leaves ripples; press to feed them.
Motion
Fish rise over about 300ms and swim at 56px/s with a swaying body and wagging tail; ripples spread over 0.9s. Reduced motion shows the fish still.
Accessibility
A real button with its text label; the canvas and the surface layer are decorative and hidden. A dark bed and text shadow keep the label readable over the fish.
Responsive
64px tall and at least 232px wide; the lane follows whatever width the label needs.
On touch
A press drops a pellet and the fish swim for a couple of seconds.

Loading components…