Skip to main content

Data Display

Renders structured or aggregated data in a formatted, readable layout for dashboards or reports. Use when data needs to be presented clearly for scanning or analysis. Don't use for simple key-value pairs.

OverviewStyleAccessibility

Description

Anatomy

PartRequired?Notes
Container Required Wraps the entire data display. Sets background (--op-color-bg-primary), border (--op-color-border-default), and radius (--op-radius-md). Provides padding via --op-space-16 or --op-space-24 depending on density variant.
Header row Recommended Contains the dataset title and an optional context label or timestamp. Uses --op-text-sm, --op-color-text-secondary for the label; --op-text-base, --op-color-text-primary for the title.
Data row Required A single labelled data point. Consists of a label cell and a value cell arranged horizontally. Multiple rows stack vertically with --op-space-8 gap.
Label cell Required Describes the data field. Uses --op-text-sm, --op-color-text-secondary, font-weight 500. Truncates with ellipsis at max-width to preserve layout.
Value cell Required Displays the data value. Uses --op-text-sm or --op-text-base depending on emphasis, --op-color-text-primary. May contain inline Badge or Tag for status values.
Divider Optional Separates logical groups of rows. Uses wa-divider with --op-color-border-default. Adds --op-space-12 vertical margin above and below.
Footer row Optional Displays supplementary metadata such as last updated time or data source. Uses --op-text-xs, --op-color-text-tertiary, right-aligned or left-aligned depending on context.
Status indicator Optional An inline Badge or coloured dot used within a value cell to communicate status. Follows the --op-color-status-* token set.

Variants

States

State Behaviour
Default All data rows are visible. Labels and values render at full opacity with standard contrast.
Loading Skeleton placeholder rows replace label and value cells. Use wa-spinner or CSS shimmer animation. Announce loading state to screen readers via aria-busy='true' on the container.
Empty Container renders with a centred empty-state message: short heading (--op-text-base, --op-color-text-primary) and a supporting sentence (--op-text-sm, --op-color-text-secondary). Do not show zero-filled rows.
Error Container displays an inline error message using --op-color-status-error and --op-color-status-error-bg. Provide a retry action where possible. Do not expose raw API error text to end users.
Stale Data is present but may be outdated. A footer note or badge using --op-color-status-warning indicates the last refresh time. Used when live data polling is unavailable.
Highlighted row A single row is visually emphasised — for example, the highest-value metric or a row with a status requiring action. Background uses --op-color-status-info-bg; border-left 3px solid --op-color-status-info.

Usage guidelines

Do / Don't

Do

Use concise, plain-language labels that a non-technical government stakeholder can understand without explanation.

Don't

Use internal system field names or database column identifiers as labels (for example, 'resp_cnt' instead of 'Total responses').

Do

Group related rows with a divider and a group heading so users can navigate logically through the data.

Don't

Present a flat, undifferentiated list of 15+ rows without visual grouping or hierarchy.

Do

Pair numeric values with units or context labels — for example, '842 responses' rather than just '842'.

Don't

Display bare numbers without units, scale, or reference point.

Do

Use status tokens (--op-color-status-*) consistently for values that carry a status meaning — always paired with a text label, never colour alone.

Don't

Use arbitrary brand colours to encode status, or rely on colour as the sole differentiator between states.

Do

Show an empty state with a helpful message when no data is available yet.

Don't

Render a data display with all zero values or dashes when the dataset is empty — this is misleading and creates unnecessary noise.

Layout & Spacing

Container padding: --op-space-16 (default), --op-space-24 (expanded/report variant) Row gap (between data rows): --op-space-8 Label-to-value gap (horizontal): --op-space-16 Divider vertical margin: --op-space-12 above and below Group heading margin-bottom: --op-space-8 Footer row margin-top: --op-space-16 Metric row gap (between metric cells): --op-space-24 Min-width for label cell: 120px; max-width: 240px

Tokens

PartTokenValue
Container background --op-color-bg-primary Default surface. Use --op-color-bg-secondary for nested or inset display contexts.
Container border --op-color-border-default 1px solid border. Omit border on metric row variant when it sits within an existing card.
Container border radius --op-radius-md 8px. Use --op-radius-sm for compact/inline variants.
Label text --op-color-text-secondary --op-text-sm, font-weight 500. Ensures visual separation from value without requiring bold.
Value text --op-color-text-primary --op-text-sm default; --op-text-base for headline metric values.
Header title --op-color-text-primary --op-text-base, font-weight 600, --op-font-heading preferred for card headers.
Header subtitle / timestamp --op-color-text-secondary --op-text-xs. Right-aligned in summary card header.
Footer text --op-color-text-tertiary --op-text-xs. Used for data source attribution or last-updated timestamp.
Highlighted row background --op-color-status-info-bg Applied to tr or row div background. Pair with left border using --op-color-status-info.
Divider --op-color-border-default Passed to wa-divider via CSS custom property or directly styled.
Error message text --op-color-status-error Error state inline message. Background: --op-color-status-error-bg.
Stale indicator --op-color-status-warning Inline badge or icon used in footer to signal data staleness.

Engineering notes

  • The data display pattern does not ship as a single custom element. Compose it from semantic HTML (dl, table, or div grid) and Web Awesome primitives (wa-badge, wa-tag, wa-progress-bar, wa-divider).
  • For tabular data with a defined row/column structure, prefer a <table> element over a CSS grid of divs — this preserves screen reader row/column association.
  • For metric row and summary card variants where data is non-tabular, a <dl> (description list) with dt/dd pairs is the correct semantic element. Apply display:grid or flex layout via CSS.
  • When data is loaded asynchronously, set aria-busy='true' on the container during loading and remove it once data is rendered. Use aria-live='polite' on the container if data updates without a full page reload.
  • Avoid inline styles for token values. Define a .data-display utility class (or BEM variant classes) in your stylesheet and reference tokens from there.
  • Numeric values that may change (for example, live response counts) should be wrapped in a <span aria-live='polite'> so screen readers announce updates.
  • When rendering status values inside value cells, always include a visible text label alongside any wa-badge or colour indicator — never rely on colour alone.
  • For the comparison table variant, use <thead> and <th scope='col'> / <th scope='row'> to maintain proper header association across all cells.

Keyboard interaction

KeyAction
Tab Moves focus to any interactive elements within the data display — for example, retry buttons in the error state, or links within value cells. The container itself is not focusable unless it contains interactive content.
Enter Activates a focused interactive element such as a retry button or an inline link within a value cell.
Arrow keys Not applicable to the default static display. If the comparison table variant includes sortable columns, arrow keys navigate between column headers in accordance with the ARIA grid pattern.

Why it matters

Government dashboards are frequently accessed by public servants using assistive technology, including screen readers and keyboard-only navigation. Data that is visually clear but semantically flat — for example, a grid of divs with no labels — is inaccessible and may prevent staff from completing their work. Correct use of dl, table, and ARIA live regions ensures parity between visual and non-visual access.

Focus

The data display container does not receive focus in its default read-only state. Interactive elements within the display (retry button, inline links, sortable column headers) receive a visible focus ring: 3px solid --op-color-interactive-focus with 2px offset. In the loading state, focus should not be trapped inside the container. If a loading spinner is present, it should be role="status" with an aria-label describing what is loading.

ARIA

Role or attributeWhen to useExample
aria-busy Applied to the container element during asynchronous data loading. <div class="data-display" aria-busy="true" aria-label="Loading consultation summary">
aria-live Applied to the container or a specific value cell when data updates in place without a page reload. <span aria-live="polite">842</span>
aria-label Applied to the container when the header is not a visible heading element, to give the region a descriptive name. <dl class="data-display" aria-label="Stakeholder engagement summary for Riverview Council">
scope (th attribute) Applied to all <th> elements in the comparison table variant to associate headers with their cells. <th scope="col">March 2026</th>
aria-describedby Links a value cell to a footnote or supplementary explanation in the footer row. <dd aria-describedby="footnote-1">73%</dd> <p id="footnote-1">Based on verified submissions only.</p>

Contrast

Label text (--op-color-text-secondary on --op-color-bg-primary): must meet 4.5:1 for --op-text-sm. Value text (--op-color-text-primary on --op-color-bg-primary): must meet 4.5:1. Highlighted row (--op-color-text-primary on --op-color-status-info-bg): verify contrast at the specific token values in your theme — do not assume the bg variant passes automatically. Status badge text within value cells: follows Badge component contrast requirements — refer to the Badge component documentation. Footer text (--op-color-text-tertiary on --op-color-bg-primary): --op-text-xs is below 18px, so 4.5:1 is required. Verify tertiary text token meets this ratio; if it does not, use --op-color-text-secondary instead.

Touch targets

The data display is primarily a read-only component. If interactive elements such as retry buttons or inline links appear within it, they must meet the 44x44px minimum touch target. Inline text links within value cells should be supplemented with padding or a wrapping touch target element if the link text is short.

Things to avoid

  • Using colour alone to convey status within a value cell — always pair a status colour with a visible text label or icon with alt text.
  • Truncating label text without providing the full label via title or aria-label — users who zoom to 200% or use screen readers need access to the complete label.
  • Placing the data display inside a scrollable container that traps keyboard focus, preventing users from navigating past it.
  • Rendering zero-filled or dash-filled rows in the empty state — this provides no actionable information and may confuse screen reader users who hear 'Responses — dash' repeated multiple times.

Was this page helpful?

Updated 5 October 2026