Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
Unified pattern for empty, loading, and error states. State-driven feedback with semantic HTML and accessible output elements.
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.content class marks the populated content (table, ul, ol, dl, articles)data-feedback="message|skeleton" controls presentation stylePut 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);}
| 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) |
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>
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>
A table with all three feedback states. Use the buttons to toggle between states.
Unordered list (notifications) and ordered list (leaderboard) with feedback states. The notifications use a message-style loading state, while the leaderboard uses skeleton lines.
A product specifications panel using a description list (<dl>) with skeleton loading feedback.
Blog posts as a collection of articles with feedback states for empty, loading, and error conditions.
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 |
The pattern works CSS-only with server-rendered data-state. For dynamic content, toggle the attribute based on data fetching status.
<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 |
Choose icons that represent the state and content type:
inbox, file-text, users, folder, bell-off, trophyloader (with data-animate="spin")alert-circle, alert-trianglerefresh-cwdata-state directly to semantic containers like <section> or <article>.content classdata-stateDetailed empty state examples
Skeleton animation details
Full-page error states (404, 500)
Semantic output element reference