Description
Displays a numeric count in a compact, rounded container, typically overlaid or attached to another UI element such as a navigation item, button, or avatar.
Navigation menus (unread notifications), inbox-style list headers (pending items), action buttons (queued tasks), and tab labels (filtered result counts) within Open Point and Social Point admin interfaces.
Government users managing stakeholder lists or consultation responses need an at-a-glance count indicator without navigating into a view. Pill provides that affordance without the semantic weight of a status Badge.
Anatomy
| Part | Required? | Notes |
|---|---|---|
| Container | Required | Rounded pill shape using --op-radius-pill. Sized to fit content with a minimum width that ensures single-digit counts remain legible and circular. |
| Count label | Required | Numeric text rendered at --op-text-xs. Always a whole number — never fractional, currency, or non-numeric. |
| Overflow indicator | Optional | When the count exceeds a configured maximum (default 99), renders '99+' to cap display length and prevent layout disruption. |
Variants
Default
Neutral count with no urgency implied
General-purpose count on navigation items, tabs, or list headers where the number is informational only.
Emphasis
Draws attention to a non-zero count that warrants action
Use when the count represents items requiring user action, such as pending approvals or unread messages in an engagement inbox.
Muted
Subdued count that is secondary to surrounding content
Use inside dense layouts where the count is contextual rather than actionable, such as a total record count alongside a search result list.
States
| State | Behaviour |
|---|---|
| Default | Displays the numeric value. Visible at all times when count is greater than zero. |
| Zero / hidden | When count is 0, the pill is not rendered. Do not render an empty pill — remove it from the DOM or set display:none to avoid redundant whitespace. |
| Overflow | When count exceeds the configured maximum (default 99), display the maximum followed by a '+' character (e.g. '99+'). The parent element's accessible label must expose the true meaning (e.g. 'more than 99 unread') for screen reader users. |
| Loading | When the count is being fetched asynchronously, omit the pill entirely or use a skeleton placeholder. Do not show '0' while loading. |
Usage guidelines
When to use
- Showing the number of unread notifications on a navigation item.
- Indicating how many stakeholder submissions are awaiting review on a button or list header.
- Labelling a tab with a count of filtered results (e.g. 'Pending (4)').
- Attaching a response count to an avatar in a consultation participant list.
Do / Don't
Do
Keep the count numeric and whole. Use '99+' when counts exceed your display maximum.
Don't
Display text strings, decimals, or percentages inside a Pill.
Do
Hide the pill when the count reaches zero. An empty or zero pill adds visual noise without meaning.
Don't
Show a '0' pill — it implies something is present when there is nothing.
Do
Pair the pill with its parent element using an accessible label that includes the count (e.g. 'Notifications, 3 unread').
Don't
Rely on visual position alone to convey what the count refers to — screen readers need explicit context.
Do
Use the Emphasis variant sparingly, only when the count represents items that need user action.
Don't
Apply Emphasis styling to every pill — it loses meaning if overused.
Layout & Spacing
Container padding: --op-space-4 (4px) vertical, --op-space-8 (8px) horizontal Minimum width: 20px to ensure single-digit counts remain circular Gap between pill and parent element: --op-space-4 (4px) Typography: --op-text-xs, font-weight 600 (semibold), --op-font-body Border radius: --op-radius-pill (999px)
Tokens
| Part | Token | Value |
|---|---|---|
| Container background (Default) | --op-color-bg-inverse | Dark background for strong contrast against light surfaces. |
| Container background (Emphasis) | --op-color-interactive-default | Green-400; draws attention to actionable counts. |
| Container background (Muted) | --op-color-bg-subtle | Low-contrast background for secondary counts. |
| Count label (Default) | --op-color-text-inverse | White text on dark container. |
| Count label (Emphasis) | --op-color-text-on-interactive | High-contrast text on green-400 background. |
| Count label (Muted) | --op-color-text-secondary | Subdued text for muted variant. |
| Border (all variants) | none | No border. Background contrast alone defines the pill shape. |
Engineering notes
- Pill is a presentational component. It does not emit events or manage internal state — the count is always passed as a prop or attribute from the parent.
- Implement overflow truncation logic in the parent component or via a 'max' attribute on the pill itself. Default max is 99.
- When count is 0, the host element should set aria-hidden='true' and visually hide the pill. Do not remove it from the DOM if the count is reactive — toggle visibility to avoid layout shift.
- When using wa-badge for pill-style display, set the 'pill' attribute to true and control the variant via the 'variant' attribute.
- For screen readers, do not rely on the pill text alone. The parent element (e.g. button or nav link) must carry an aria-label that includes the count in full (e.g. 'Submissions, 12 pending').
- Avoid hardcoding colour values. Always reference --op-color-* semantic tokens to ensure theme and contrast-mode compatibility.
Keyboard interaction
| Key | Action |
|---|---|
| Tab | Pill is non-interactive and not focusable. Focus passes to the parent element (button, link, or nav item) to which the pill is attached. |
| Enter | Not applicable. Pill has no interactive behaviour. |
Why it matters
Government platforms serve users with a wide range of accessibility needs, including those using screen readers or high-contrast modes. A count that is only visible — with no accessible text equivalent on the parent element — means some users never know there are items waiting for their attention. This is especially critical in consultation management workflows where missed submissions can affect compliance obligations.
Focus
Pill is not focusable in isolation. It must be embedded within a focusable parent element. The parent element's accessible name (aria-label or visible text) must include the count value so it is announced by screen readers. When the count updates dynamically, use aria-live="polite" on the parent region or announce the change via an off-screen live region.
ARIA
| Role or attribute | When to use | Example |
|---|---|---|
aria-label on parent element | Provide the full human-readable count context on the parent interactive element. | <button aria-label="Notifications, 5 unread"><wa-badge pill>5</wa-badge> Notifications</button> |
aria-hidden="true" on pill when count is zero | Prevent screen readers announcing an invisible or zero-value pill. | <wa-badge pill aria-hidden="true" style="display:none">0</wa-badge> |
aria-live="polite" on containing region | Announce count changes to screen readers when the value updates without a page reload. | <nav aria-live="polite">...</nav> |
Contrast
Default variant: --op-color-text-inverse on --op-color-bg-inverse must meet 4.5:1 for --op-text-xs text. Emphasis variant: --op-color-text-on-interactive on --op-color-interactive-default (green-400) must meet 4.5:1. Verify this ratio whenever the green-400 token value changes. Muted variant: --op-color-text-secondary on --op-color-bg-subtle must meet 4.5:1. This is the variant most at risk of insufficient contrast — test in both light and dark themes before shipping.
Touch targets
Pill itself is not a touch target. The parent element (button, link) must meet the 44x44px minimum touch target requirement. Do not use a pill as a standalone tappable element.
Things to avoid
- Do not use colour alone to convey urgency — the Emphasis variant's green background must always be paired with parent-element labelling that describes the urgency in text.
- Do not animate count changes with motion that cannot be suppressed — respect prefers-reduced-motion when transitioning between count values.
- Do not render a pill with count text smaller than --op-text-xs — below this size numerals become illegible for users with low vision.