refactor(ui): extract ErrorState

Merged
CH
5d ago
+352-177

F22 of the design-system migration (stack S1: F19 Badge โ†’ F20 Avatar โ†’ F21 EmptyState โ†’ F22 ErrorState).

What

  • ErrorState in @luke/ui (barrel), extracted from the desktop's ReadFailure: a failure's sentence in the danger text colour and, where onRetry is given, a Retry. Its two placements are an as const set: ERROR_STATE_PLACEMENT.INLINE puts Retry beside the sentence (caption type) and CENTRED puts it under the sentence (body type, 12px gap, like EmptyState's line and action). It takes no className or style.
  • The sentence and Retry are one role="alert", so a screen reader hears the failure where it appears. Retry is the shared quiet Button with a new RetryIcon glyph. @luke/ui has no lucide dependency, so the glyph is drawn from Lucide's rotate-cw with its ISC licence credited.
  • Stylesheet components/error-state/error-state.css (luke-error-state* classes, components layer), 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 ReadFailure caller (18 files, including agent-pull-request-tab.tsx from the restack) now imports ErrorState from the barrel and no compat twin was needed. read-failure.tsx and its .read-failure* rules are deleted. The Factory's margin rule now targets .luke-error-state[data-placement="centred"].
  • FailureNote and RetryButton stay 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 existing ui project):
    • 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.
  • Existing desktop behaviour tests check what a reader sees:
    • read-failure.test.ts checks 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.ts reads the alert's text instead of selecting by class.
  • pnpm check:affected is 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 darkDesktop lightWeb 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


Open workspace in Conductor