KQ-1722 · PATINA/BOARD
Patina Board
A kanban board whose cards weather while they wait: every day still adds a faint patina — tarnish gathering at the edges first, then a mottled bloom from a seeded turbulence mask — mixed with the ink so it darkens on a light page and lifts like dust on a dark one, and a corner counter turns warn when a card goes stale and offers to nudge its owner. Moving or editing a card polishes it: a band of light crosses its face on a move-eased tween while a clip follows its edge and wipes the patina off, and the counter rolls to today on snap. The Stale filter gathers the oldest cards to the top of each column on glide; cards move by drag on flick and snap or by keys — Space lifts, arrows carry, Space drops, Escape puts back, Enter renames, N nudges.
Basinworks facilities
14 requests · 7 stale
Arrow keys move between cards. Space picks a card up; arrows carry it, Space drops it, Escape puts it back. Enter renames a card; N nudges the owner of a stale one.
14 requests · 7 stale
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/patina-board
Props
- columns
- PatinaColumn[]
- default defaultPatinaColumns
- The columns in order ({ id, title, status? }); a loading column draws outlines.
- cards / defaultCards
- PatinaCard[]
- default defaultPatinaCards
- Controlled or initial cards ({ id, columnId, title, key?, owner?, still }); still is when the card last moved or changed.
- onCardsChange
- (cards: PatinaCard[]) => void
- default —
- Fires with every card after a move or an edit; the touched card's still is now.
- onMove
- (move: PatinaMove) => void
- default —
- Fires from the drop or key that moved a card.
- onEdit
- (cardId: string, title: string) => void
- default —
- Fires when a card's title is edited in place.
- onNudgeOwner
- (card: PatinaCard) => void
- default —
- Nudge was pressed (or N) on a stale card, for its owner.
- staleFirst / defaultStaleFirst
- boolean
- default false
- Controlled or initial Stale filter: the oldest cards gathered to the top of each column.
- onFilterStale
- (on: boolean) => void
- default —
- Fires from the toggle that turned the Stale filter on or off.
- patinaRate
- number
- default 0.08
- How much patina a card gains per day still, as a share of full; full at 1 / rate days.
- cardStyle
- "plain" | "raised" | "outline"
- default "plain"
- Cards drawn plain with a hairline, raised on a shadow, or as an outline over the column.
- tone
- "copper" | "rust" | "dust" | "ink"
- default "copper"
- The patina's pigment; it follows the theme.
- staleAfter
- number
- default 7
- Days still before a card counts as stale: its counter warns and its owner can be nudged.
- now
- number
- default defaultPatinaNow
- Today, ms since the epoch.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the board's cards have arrived.
- onRetry
- () => void
- default —
- "Try again" was pressed after the board failed to load.
- title
- string
- default "Basinworks facilities"
- The heading.
- height
- number
- default 460
- The board's height in px; columns scroll inside it.
- label
- string
- default —
- The board's accessible name; defaults to the title.
- disabled
- boolean
- default false
- Freezes the board: no drags, keys, edits or nudges.
- className
- string
- default —
- Classes for the outer panel.
| Prop | Type | Default | Description |
|---|---|---|---|
| columns | PatinaColumn[] | defaultPatinaColumns | The columns in order ({ id, title, status? }); a loading column draws outlines. |
| cards / defaultCards | PatinaCard[] | defaultPatinaCards | Controlled or initial cards ({ id, columnId, title, key?, owner?, still }); still is when the card last moved or changed. |
| onCardsChange | (cards: PatinaCard[]) => void | — | Fires with every card after a move or an edit; the touched card's still is now. |
| onMove | (move: PatinaMove) => void | — | Fires from the drop or key that moved a card. |
| onEdit | (cardId: string, title: string) => void | — | Fires when a card's title is edited in place. |
| onNudgeOwner | (card: PatinaCard) => void | — | Nudge was pressed (or N) on a stale card, for its owner. |
| staleFirst / defaultStaleFirst | boolean | false | Controlled or initial Stale filter: the oldest cards gathered to the top of each column. |
| onFilterStale | (on: boolean) => void | — | Fires from the toggle that turned the Stale filter on or off. |
| patinaRate | number | 0.08 | How much patina a card gains per day still, as a share of full; full at 1 / rate days. |
| cardStyle | "plain" | "raised" | "outline" | "plain" | Cards drawn plain with a hairline, raised on a shadow, or as an outline over the column. |
| tone | "copper" | "rust" | "dust" | "ink" | "copper" | The patina's pigment; it follows the theme. |
| staleAfter | number | 7 | Days still before a card counts as stale: its counter warns and its owner can be nudged. |
| now | number | defaultPatinaNow | Today, ms since the epoch. |
| status | "ready" | "loading" | "error" | "ready" | Whether the board's cards have arrived. |
| onRetry | () => void | — | "Try again" was pressed after the board failed to load. |
| title | string | "Basinworks facilities" | The heading. |
| height | number | 460 | The board's height in px; columns scroll inside it. |
| label | string | — | The board's accessible name; defaults to the title. |
| disabled | boolean | false | Freezes the board: no drags, keys, edits or nudges. |
| className | string | — | Classes for the outer panel. |