KQ-1762 · THEME/SORTER

Theme Sorter

A feedback inbox you sort by hand: a card lifts after 4px of travel and follows the pointer 1:1 as a copy above both columns, leaning with its speed, while the theme under it swells on springs.snap. Dropped on a theme it is squeezed in — narrowing on springs.glide and flattening 60ms behind on the same spring toward the theme's centre — the theme gulps on springs.recoil as its tally rolls up on snap, and once it is in the themes re-rank by count with a layout glide; dropped anywhere else it springs home with the release velocity. The inbox is one tab stop with roving focus: Up and Down move, T or Enter opens a theme menu with type-ahead and New theme, A or Delete archives, and a failed tag springs the card back into its slot with a note.

KQ-1762 · THEME/SORTER

Feedback inbox

9 to sort · 5 themes

  • Onboarding4
  • Permissions3
  • Pricing3
  • Exports2
  • Mobile app1
  • “The jump from Team to Business is too steep for a crew of twelve.”

    Ana Ruiz · ColdbrookTeam3 h ago
  • “Setting up our first workspace took ten minutes. Lovely.”

    Theo Park · BasinworksStarter5 h ago
  • “I export the same report every Monday by hand. Can it just arrive?”

    Mira Holt · ColdbrookBusiness9 h ago
  • “The app logs me out every time I switch to the camera.”

    Jonah Reyes · BasinworksTeam14 h ago
  • “We need viewers who can comment but not edit.”

    Priya Shah · ColdbrookEnterprise1 d ago
  • “Annual billing saved us a lot. Thanks for adding it.”

    Sam Okoro · BasinworksBusiness1 d ago
  • “Invite emails landed in spam for half the team.”

    Lena Fox · ColdbrookTeam2 d ago
  • “CSV exports drop the leading zeros from our part numbers.”

    Ravi Menon · BasinworksTeam2 d ago
  • “Dark mode on the phone at night would be great.”

    Elif Kaya · ColdbrookStarter3 d ago

Up and Down move between quotes. T or Enter chooses a theme, A or Delete archives. Cards can also be dragged onto a theme.

9 in inbox · 5 themes

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/theme-sorter

Props

items / defaultItems
FeedbackItem[]
default — / defaultThemeSorterItems
Feedback as { id, quote, customer, company, source: "email" | "call" | "survey" | "chat" | "review", plan, sentiment: "positive" | "neutral" | "negative", at, themeId?, archived? }, controlled or initial. Untagged, unarchived items wait in the inbox; tagged ones count toward their theme.
onItemsChange
(items: FeedbackItem[]) => void
default —
Fires with every item after a tag, an archive or a failed tag's revert.
themes / defaultThemes
FeedbackTheme[]
default — / defaultThemeSorterThemes
Themes as { id, name }, controlled or initial; shown most-counted first.
onThemesChange
(themes: FeedbackTheme[]) => void
default —
Fires with every theme after one is created.
onTag
(itemId: string, themeId: string) => void | Promise<unknown>
default —
An item was tagged by a drop or the theme menu. Return a promise; a rejection springs the item back into the inbox with a note and rolls the tally back.
onCreateTheme
(theme: FeedbackTheme) => void
default —
A theme was created from New theme; the item it was opened on is tagged into it.
onArchive
(itemId: string) => void
default —
An item was archived without a theme.
bucketStyle
"tray" | "jar" | "row"
default "tray"
How a theme looks: a tray with its count, a share bar coloured by its sentiment mix and a percentage; a jar whose level rises with its count; or a compact row. Chips at narrow widths keep a hint of each.
density
"compact" | "regular" | "roomy"
default "regular"
Card padding, quote size and gaps.
sentimentPalette
"signal" | "duo" | "mono"
default "signal"
Sentiment colours: green, ink and red; cobalt, ink and amber; or ink alone, told apart by the dot, ring and diamond marks.
now
number | Date
default defaultThemeSorterNow
The moment ages count from, so the server and the browser print the same text.
status
"ready" | "loading" | "error"
default "ready"
Whether the items have arrived: loading shows skeleton cards and themes, error a line with Try again.
onRetry
() => void
default —
Try again was pressed after the items failed to load.
title
string
default "Feedback inbox"
The inbox's heading and accessible name.
maxHeight
number
default 540
Tallest the surface grows before its columns scroll, in px.
disabled
boolean
default false
Nothing can be dragged, tagged, created or archived.
className
string
default —
Classes for the surface.