refactor(ui): extract ErrorState
Merged
CH
5d ago+352+352+012345678901234567890123456789-177-177-012345678901234567890123456789
F22 of the design-system migration (stack S1: F19 Badge โ F20 Avatar โ F21 EmptyState โ F22 ErrorState).
What
ErrorStatein@luke/ui(barrel), extracted from the desktop'sReadFailure: a failure's sentence in the danger text colour and, whereonRetryis given, a Retry. Its two placements are anas constset:ERROR_STATE_PLACEMENT.INLINEputs Retry beside the sentence (caption type) andCENTREDputs it under the sentence (body type, 12px gap, like EmptyState's line and action). It takes noclassNameorstyle.- The sentence and Retry are one
role="alert", so a screen reader hears the failure where it appears. Retry is the shared quietButtonwith a newRetryIconglyph.@luke/uihas no lucide dependency, so the glyph is drawn from Lucide'srotate-cwwith its ISC licence credited. - Stylesheet
components/error-state/error-state.css(luke-error-state*classes,componentslayer), barrel export, and a catalog example "Error states" with four cases: inline, centred, no Retry, and a narrow pane with long words. - Callers: no caller passed classes, so every desktop
ReadFailurecaller (18 files, includingagent-pull-request-tab.tsxfrom the restack) now importsErrorStatefrom the barrel and no compat twin was needed.read-failure.tsxand its.read-failure*rules are deleted. The Factory's margin rule now targets.luke-error-state[data-placement="centred"]. FailureNoteandRetryButtonstay in the desktop app: they carry desktop-specific remedy buttons and tooltip use, which is outside this row.
Tests
Following the new test rule, the PR adds no browser, axe, layout or class/style assertions.
- New
error-state.test.ts(jsdom, the package's existinguiproject):- Inline and centred each render one alert with the sentence and a Retry button, and pressing Retry calls
onRetry. - Retry is an enabled button that can take keyboard focus.
- With no
onRetry, the sentence stands alone and there's no button.
- Inline and centred each render one alert with the sentence and a Retry button, and pressing Retry calls
- Existing desktop behaviour tests check what a reader sees:
read-failure.test.tschecks one alert, the sentence and one working Retry on the sidebar, Inbox, side panel and Settings. Its "fault red" check, which read a class, is removed.workspace-page.test.tsreads the alert's text instead of selecting by class.
pnpm check:affectedis green.
Visible changes
The design is unchanged; these come from moving onto the shared control contract:
- Retry is now the shared quiet button: 13px weight 500 text (was 12px, 600), a 15px glyph (was 13px), and full-strength text ink (was the muted secondary ink). Height and the hover fill are unchanged.
- A centred failure is now set at the body size, 13/20 on the desktop, rather than inheriting its region's size, which was 13px almost everywhere. The gap above Retry is now 12px (was 4px).
- Inline placement is unchanged: Retry is still pulled back by its own inset, so the glyph stands 8px after the words.
| Desktop dark | Desktop light | Web light |
|---|---|---|
![]() | ![]() | ![]() |
Not verified on a Mac. The screenshots are from the catalog in Linux headless Chrome; the Electron app was not run.
๐ค Generated with Claude Code


