ErrorState
The presentation of a region that could not be loaded, what failed, and the retry beside the explanation.
A failure in a region is two things at once: something the reader has to understand, and
something they have to do about it. ErrorState is the one surface in the library whose whole
purpose is to say both, what failed, what it cost them, and, when there is anything left to try,
the retry sitting beside the explanation rather than three screens away.
It is EmptyState with the error tone: the same centred block and the same action group, plus a
retry and a disclosure for the message the request returned. It is drawn as a panel and announced
as an alert by default, because it usually replaces content a moment after a request failed.
Usage
import { ErrorState } from "@asheeui/web";
<ErrorState
title="Invoices could not be loaded"
description="The request timed out. Trying again usually works."
onRetry={refetch}
/>
onRetry is a React handler rather than a configured action, because a configured action describes
a destination and "run that request again" is not one. Its presence is what draws the control:
without onRetry the state offers no retry at all, which is the honest arrangement when nothing
can be retried. The control it draws is a solid, danger-coloured button, which is what makes the
retry the primary action of a failed region rather than one option among several.
description is where the reassurance goes, nothing has been charged, the draft is saved,
because a reader who has just lost sight of their data is asking what it cost them before they ask
how to get it back. retryLabel renames the control for the cases where "Try again" is too vague
about what is being tried again, and both that label and detailLabel take a node rather than a
string, so either control can carry an icon; what a reader hears is the text the node renders.
The technical detail
detail is the message a request returned, and it is written for a developer. It renders folded
into a disclosure labelled by detailLabel and opened only when someone asks, so the reader who
needs it can paste it into a support ticket and the reader who does not is never shown a stack
trace. The disclosure is the platform's own <details>, so it opens without scripting and is
announced with its open state.
When trying again is not the answer
primaryAction and secondaryAction are the same configurations EmptyState takes, and they share
one group with the retry: the configured actions first, the retry after them. That order is
deliberate, the configured pair is the consumer's own statement of where the reader can go and
leads the row, while the retry is the framework's recovery path for the request that failed. When
no configured action is given the retry is the only control, and therefore first. A permission
failure is the case worth designing for, there is no request worth running again, so the state
explains, and offers the two destinations that can actually help.
Props
size, panel, role, retryLabel and detailLabel resolve instance → components.errorstate →
the framework's fallback, registered as md, true, alert, Try again and Technical details.
The default panel and the default alert are one decision seen twice: this state usually replaces a
region that was there a moment ago, so it draws the surface it replaced and says what happened.
An ErrorState is dressed by components.errorstate alone, it composes the empty presentation
but states its own density and its own surface, so restyling the one does not restyle the other.
title, titleAs, description, detail, onRetry, primaryAction, secondaryAction, icon,
as and children are instance-only. There is no type: a failure is the error tone, and a
component that could be told to look like a success is a component that would lie about the data.
Accessibility
- The state is an
alertby default, and that is a statement about timing. A reader mid-task is interrupted because a request they started has just failed: silence would leave them waiting for content that is not coming. role="status"is the polite version, for a failure that is not the result of what the reader just did, a background refresh, a second region that failed while they were reading the first. It waits for a pause instead of cutting in.role="none"renders no live region at all, for a failure that was part of the page from the start, such as a not-found page: there was no change, so there is nothing to announce.- The retry is a real
<button type="button">and the disclosure is a<summary>inside<details>, so both are keyboard-operable and both are reached by tabbing. Focus is never moved to either: the failure reports, and the reader decides when to act on it. - The title renders as a heading, an
<h2>unlesstitleAsstates another level, so a reader can reach the failure by heading navigation, the same way they reach a section.titleAsis how a failure that is the page renders anh1, and how one inside a card'sh3renders anh4.
The two channels, and why a retry never moves focus, are Announcements.