Badge
On this page
A badge wraps something and pins a marker to its top corner: unread messages on an inbox button, an online dot on an avatar. It takes the thing it marks as children.
Basic badge
stack({ direction: 'row', gap: 'xl', align: 'center' },
badge({ content: 4 }, button({ variant: 'soft', color: 'neutral' }, 'Inbox')),
badge({ content: 12 }, avatar({ name: 'Ada Lovelace' })),
)Colors
stack({ direction: 'row', gap: 'xl', align: 'center', wrap: true },
badge({ content: 3, color: 'primary' }, button({ variant: 'soft', color: 'neutral' }, 'Primary')),
badge({ content: 3, color: 'neutral' }, button({ variant: 'soft', color: 'neutral' }, 'Neutral')),
badge({ content: 3, color: 'success' }, button({ variant: 'soft', color: 'neutral' }, 'Success')),
badge({ content: 3, color: 'warning' }, button({ variant: 'soft', color: 'neutral' }, 'Warning')),
badge({ content: 3, color: 'danger' }, button({ variant: 'soft', color: 'neutral' }, 'Danger')),
)Maximum
A count above max renders as n+, so a badge never grows wide enough to unbalance the thing it sits on.
stack({ direction: 'row', gap: 'xl', align: 'center', wrap: true },
badge({ content: 9 }, button({ variant: 'soft', color: 'neutral' }, 'Nine')),
badge({ content: 250 }, button({ variant: 'soft', color: 'neutral' }, 'Capped at 99')),
badge({ content: 250, max: 999 }, button({ variant: 'soft', color: 'neutral' }, 'max: 999')),
)Dot
A dot says "something changed" without saying how much. Give it a label — a bare dot means nothing to a screen reader, so without one it is hidden from the accessibility tree entirely.
stack({ direction: 'row', gap: 'xl', align: 'center' },
badge({ dot: true, color: 'success', label: 'Online' }, avatar({ name: 'Ada Lovelace' })),
badge({ dot: true, color: 'warning', label: 'Needs attention' },
iconButton({
label: 'Settings',
variant: 'soft',
color: 'neutral',
icon: icon('settings'),
}),
),
)Labelling the count
A bare number is ambiguous out of context. label becomes the badge’s accessible name, so it reads as "4 unread messages" rather than "4".
badge({ content: 4, label: '4 unread messages' },
button({ variant: 'soft', color: 'neutral' }, 'Inbox'),
)Changing the count
A count is the number on a page most likely to change while it is open. setBadge() clamps it to max the way the server did, keeps the announced text with it, and drops an emptied badge out of the accessibility tree — which is how a badge disappears.
The announced text is the site’s prose, so pass it whenever the badge has one:
button({ onclick: "import('/su/badge.js').then(m=>m.set('inbox',0))" }, 'Mark all read')Or from your own module, when there is already one running:
import { setBadge } from 'sitelo/ui/client'
setBadge('inbox', 7, { label: '7 unread messages' })Props
| Prop | Type | Default | Description |
|---|---|---|---|
content | string | number | — | What the badge shows. Ignored when dot is set. |
color | 'primary' | 'neutral' | 'success' | 'warning' | 'danger' | 'danger' | Badge colour. |
dot | boolean | false | A small dot instead of a value. |
max | number | 99 | Counts above this render as n+. |
label | string | — | Accessible name for the badge itself. |