Badge
Badge indicates quantitative data associated with a parent element.
Component Type
Indicator
Platform
Web
Component
Sana Canvas
Delivery Channels
Web
Version
16.1.7Experience Surfaces
Page Body Inline
Anatomy

- High Emphasis: For conveying notifications.
- Low Emphasis: For conveying non-pertinent quantitative data.
- High Emphasis Inverse: For use on backgrounds darker than 500 level.
- Low Emphasis Inverse: For use on backgrounds darker than 500 level.
Usage Guidance
When to Use
- Use a Badge to indicate quantitative data associated with a parent element (e.g., icons, applets, Avatars, text, etc.).
When to Consider Something Else
- Consider using a Status Indicator to identify the status of a task.
Behaviors
Expansion
A Low Emphasis Badge should always expand rightwards from its absolute position when featuring a larger count and / or descriptive metadata. When expanding beyond 99, the count cuts off at 99+.

Screen Readers
When a Low Emphasis Badge is used in-line with text, the order in which it is read should be configurable, so it can be read how it makes the most logical sense. High Emphasis Badges do not have focus stops, since they do not support text.
High Emphasis
High Emphasis Badges are intended for usage as an indicator of notification.

After navigating resolving the source of the notification, the Badge should no longer appear. However, High Emphasis Badges may not always disappear after resolving an item if it is representative of multiple pieces of data.
Low Emphasis
Low Emphasis Badges are intended for conveying less pertinent numerical information. By accepting text, these Badges also allow for additional descriptive text to be passed in to compliment the numerical data.

Low Emphasis Badges could be used in the place of using parentheses to convey quantitative data.
After resolving the source of the indicator, the Badge should either augment down or not appear if it is representative of a standalone piece of data.
Examples
The following section provides examples for common use cases. Please be sure to also read the Accessibility section below.
Basic
Use the default CountBadge variant for most situations. The default high emphasis is ideal for
drawing attention to important or primary information.
import {CountBadge} from '@workday/canvas-kit-react/badge';
import {createStyles} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';
const containerStyles = createStyles({
display: 'flex',
gap: system.gap.sm,
padding: system.padding.md,
});
export default () => {
return (
<div className={containerStyles}>
<CountBadge count={427} />
</div>
);
};
Emphasis
Select the low emphasis option for less prominent or secondary information. This is useful when
you want the badge to be visible but not distracting.
high emphasis is ideal for drawing attention to important or primary information.
import {CountBadge} from '@workday/canvas-kit-react/badge';
import {Text} from '@workday/canvas-kit-react/text';
import {createStyles} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';
const containerStyles = createStyles({
display: 'flex',
gap: system.gap.sm,
padding: system.padding.md,
flexDirection: 'column',
});
const textStyles = createStyles({
paddingInlineEnd: system.padding.xs,
});
export default () => {
return (
<div className={containerStyles}>
<div>
<Text as="strong" className={textStyles}>
Low Emphasis:
</Text>
<CountBadge count={427} emphasis="low" />
</div>
<div>
<Text as="strong" className={textStyles}>
High Emphasis:
</Text>
<CountBadge count={427} emphasis="high" />
</div>
</div>
);
};
Inverse
Apply the inverse variant when displaying the badge on dark or accent backgrounds to maintain
proper contrast and readability. This ensures the badge remains legible in visually dense or colored
areas. The same rules apply for low and high emphasis. Only use this combination on backgrounds with
a token of 600 or greater
to ensure sufficient contrast and accessibility.
import {CountBadge} from '@workday/canvas-kit-react/badge';
import {Text} from '@workday/canvas-kit-react/text';
import {createStyles} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';
const containerStyles = createStyles({
display: 'flex',
gap: system.gap.sm,
padding: system.padding.md,
backgroundColor: system.color.accent.info,
flexDirection: 'column',
});
const textStyles = createStyles({
paddingInlineEnd: system.padding.sm,
});
export default () => {
return (
<div className={containerStyles}>
<div>
<Text as="strong" variant="inverse" className={textStyles}>
Low Emphasis:
</Text>
<CountBadge count={427} variant="inverse" emphasis="low" />
</div>
<div>
<Text as="strong" variant="inverse" className={textStyles}>
High Emphasis
</Text>
<CountBadge count={427} variant="inverse" emphasis="high" />
</div>
</div>
);
};
Custom Limit
By default, CountBadge’s limit is set to 1000. Once the count reaches the limit, the badge will
format the number: 1000 becomes 999+. The default limit is largely arbitrary and intended to
prevent unexpected overflow. You should choose a limit based on your specific use case and consider
the user’s experience. For example, someone looking for a new job finds there are 99+ new
opportunities. Or perhaps someone returns from extended leave and is overwhelmed by 999+ unread
messages on their first day back.
import * as React from 'react';
import {CountBadge} from '@workday/canvas-kit-react/badge';
import {TertiaryButton} from '@workday/canvas-kit-react/button';
import {createStyles} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';
const columnStyles = createStyles({
boxSizing: 'border-box',
display: 'flex',
flexDirection: 'column',
gap: system.gap.md,
});
const controls = createStyles({
boxSizing: 'border-box',
borderBlockEnd: `solid 1px ${system.color.border.strong}`,
display: 'flex',
gap: system.gap.xs,
padding: system.padding.xxs,
});
const defaultBackground = createStyles({
boxSizing: 'border-box',
backgroundColor: system.color.surface.alt.default,
padding: system.padding.md,
});
const inverseBackground = createStyles({
boxSizing: 'border-box',
backgroundColor: system.color.brand.accent.primary,
padding: system.padding.md,
});
const initialCount = 1;
export default () => {
const [count, setCount] = React.useState(initialCount);
return (
<div className={columnStyles}>
<div className={controls}>
<TertiaryButton size="small" onClick={() => setCount(count + 1)}>
Increment
</TertiaryButton>
<TertiaryButton size="small" onClick={() => setCount(initialCount)}>
Reset
</TertiaryButton>
</div>
<div className={defaultBackground}>
<CountBadge count={count} limit={10} />
</div>
<div className={inverseBackground}>
<CountBadge count={count} limit={10} variant="inverse" />
</div>
</div>
);
};
Accessibility
Notification Badge
Notifications are a major use case for CountBadge. When the CountBadge value is updated in
real-time, screen readers must be supported with an AriaLiveRegion that will automatically
describe the change in the number of notifications. If the web app only updates CountBadge as part
of another screen update, then this use of AriaLiveRegion is unnecessary and not recommended.
Tooltipis set on theSecondaryButtonautomatically applying thearia-labelto the button.aria-describedbyproperty is conditionally set on theSecondaryButtonwhen greater than zero referencing a uniqueidfor theCountBadgevalue .AriaLiveRegionis used around theCountBadge, enabling screen readers to monitor changes in value.aria-labelstring is conditionally set onAriaLiveRegionwhen greater than zero, describing “New notification”
import * as React from 'react';
import {CountBadge} from '@workday/canvas-kit-react/badge';
import {SecondaryButton, TertiaryButton} from '@workday/canvas-kit-react/button';
import {AriaLiveRegion, useUniqueId} from '@workday/canvas-kit-react/common';
import {Flex} from '@workday/canvas-kit-react/layout';
import {Tooltip} from '@workday/canvas-kit-react/tooltip';
import {createStyles, cssVar} from '@workday/canvas-kit-styling';
import {notificationsIcon} from '@workday/canvas-system-icons-web';
import {system} from '@workday/canvas-tokens-web';
function negate(value: string, fallback?: string) {
return `calc(${cssVar(value, fallback)} * -1)`;
}
const container = createStyles({
boxSizing: 'border-box',
flexDirection: 'column',
gap: system.gap.md,
});
const controls = createStyles({
boxSizing: 'border-box',
gap: system.gap.sm,
padding: system.padding.xxs,
});
const notificationContainerStyles = createStyles({
boxSizing: 'border-box',
position: 'relative',
});
const countBadgeStyles = createStyles({
boxSizing: 'border-box',
position: 'absolute',
top: negate(system.gap.xs),
insetInlineEnd: negate(system.gap.xs),
});
// Testing notes (Aug. 30, 2024):
// Windows 11
// JAWS 2024 + Chrome / Edge: "New notifications" once, then only the count change "2"
// JAWS 2024 + FF: "New notifications" once, then describes nothing
// NVDA + Chrome / Edge: Consistently describes "{X} New notifications"
// NVDA + FF: Consistently describes count value only "{X}"
// macOS v14.6.1
// VoiceOver + Chrome / Safari: Consistently describes "New notifications {X}"
export default () => {
const [count, setCount] = React.useState(4);
const badgeID = useUniqueId();
return (
<Flex cs={container}>
<Flex cs={controls}>
<TertiaryButton size="small" onClick={() => setCount(count + 1)}>
Add Notification
</TertiaryButton>
<TertiaryButton size="small" onClick={() => setCount(0)}>
Clear
</TertiaryButton>
</Flex>
<Flex>
<span className={notificationContainerStyles}>
<Tooltip title="Notifications">
<SecondaryButton
size="medium"
icon={notificationsIcon}
aria-describedby={!!count ? badgeID : undefined}
/>
</Tooltip>
<AriaLiveRegion aria-label={!!count ? 'New notifications' : undefined}>
{!!count && <CountBadge id={badgeID} count={count} limit={100} cs={countBadgeStyles} />}
</AriaLiveRegion>
</span>
</Flex>
</Flex>
);
};
Custom Styles
Count Badge supports custom styling via the cs prop. For more information, check our
“How To Customize Styles” .
Component API
CountBadge
CountBadge provides a quantity-based summary with dynamic values.
Props
Props extend from span. Changing the as prop will change the element interface.
| Name | Type | Description | Default |
|---|---|---|---|
count | number | Sets the count displayed in the badge | 0 |
emphasis | 'high' | 'low' | Sets the emphasis of the badge | 'high' |
limit | number | Sets the maximum count to display before formatting the number.
E.g. Given a count of | 1000 |
variant | 'inverse' | Sets the variant of the Count Badge | |
cs | | The | |
children | React.ReactNode | ||
as | React.ElementType | Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care. | span |
ref | React.Ref<R = span> | Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If |