Skip to main content

Placeholders

Empty state components displayed when no content is available in a view. Use to guide users toward a relevant action when a list, table, or page has no data. Don't leave empty states blank.

OverviewStyleAccessibility

Description

Anatomy

PartRequired?Notes
Container Required Full-width block centred within its parent region. Sets padding and vertical rhythm for all child elements.
Icon or illustration Recommended A single wa-icon or a decorative illustration that represents the empty category (e.g. a person outline for an empty stakeholder list). Sized at 48px by default.
Heading Required One short sentence naming what is empty. Uses --op-text-lg, --op-font-heading, --op-color-text-primary. Example: 'No stakeholders added yet'.
Supporting text Recommended One or two sentences explaining why the state exists or what to do next. Uses --op-text-sm, --op-font-body, --op-color-text-secondary.
Primary action Optional A single wa-button (variant='primary') pointing to the most likely next step. Only include when a direct action resolves the empty state.
Secondary action Optional A text link or ghost button for a secondary path, such as 'Learn more' or 'Import from CSV'. Appears below the primary action.

Variants

States

State Behaviour
Static Default render — icon, heading, text, and optional action are visible. No interactive states on the container itself.
Loading (transitional) Before the empty state is confirmed, show a wa-spinner or skeleton. Only replace with the placeholder once the data fetch has resolved with an empty result.
Action hover The embedded wa-button follows its own hover state using --op-color-interactive-hover. The placeholder container itself does not change.

Usage guidelines

Do / Don't

Do

Write heading text that names the specific thing that is empty: 'No consultations found' rather than 'Nothing here'.

Don't

Use vague headings like 'No data' or 'Empty' that give the user no context.

Do

Include a primary action only when it directly resolves the empty state (e.g. 'Add stakeholder', 'Create consultation').

Don't

Add a primary action that navigates away to an unrelated part of the product — this increases cognitive load.

Do

For no-results states, show what filters or search terms are active and offer a 'Clear filters' action.

Don't

Show the same generic placeholder for both a genuinely empty list and a filtered-empty list — users need different guidance.

Do

Keep supporting text to one or two sentences. Write in plain language accessible to non-technical government staff.

Don't

Include technical error codes or jargon in the visible placeholder text.

Layout & Spacing

Container: padding --op-space-48 (48px) top and bottom, --op-space-24 (24px) left and right. Container max-width: 480px, centred with margin: 0 auto. Icon / illustration: margin-bottom --op-space-16 (16px). Heading: margin-bottom --op-space-8 (8px). Supporting text: margin-bottom --op-space-24 (24px) when an action follows; --op-space-2 when it is the last element. Action stack: gap --op-space-12 (12px) between primary and secondary actions, centred.

Tokens

PartTokenValue
Container background --op-color-bg-primary Transparent or surface-level background — matches the parent panel or page.
Container background (error variant) --op-color-status-error-bg Subtle tinted background to signal a system error state.
Icon --op-color-text-secondary Muted icon colour so it does not compete with the heading.
Heading text --op-color-text-primary Full contrast heading.
Supporting text --op-color-text-secondary Secondary text colour for the descriptive copy.
Heading font --op-font-heading ABC Otto. Size: --op-text-lg.
Body font --op-font-body ABC Social. Size: --op-text-sm.

Engineering notes

  • The placeholder is a layout pattern, not a single web component. Compose it from a div container, wa-icon, heading element (h2 or h3 depending on page hierarchy), p, and wa-button.
  • Always wait for the data fetch to resolve before rendering the placeholder. Avoid a flash of the empty state while data is loading — show wa-spinner first.
  • Use the correct heading level (h2 or h3) based on the surrounding document outline. Do not use h1 inside a placeholder within a page that already has an h1.
  • For the no-results variant, surface the active search term or filter names in the supporting text so users know exactly what returned no results.
  • When the error variant is used, log the underlying error to your monitoring tool but do not expose stack traces or error codes in the visible UI.
  • If the placeholder appears inside a wa-dialog or panel, reduce padding to --op-space-32 (32px) top and bottom to suit the smaller container.

Keyboard interaction

KeyAction
Tab Moves focus to the primary action button, then the secondary action (if present). The placeholder container itself is not focusable.
Enter Activates the focused action button.
Space Activates the focused action button.

Why it matters

Government users include people with low digital literacy, cognitive disabilities, and assistive technology users. An empty state that is not announced to screen readers leaves these users uncertain whether the page has loaded correctly, which can cause repeated refresh attempts and loss of trust in the system.

Focus

The placeholder container has no focus state of its own. Focus passes through to interactive children (wa-button) in DOM order. When a placeholder replaces a loading spinner, move focus to the placeholder heading using programmatic focus (tabindex="-1" on the heading element, then heading.focus()) so screen reader users are informed of the state change without a full page reload. Focus ring on buttons: 3px solid --op-color-interactive-focus, offset 2px.

ARIA

Role or attributeWhen to useExample
aria-live="polite" Apply to the placeholder container region when it is injected into the DOM after a data fetch. This announces the empty state to screen readers without interrupting the user. <div class="placeholder" aria-live="polite">
role="status" Can be used instead of or alongside aria-live='polite' on the container to convey a status message to assistive technology. <div class="placeholder" role="status">
aria-hidden="true" on decorative icon Mark the wa-icon as aria-hidden='true' when it is purely decorative and the heading already describes the empty state. <wa-icon name="person-outline" aria-hidden="true"></wa-icon>
tabindex="-1" on heading Add when programmatically shifting focus to the placeholder heading after a state transition, to avoid adding it to the natural tab order. <h2 tabindex="-1" class="placeholder__heading">No stakeholders added yet</h2>

Contrast

Heading (--op-color-text-primary on --op-color-bg-primary): meets WCAG AA at all text sizes. Supporting text (--op-color-text-secondary on --op-color-bg-primary): verify at --op-text-sm; must meet 4.5:1. Icon (--op-color-text-secondary on --op-color-bg-primary): decorative — no contrast requirement if aria-hidden="true". Error variant background (--op-color-status-error-bg): ensure heading and body text tokens still meet 4.5:1 against this tinted background.

Touch targets

The placeholder container itself is not interactive. All embedded wa-button elements must maintain a minimum touch target of 44x44px. Do not reduce button size to fit a narrow placeholder container — use the full-width button option instead.

Things to avoid

  • Do not use colour alone to distinguish the error variant from the default variant — also change the icon and heading text.
  • Do not auto-dismiss or replace the placeholder with a spinner loop — this disorients screen reader users.
  • Do not place placeholder text in a visually hidden element while showing only an illustration — the text must be visible, not just announced.

Was this page helpful?

Updated 5 October 2026