KQ-892 · UNREAD/LINE
Unread Line
The rule that says where you stopped. The separator draws itself with a scaleX from its left origin on glide while the pill beside it arrives 4px up on flick, and as you read it keeps its key inside one AnimatePresence so the same rule travels to its new seat under layout while the messages re-seat around it. The pill is a real button that marks everything below it read, and when the last unread goes the rule fades and lifts away on the exit ease rather than springing.
KQ-892 · UNREAD/LINE
4 new messages
- Ines Moreau, 01:48, read.Gate is locked. Everything is on dock two tonight.
- Rui Baptista, 01:55, read.Loader keys are back on the hook by the office.
- Ines Moreau, 02:03, read.Good. I am off at three, Marta takes the handover.
- Marta Ferreira, 02:14, unread.Pallet 4471 is still held. Waylight Pay has not cleared it.
- Marta Ferreira, 02:16, unread.I left the paperwork on the desk under the sheet.
- Rui Baptista, 02:31, unread.Dock three is free from nine if the yard needs it.
- Marta Ferreira, 02:40, unread.Coldbrook depot closes Thursday from noon. Nothing leaves after eleven.
4 unread · first from Marta Ferreira
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/unread-line
Props
- messages
- UnreadMessage[]
- default —
- The thread, oldest first: id, author, text and a preformatted time.
- readCount / defaultReadCount
- number
- default messages.length
- Controlled or initial count of messages read from the top; the rule sits after them.
- onReadCountChange
- (count: number) => void
- default —
- Fires when the rule's own pill advances the boundary.
- onMarkAllRead
- () => void
- default —
- Fires from the pill, alongside onReadCountChange.
- onAnnounce
- (sentence: string) => void
- default —
- Fires once per frozen change sentence, for a parent's own status line.
- label
- string
- default —
- Names the thread for assistive technology.
- dividerLabel
- string
- default "New messages"
- The pill's word.
- upToDateLabel
- string
- default "You are up to date"
- What the header reads when nothing is unread.
- maxHeight
- number
- default 300
- Tallest the thread grows before it scrolls inside its own box.
| Prop | Type | Default | Description |
|---|---|---|---|
| messages | UnreadMessage[] | — | The thread, oldest first: id, author, text and a preformatted time. |
| readCount / defaultReadCount | number | messages.length | Controlled or initial count of messages read from the top; the rule sits after them. |
| onReadCountChange | (count: number) => void | — | Fires when the rule's own pill advances the boundary. |
| onMarkAllRead | () => void | — | Fires from the pill, alongside onReadCountChange. |
| onAnnounce | (sentence: string) => void | — | Fires once per frozen change sentence, for a parent's own status line. |
| label | string | — | Names the thread for assistive technology. |
| dividerLabel | string | "New messages" | The pill's word. |
| upToDateLabel | string | "You are up to date" | What the header reads when nothing is unread. |
| maxHeight | number | 300 | Tallest the thread grows before it scrolls inside its own box. |