What Badge does for you, and what it leaves to the page around it. Badge is presentational, so almost everything here is the second kind.
What a Badge renders
Badge is presentational: no role, no state, no keyboard behavior and no live region. It renders a root <span> around what you wrapped, plus one element for the mark.
A count renders as a <sup> of digits. They are read, but as loose text next to what they belong to, not as part of it.
A dot and a status dot are empty colored spans with no text and no role, so a status badge without text announces nothing.
title sets a native tooltip on the count, the bare number by default. It only shows on mouse hover and is read inconsistently, so never rely on it for the information.
Other props land on the root span, so an aria-label on the Badge sits on an element with no role and is not reliably read. Name the control instead, as the naming section below shows.
Never the only way to learn the count
A badge is for a glance, and some readers do not glance. The count has to be available another way.
The count sits beside what it decorates, not inside it. The markup is <span><button/><sup>3</sup></span>, so a wrapped bell button is still announced as “Notifications, button”, and the 3 is separate text.
Show the number at the destination too, like “3 unread” at the top of the inbox.
A dot has no text, so pair a “something is new” dot with a visible word, or with a control name that changes with it.
Do not put a live region on a changing count. A counter that speaks on every tick is noise.
Name the thing, not the badge
The badge has no name. The control it decorates does, so put the count there.
Put the count in the name of the control it sits on: aria-label="Notifications, 3 unread" on the button, not on the Badge. That is one announcement instead of two pieces.
When the count is in the name, hide the visible one with aria-hidden, or accept hearing it twice. Twice is better than once in a place nothing connects to the bell.
A standalone count inside a nav link needs nothing extra: “Inbox 3” is already one name.
Keep the name in step with the cap. 99+ on screen and “100 unread” in the name disagree.
A color is not a signal
status and color paint an empty span. The color is the whole message, and a reader who cannot tell the hues apart, or who is listening, never gets it.
Always pair a status dot with text. It is the only part of a status badge with content, so without it nothing is announced.
Keep the word specific. “Degraded” and “Operational” do the work; “Status” repeats the column header and says nothing.
emphasis="strong" helps a sighted reader scan a column. It changes nothing for a screen reader.
Two statuses that differ only by color read the same. success and processing in one table need their own words.
Contrast on a small fill
The count is small text on a saturated fill, and both colors come from your theme, so check them.
A custom color changes the fill only. The digits stay light, so a pale hex gives you white on near-white. Check a custom color against white text.
A preset or palette name is safer, since it changes with the mode.
With emphasis="strong", the label is colored text on the page. A preset label is a shade darker than its dot so it meets WCAG AA.
The status colors are not darkened, so they match the rest of the page, and success and warning miss AA as text. Keep the dot beside the word, or darken colorSuccess and colorWarning in your theme.
The kit rings every count in the container's background color so it stands out from what it overlaps. If you remove the ring through classNames, check the contrast yourself.
Motion on the count
A badge zooms as it appears and disappears, and its digits roll as the number changes. Neither can be turned off per badge, and neither checks for reduced motion on its own.
theme={{ token: { motion: false } }} turns both off. Set it once at the provider from the reader's prefers-reduced-motion.
The rolling digits are real text while they roll. A badge read mid-roll can expose the numbers in between, not just the one it settles on.
A count on a timer animates on every tick. Update it less often instead.
The animation only shows a change, it does not announce one. If a count going from 0 to 1 is the whole event, say so somewhere a screen reader can hear it.