KQ-1266 · PUBLISH/PRESS
Publish Press
A publish button drawn as a sheet of paper, printed by a roller: a press sets an inked roller down at the start of the label, it bobs in place on a mirrored tween while the action is pending, and on success it rolls across the label on a symmetric move tween, inking the new word behind it through a clip that follows its track while the old label smears into a dragged, skewed band that dries away; the roller is a cylinder seen face on whose ribs turn at distance over radius, so a bigger drum turns slower. The ink is printed through a halftone screen whose dots grow with the ink load and starts wet and solid, drying back to its screen over the success hold; with a schedule set, a date stamp hovers while pending and slams down on the recoil spring instead, and pressing a published or scheduled button lifts the ink off in four strips on the exit ease in a cascade. It is a native button: Enter or Space presses it, Escape cancels while pending, and a failure leaves Retry.
Field notes: the spring survey
Waylight Journal · draft saved 2 min ago
draft · press publish
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/publish-press
Props
- value / defaultValue
- "draft" | "published" | "scheduled"
- default "draft"
- The document's status, controlled or initial. A controlled press reports through onValueChange and prints once the host answers.
- onValueChange
- (value: "draft" | "published" | "scheduled") => void
- default —
- Each status the document moves to, once its action has succeeded.
- onPublish
- (at: Date | null, signal: AbortSignal) => void | Promise<unknown>
- default —
- Publishes now (at is null) or schedules for at. Return a promise and the roller bobs until it settles; the signal aborts on Escape.
- onUnpublish
- (signal: AbortSignal) => void | Promise<unknown>
- default —
- Takes a published or scheduled document back to draft.
- schedule
- Date | number | null
- default —
- When set, a press schedules for this moment and stamps the date instead of rolling; the draft label becomes scheduleLabel.
- formatDate
- (date: Date) => string
- default UTC "Oct 9 · 09:00"
- How the stamped date reads. The default prints "Oct 9 · 09:00" from UTC parts so server and browser agree; pass your own for the reader's zone.
- state
- "idle" | "pending" | "success" | "error"
- default —
- Controlled action state. Every move goes through onStateChange and waits for this.
- onStateChange
- (state: "idle" | "pending" | "success" | "error") => void
- default —
- Each action state, from the press, the answer or the timer that caused it.
- ink
- number
- default 0.7
- How much ink the roller carries, 0 to 1: a pale, speckled halftone print or a dense, solid one.
- roller
- number
- default 0.5
- How big and heavy the roller is, 0 to 1: a quick small brayer or a slow wide drum whose ribs turn slower.
- smudge
- number
- default 0.5
- How much the old label smears as the roller drags over it, 0 to 1: the band's width, drag, skew and strength. 0 wipes clean.
- label
- string
- default "Publish"
- The text on a draft.
- scheduleLabel
- string
- default "Schedule"
- The text on a draft when schedule is set.
- pendingLabel
- string
- default "Publishing"
- The text while publishing.
- publishedLabel
- string
- default "Published"
- The word the roller inks.
- errorLabel
- string
- default "Retry"
- The text after an action failed; a press tries it again.
- inkColor
- string
- default "var(--accent-bright)"
- The ink: the printed word, the stamp and the roller. Any CSS colour.
- successHold
- number
- default 1400
- How long fresh ink stays wet before it dries and the button rests, in ms.
- errorHold
- number
- default 2600
- How long the error holds before the button rests, in ms.
- size
- "sm" | "md" | "lg"
- default "md"
- 32, 40 or 48 px tall; the roller and the strips scale with it.
- sound
- boolean
- default false
- Play the thock of the roller setting down and the stamp landing, and the swish of the roll and the peel, only within the press's own beat. No audio context exists unless this is on.
- disabled
- boolean
- default false
- Dims and disables the button.
- className
- string
- default —
- Merged onto the root's classes.
| Prop | Type | Default | Description |
|---|---|---|---|
| value / defaultValue | "draft" | "published" | "scheduled" | "draft" | The document's status, controlled or initial. A controlled press reports through onValueChange and prints once the host answers. |
| onValueChange | (value: "draft" | "published" | "scheduled") => void | — | Each status the document moves to, once its action has succeeded. |
| onPublish | (at: Date | null, signal: AbortSignal) => void | Promise<unknown> | — | Publishes now (at is null) or schedules for at. Return a promise and the roller bobs until it settles; the signal aborts on Escape. |
| onUnpublish | (signal: AbortSignal) => void | Promise<unknown> | — | Takes a published or scheduled document back to draft. |
| schedule | Date | number | null | — | When set, a press schedules for this moment and stamps the date instead of rolling; the draft label becomes scheduleLabel. |
| formatDate | (date: Date) => string | UTC "Oct 9 · 09:00" | How the stamped date reads. The default prints "Oct 9 · 09:00" from UTC parts so server and browser agree; pass your own for the reader's zone. |
| state | "idle" | "pending" | "success" | "error" | — | Controlled action state. Every move goes through onStateChange and waits for this. |
| onStateChange | (state: "idle" | "pending" | "success" | "error") => void | — | Each action state, from the press, the answer or the timer that caused it. |
| ink | number | 0.7 | How much ink the roller carries, 0 to 1: a pale, speckled halftone print or a dense, solid one. |
| roller | number | 0.5 | How big and heavy the roller is, 0 to 1: a quick small brayer or a slow wide drum whose ribs turn slower. |
| smudge | number | 0.5 | How much the old label smears as the roller drags over it, 0 to 1: the band's width, drag, skew and strength. 0 wipes clean. |
| label | string | "Publish" | The text on a draft. |
| scheduleLabel | string | "Schedule" | The text on a draft when schedule is set. |
| pendingLabel | string | "Publishing" | The text while publishing. |
| publishedLabel | string | "Published" | The word the roller inks. |
| errorLabel | string | "Retry" | The text after an action failed; a press tries it again. |
| inkColor | string | "var(--accent-bright)" | The ink: the printed word, the stamp and the roller. Any CSS colour. |
| successHold | number | 1400 | How long fresh ink stays wet before it dries and the button rests, in ms. |
| errorHold | number | 2600 | How long the error holds before the button rests, in ms. |
| size | "sm" | "md" | "lg" | "md" | 32, 40 or 48 px tall; the roller and the strips scale with it. |
| sound | boolean | false | Play the thock of the roller setting down and the stamp landing, and the swish of the roll and the peel, only within the press's own beat. No audio context exists unless this is on. |
| disabled | boolean | false | Dims and disables the button. |
| className | string | — | Merged onto the root's classes. |