KQ-1373 · CIPHER/WHEEL
Cipher Wheel
A headline that decrypts on a row of rotary dials: every letter turns its own drum through the character set with a ratchet feel — each notch a springs.flick step, summed in closed form from one clock — and locks onto its letter on springs.snap with one clean overshoot and a hairline flash, the locks cascading left to right, at random or from the middle before the housings dissolve into plain type. Pressing the headline, or Enter or Space on it, turns the dials again from their own letters, and the pointer thumbs a resting dial that the detent pulls back.
Fieldline Vault · key 4F-21
Sealed until you say so
Press Enter to turn the dials again.Every file in this folder opens with your key, never ours.
decrypting · 0 of 0 dials locked
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/cipher-wheel
Props
- text
- string
- default —
- The headline. Read once by assistive technology, and copied as written.
- as
- "h1" | "h2" | "h3" | "h4" | "p" | "span" | "div"
- default "span"
- The element the headline renders as.
- order
- "ltr" | "random" | "center"
- default "ltr"
- Which dial locks first: left to right, a seeded shuffle, or from the middle outward.
- speed
- number
- default 1
- Time scale of the whole run, 0.25 to 3: slow is chunky and every click legible, fast is brisk.
- charset
- "alpha" | "digits" | "hex" | "symbols"
- default "alpha"
- The ring each dial turns through. A glyph outside it is added to its own ring.
- characters
- string
- default —
- A custom ring of characters, in the order they roll past; overrides charset.
- duration
- number
- default 900
- How long the first dial turns before it locks, ms at speed 1.
- stagger
- number
- default 70
- The gap between one lock and the next, ms at speed 1; a long headline's cascade is compressed to 1200ms.
- delay
- number
- default 0
- Wait before the dials start, ms.
- trigger
- "mount" | "inView" | "hover" | "manual"
- default "inView"
- What starts the dials. hover rests decrypted and turns again on hover or focus.
- once
- boolean
- default true
- With inView, decrypt only the first time it is seen.
- active
- boolean
- default —
- With manual: true decrypts, false returns to the scrambled rest.
- interactive
- boolean
- default true
- A press, Enter or Space turns the dials again, and the pointer thumbs a resting dial.
- housing
- boolean
- default true
- Draw each dial's housing while it turns.
- flashColor
- string
- default "var(--accent-bright)"
- The hairline that flashes under a dial as it locks.
- seed
- number
- default 0
- Seeds the scrambled start and the random order.
- sound
- boolean
- default false
- Tick as each dial locks, for runs started by a press, Enter or Space. Off unless asked for.
- onLock
- (locked: number, total: number) => void
- default —
- A dial locked, or the dials were reset: how many are locked, of how many.
- onComplete
- () => void
- default —
- Every dial has locked and its housing has gone.
- className
- string
- default —
- Classes for the headline element; size, weight and colour are inherited by every dial.
| Prop | Type | Default | Description |
|---|---|---|---|
| text | string | — | The headline. Read once by assistive technology, and copied as written. |
| as | "h1" | "h2" | "h3" | "h4" | "p" | "span" | "div" | "span" | The element the headline renders as. |
| order | "ltr" | "random" | "center" | "ltr" | Which dial locks first: left to right, a seeded shuffle, or from the middle outward. |
| speed | number | 1 | Time scale of the whole run, 0.25 to 3: slow is chunky and every click legible, fast is brisk. |
| charset | "alpha" | "digits" | "hex" | "symbols" | "alpha" | The ring each dial turns through. A glyph outside it is added to its own ring. |
| characters | string | — | A custom ring of characters, in the order they roll past; overrides charset. |
| duration | number | 900 | How long the first dial turns before it locks, ms at speed 1. |
| stagger | number | 70 | The gap between one lock and the next, ms at speed 1; a long headline's cascade is compressed to 1200ms. |
| delay | number | 0 | Wait before the dials start, ms. |
| trigger | "mount" | "inView" | "hover" | "manual" | "inView" | What starts the dials. hover rests decrypted and turns again on hover or focus. |
| once | boolean | true | With inView, decrypt only the first time it is seen. |
| active | boolean | — | With manual: true decrypts, false returns to the scrambled rest. |
| interactive | boolean | true | A press, Enter or Space turns the dials again, and the pointer thumbs a resting dial. |
| housing | boolean | true | Draw each dial's housing while it turns. |
| flashColor | string | "var(--accent-bright)" | The hairline that flashes under a dial as it locks. |
| seed | number | 0 | Seeds the scrambled start and the random order. |
| sound | boolean | false | Tick as each dial locks, for runs started by a press, Enter or Space. Off unless asked for. |
| onLock | (locked: number, total: number) => void | — | A dial locked, or the dials were reset: how many are locked, of how many. |
| onComplete | () => void | — | Every dial has locked and its housing has gone. |
| className | string | — | Classes for the headline element; size, weight and colour are inherited by every dial. |