crucıble

Winnow

A search input whose empty placeholder cycles (each sliding up as the next arrives), and whose typed text — on submit — rasterizes into fine grains that pour off left-to-right and blow away, clearing the field. A hairline underline fills in from center to one cool accent on focus. The submit is genuine; the vanish is decoration.

canvasfree

Installation

CLI

npx shadcn@latest add @crucible/winnow

Manual — install dependencies, then copy the source

npm install motion

Props

PropTypeDefaultDescription
valuestringControlled value. Omit for uncontrolled use (see defaultValue).
defaultValuestring""Initial value when uncontrolled.
onChange(value: string) => voidFires on every keystroke with the current string value.
onSubmit(value: string) => voidFires on genuine submit (Enter or the submit button) with the field value BEFORE it is cleared — the vanish is purely decorative over this call.
placeholdersstring[]a few search promptsRotating placeholder suggestions. Each slides up and out as the next arrives while the field is empty.
cycleIntervalnumber2800Milliseconds each placeholder is shown before cycling.
particleSizenumber1.7Base grain size in px (each grain jitters around it).
densitynumber1Grain density multiplier — higher packs more, finer grains.
scatterVelocitynumber3.4Base scatter speed in px/frame at 60fps.
scatterDirectionnumber-14Scatter direction in degrees. 0 = straight right, negative = upward. Grains blow off along this heading before gravity takes over.
gravitynumber0.09Downward acceleration applied to freed grains per frame.
dissolveDurationnumber0.9Seconds a single grain takes to scatter and fade.
submitClearsbooleantrueClear the field on submit (via the dissolve).
colorsstring[]soft whitesGrain palette, assigned per grain. Keep it a narrow near-white ramp — the vanish should read as fine sand, not confetti.
accentstringskyThe single cool accent for the focus underline.
errorbooleanfalseError state — reddens the underline and sets aria-invalid.
pausedbooleanfalseFreeze the placeholder cycle.
seednumber7Deterministic seed for grain jitter.
classNamestring
Also accepts all props of Omit< React.ComponentPropsWithoutRef<"input">, "value" | "defaultValue" | "onChange" | "onSubmit" | "color" | "size" >they pass through to the underlying element.

Honors prefers-reduced-motion with a designed static fallback, pauses offscreen and on hidden tabs, and passes className through.