Skip to main content

Pill

A small label used to display a count or numeric value, typically attached to another component. Use when you need to show a quantity at a glance. Don't use for status labels or categories — use Badge instead.

OverviewStyleAccessibility

Description

Anatomy

PartRequired?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

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

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

PartTokenValue
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

KeyAction
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 attributeWhen to useExample
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.

Was this page helpful?

Updated 5 October 2026