Feedback States
Unified pattern for empty, loading, and error states. State-driven feedback with semantic HTML and accessible output elements.
Overview
Feedback states communicate container status to users. Instead of separate components, this pattern uses a single data-attribute system to toggle between empty, loading, and error states.
Key concepts:
data-state="empty|loading|error"on the container controls visibilityoutput[data-empty|loading|error]elements contain state-specific feedback.contentclass marks the populated content (table, ul, ol, dl, articles)data-feedback="message|skeleton"controls presentation style
The Pattern
Put data-state directly on semantic containers (section, article). Use <output> elements with data attributes for each feedback type.
/* Default: feedback outputs hidden */output[data-empty],output[data-loading],output[data-error] { display: none;} /* When state is set: hide content, show matching feedback */[data-state="empty"] > .content,[data-state="loading"] > .content,[data-state="error"] > .content { display: none;} [data-state="empty"] > [data-empty],[data-state="loading"] > [data-loading],[data-state="error"] > [data-error] { display: flex; flex-direction: column; align-items: center; text-align: center; padding: var(--size-xl);} /* Feedback presentation variants */[data-feedback="skeleton"] { gap: var(--size-s);} [data-feedback="message"] { gap: var(--size-m);}
Data Attributes
| Attribute | Applied To | Purpose |
|---|---|---|
data-state="empty|loading|error" |
Container | Current state of the container |
data-empty |
Output | Feedback shown when state is empty |
data-loading |
Output | Feedback shown when state is loading |
data-error |
Output | Feedback shown when state is error |
data-feedback="message|skeleton" |
Output | Presentation style (text vs visual) |
Basic Structure
A container with a single empty state feedback output.
<section class="messages" data-state="empty"> <h2>Messages</h2> <!-- Populated content --> <ul class="content"> <li>Message 1</li> </ul> <!-- Feedback for empty state --> <output data-empty data-feedback="message" role="status"> <icon-wc name="inbox"></icon-wc> <h3>No messages</h3> <p>Messages will appear here.</p> </output></section>
Multiple States
A single container can have feedback for multiple states. Only the matching feedback is visible based on data-state.
<section class="products" data-state="loading"> <h2>Products</h2> <table class="content">...</table> <!-- Loading feedback (skeleton style) --> <output data-loading data-feedback="skeleton" role="status" aria-busy="true"> <span class="skeleton-line"></span> <span class="skeleton-line"></span> </output> <!-- Empty feedback --> <output data-empty data-feedback="message" role="status"> <icon-wc name="package"></icon-wc> <h3>No products</h3> </output> <!-- Error feedback --> <output data-error data-feedback="message" role="alert"> <icon-wc name="alert-circle"></icon-wc> <h3>Failed to load</h3> </output></section>
Table Example
A table with all three feedback states. Use the buttons to toggle between states.
List Examples
Unordered list (notifications) and ordered list (leaderboard) with feedback states. The notifications use a message-style loading state, while the leaderboard uses skeleton lines.
Description List Example
A product specifications panel using a description list (<dl>) with skeleton loading feedback.
Articles Example
Blog posts as a collection of articles with feedback states for empty, loading, and error conditions.
Feedback Types
Use data-feedback to control the presentation style of feedback outputs.
| Type | Use For | Contents |
|---|---|---|
message |
Empty and error states, short loading messages | Icon, heading, description, optional action button |
skeleton |
Loading states showing content structure | Animated placeholder lines matching expected content |
JavaScript Integration
The pattern works CSS-only with server-rendered data-state. For dynamic content, toggle the attribute based on data fetching status.
Accessibility
<output>element: Semantic element for content that is the result of a user action or calculation. Screen readers announce it naturally.role="status": Live region for empty and loading states. Changes are announced politely.role="alert": Live region for error states. Changes are announced immediately.aria-busy="true": Indicates loading state to assistive technologies.
| State | Role | Additional Attributes |
|---|---|---|
| Empty | role="status" |
None |
| Loading | role="status" |
aria-busy="true" |
| Error | role="alert" |
None |
Icon Options
Choose icons that represent the state and content type:
- Empty:
inbox,file-text,users,folder,bell-off,trophy - Loading:
loader(withdata-animate="spin") - Error:
alert-circle,alert-triangle - Retry action:
refresh-cw
Usage Notes
- No wrapper divs: Apply
data-statedirectly to semantic containers like<section>or<article> - Content class: Always mark your populated content with
.contentclass - Multiple outputs: Include all relevant feedback outputs in the container; CSS shows only the matching one
- Progressive enhancement: Works CSS-only; JavaScript enhances by toggling
data-state - Skeleton matching: For skeleton loading, match the number and width of lines to your expected content
Related
Empty States
Detailed empty state examples
Skeleton
Skeleton animation details
Error Pages
Full-page error states (404, 500)
Output Element
Semantic output element reference