A message component shown where content would be — never a loading state.
Use qds-empty-state for exactly these five situations:
variant="empty") — “No accounts yet.”variant="no-results") — “No accounts match your filter.”variant="error") — “We couldn’t load your accounts.”variant="no-access") — “You don’t have access to this portfolio.”variant="done") — “All tasks complete.”Do not use it for a loading state. That is a skeleton or a spinner. Showing “No data” while a request is in flight is a lie the user will act on.
Distinguishing the five cases matters more than the visual design. “No accounts exist” and “No accounts match your filter” need different copy and different actions, and a table that shows the first when it means the second sends the user looking for a bug.
The qds-empty-state component is a custom element that renders a centred
icon, title, and description with optional action buttons. The variant
attribute sets only the default icon and icon colour — it never supplies
default copy, because default copy would be wrong in every specific case and
authors would ship it anyway.
No border, no background, no card. It sits inside something that already has those, and doubling them makes the page look broken.
<qds-empty-state
variant="empty"
title-text="No accounts yet"
description="Accounts appear here once your first portfolio is linked.">
<qds-button slot="action">Link account</qds-button>
</qds-empty-state>
variant — One of empty, no-results, error, no-access, done. Default empty. Sets the default icon and icon colour.icon — A named icon (e.g. prime:pi-inbox or lucide:inbox). Legacy pi-* values remain supported. Overrides the variant default icon.title-text — The title text. Required in practice; warns via console.debug when empty. (The title attribute is a reserved global attribute — do not use it.)description — The description text below the title.size — One of sm, md, lg. Default md. sm for inside a select listbox, lg for a full page.compact — Boolean. When present, renders icon and title on one line. For table rows.variant |
Icon | Icon colour |
|---|---|---|
empty |
pi-inbox |
--theme-text-tertiary |
no-results |
pi-search |
--theme-text-tertiary |
error |
pi-exclamation-triangle |
--theme-error |
no-access |
pi-lock |
--theme-text-tertiary |
done |
pi-check-circle |
--theme-success |
container — The outer container.icon — The icon wrapper.title — The title element.description — The description element.actions — The actions slot wrapper.| Property | Default |
|---|---|
--qds-empty-state-padding |
var(--theme-space-6, 24px) |
--qds-empty-state-icon-size |
32px |
--qds-empty-state-max-width |
40ch |
--qds-empty-state-gap |
var(--theme-space-3, 12px) |
This component dispatches no custom events. It is display-only.
variant="no-results" sets role="status" with aria-live="polite" — the state appears in response to a user action and should be announced.role="note" with no live region — an empty state present on load should not interrupt.variant="error" does not use role="alert". An empty state is not urgent; a genuinely urgent failure belongs in qds-toast.aria-hidden="true" — it is decoration, and the title carries the meaning.<!-- No filter results -->
<qds-empty-state
variant="no-results"
title-text="No matching accounts"
description="Try adjusting your filters or clearing the search.">
<qds-button slot="action">Clear filters</qds-button>
</qds-empty-state>
<!-- Load failure -->
<qds-empty-state
variant="error"
title-text="We couldn't load your accounts"
description="Check your connection and try again.">
<qds-button slot="action">Retry</qds-button>
</qds-empty-state>
<!-- Compact, inside a table row -->
<qds-empty-state
compact
variant="empty"
title-text="No rows"
size="sm">
</qds-empty-state>