shadcn.io is not affiliated with official shadcn/ui
Badge Counter Notification Block for React Aria
A UI kit's count badge drawn on three header buttons: a number field sets the bell, a toggle switches number and dot, and each cell prints what is shown and what a screen reader hears.
Installation
Overview
Settle how a count looks and what it sounds like, with this badge counter for React with React Aria. It's the counter of Dotmarsh, a UI kit: three header buttons in hairline cells under a controls band, all inside a frame with a plus mark on each corner. A Number Input (0 to 999) sets the bell's count, a Toggle Group switches the badge between a number and a plain dot, the inbox holds 7 and the chat holds none. The rules are the same in every cell: nothing at 0, the number from 1 to 99, "99+" above that, and the button's name follows the count ("Notifications, 120 unread") whatever the badge shows, so a dot is never the only thing that says there is news. Each cell prints the count in a Sliding Number, which rolls, then what is shown and what is read out. It's built from Number Input, Toggle Group, Sliding Number, Badge and Guides, with a CSS entrance only. For a design system, a header or any place a number sits on an icon.
FAQ
What are the rules, and where do I change them?
CAP (99) is the most a badge prints; numberOf gives the number or "99+", shownOf says what the badge is in words (nothing, a dot, or the number), and spokenOf builds the button's name ("Notifications, 120 unread", or "Notifications, nothing unread"). Change CAP and every cell follows. The bell's field takes whole numbers from 0 to 999 (MAX_COUNT); the other two buttons are the constants INBOX_COUNT and CHAT_COUNT.
Why does the dot still say the count?
A dot shows that something is new and nothing more, so a screen reader would hear nothing at all. The button's name is built from the count in both styles ("Notifications, 120 unread"), and the cell prints the same sentence under "Read out". Only the picture changes between Number and Dot.
What stays local, and how do I make it real?
Nothing is saved: the bell's count is the field's value and the other two are typed constants. The buttons are drawn and press nothing. In your app put the same Mark on a real button, pass it your unread count and give the button aria-label from spokenOf; if you hide the badge from a screen reader, as this block does, the name is the only place the count is spoken.
How does the number field behave?
Type or use the steppers; the bell follows what you enter, and a polite status message says what the badge shows and how the button reads about a second after you stop. Anything that isn't a number counts as 0, and a number above 999 is brought back to 999 when you leave the field. React Aria commits what you typed when you leave the field, press Enter or use an arrow, rather than on each keystroke.
Is it accessible?
Each drawn button is an image with the name a real button would have, so a screen reader hears the same words a sighted visitor can read under "Read out". The badge itself is hidden from assistive technology because the name already carries the count. The field and the toggle group are named, the toggle group works with the arrow keys, and the badge colours are foreground on background in every theme.