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.
FigmaDescription
Presents structured datasets, aggregated metrics, or tabular information in a consistent, scannable format. Supports numeric, categorical, and status-based data with clear visual hierarchy.
Dashboard summary panels, report views, stakeholder engagement summaries, consultation response breakdowns, and activity feed analytics in both Open Point and Social Point.
Government users need to quickly assess programme status, response volumes, and engagement metrics without navigating into individual records. A dedicated data display pattern ensures consistent density, labelling, and contrast across product surfaces.
Anatomy
| Part | Required? | 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
Summary card
A compact, bordered card surface presenting 3–8 aggregated metrics for a single entity or programme.
Use on dashboard views where a user needs a quick status snapshot — for example, a stakeholder record showing total engagements, last contact date, and assigned team.
Comparison table
A multi-column layout that aligns the same fields across two or more entities side by side.
Use when users need to compare consultation results, stakeholder tiers, or response rates across multiple programmes or time periods.
Metric row
A horizontal strip of headline numbers with supporting labels, typically used at the top of a report or dashboard section.
Use to surface top-level KPIs — for example, total responses received, active consultations, or engagement rate — before a user drills into detail.
Breakdown list
A vertical list of labelled data points with optional progress bars or inline percentages indicating relative proportion.
Use to display categorical breakdowns such as response sentiment distribution, stakeholder group participation, or submission channel split.
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
When to use
- Displaying aggregated engagement metrics on a stakeholder or consultation record — for example, total submissions, average sentiment score, and response rate.
- Presenting report summaries that a government programme manager needs to scan before exporting or presenting to stakeholders.
- Showing a breakdown of consultation responses by category, channel, or demographic group in a dashboard widget.
- Comparing two or more programme outcomes side by side to support evidence-based decision making.
When not to use
- Simple key-value metadata (for example, a record's created date or assigned owner) — use a Description List or form read-only field instead.
- Large datasets with more than 20 rows and sortable or filterable columns — use a Data Table component instead.
- Purely narrative or prose-based content — use a content block or card with rich text.
- Real-time streaming data that updates more than once per second — this pattern is not optimised for rapid re-renders.
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
| Part | Token | Value |
|---|---|---|
| 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-headline 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
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
- (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 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 and
/ to maintain proper header association across all cells. Keyboard interaction
Key Action 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 attribute When to use Example aria-busyApplied to the container element during asynchronous data loading. <div class="data-display" aria-busy="true" aria-label="Loading consultation summary">aria-liveApplied to the container or a specific value cell when data updates in place without a page reload. <span aria-live="polite">842</span>aria-labelApplied 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 elements in the comparison table variant to associate headers with their cells. <th scope="col">March 2026</th>aria-describedbyLinks 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.
- For metric row and summary card variants where data is non-tabular, a