Counter Badge
The CounterBadge component is used to highlight and display numerical values or simple count indicators. It is designed to be non-interactive (read-only) and supports different color themes.
Variations
Outline
The outline variant (the default) provides a subtle border-based appearance, ideal for secondary information display.
Secondary
The secondary variant uses a soft background appearance for more prominent display while maintaining subtlety.
Comparison
Compare both variants side by side to see the visual differences:
Themes
The badge supports four color themes. default and info read as neutral indicators for general counts, while success and danger are status indicators that signal an improvement or a concern.
Installation
import { CounterBadge } from '@harnessio/ui/components'
// ...
// Default counter badge<CounterBadge>1</CounterBadge>
// Info theme counter badge<CounterBadge theme="info">42</CounterBadge>
// Success theme counter badge<CounterBadge theme="success">10</CounterBadge>
// Danger theme counter badge<CounterBadge theme="danger">99+</CounterBadge>Anatomy
CounterBadge is a single, self-contained element — there are no parts to compose:
<CounterBadge theme="info">42</CounterBadge>- It renders one
<div>styled by thevariant(outline or secondary) andtheme(default, info, success, danger) you pass, and shows itschildrenas-is — a number, a short string like99+, or a signed value like-123. - It’s read-only by design: the props that would make it interactive or reassign its semantics —
role,onClick,tabIndex,aria-readonly,color— are removed from its type, so it stays a plain, non-interactive indicator. - Any remaining
HTMLAttributes(includingclassNameandaria-*) pass straight through to that div.
Best practices
Do
- Use a counter badge to surface a count or short value next to the thing it describes — a tab, a list heading, a menu item.
- Keep the content to a compact number or short token (
8,+99,99+), and cap large numbers (e.g.99+) so the badge stays small. - Choose the
themefor meaning:default/infofor a neutral count,successfor a positive change,dangerfor a problem count.
Don’t
- Don’t put a counter badge on something people are meant to click through the badge — it’s non-interactive; make the surrounding element the control.
- Don’t use it for long text or full labels; reach for Tag or Status Badge when you need words.
- Don’t rely on theme color alone to carry meaning — see the accessibility notes.
Content
- Keep it to a number or a short count token; avoid words and punctuation beyond what a count needs (
+,-,%). - Pick a consistent overflow convention (e.g.
99+) and use it everywhere counts can get large.
Accessibility
CounterBadge is a visual, non-interactive indicator: it renders its content as plain text inside a <div> with no semantic role of its own.
Labeling
- The badge’s content is read inline as text, but nothing ties it to what it counts — a bare
3gives no context on its own. Make sure the surrounding label supplies the meaning (“Errors 3”), or add anaria-labelon the badge (or its container) that spells it out.
Focus & states
- The badge isn’t focusable and has no interactive states — it never enters the tab order. When the count belongs to an interactive element, put the behavior and focus on that element, not the badge.
Known limitations
- A changing count isn’t announced. The badge is a plain
<div>, not a live region (role="status"/aria-live), so screen-reader users aren’t notified when the number updates. If announcing the change matters, wrap it in your own live region. - Theme color isn’t meaning.
successanddangerconvey improvement/concern only through color. Keep the value itself explicit (a sign like+/-, or an adjacent label) so the meaning survives without color perception.
Properties
Prop | Required | Default | Type |
|---|---|---|---|
| variant | false | 'outline' | 'outline' | 'secondary' |
| theme | false | 'default' | 'default' | 'info' | 'success' | 'danger' |
| className | false | string | |
| children | true | ReactNode |
CounterBadge also forwards the remaining HTMLAttributes<HTMLDivElement> (e.g. aria-*, id) onto the underlying div — except color, role, aria-readonly, tabIndex, and onClick, which are removed to keep it read-only.