Files
AOS/skills/design/horizontal-craft/state-coverage.md
T
Z User e45b68bb8b feat: IMAP mail sync with thread-to-contact matching (PRD §6.1)
Real IMAP connection via imapflow library:
- Connects to IMAP server (IMAPFlow, TLS on port 993)
- Fetches recent messages (configurable limit, default 20)
- Extracts: from, fromEmail, subject, date, snippet (200 chars)
- Thread-to-contact matching (PRD §6.1):
  1. Match sender email against persons table
  2. If no person match, extract domain and match against company.website
  3. Returns matchedCompany + matchedPerson for each email
- Audit-logged (action: crm.mailsync:imap)
- Zod validated (host, port, user, password, folder, limit)
- 502 on IMAP connection failure with detail
- 400 on validation failure

Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
2026-10-27 22:50:50 +00:00

4.8 KiB
Executable File
Raw Blame History

name, description, mode, platform, scenario, preview, default_for, fidelity, sources
name description mode platform scenario preview default_for fidelity sources
state-coverage Required UI states for interactive, data-driven, form, tool, dashboard, and prototype surfaces. Prevents designs that only render the ideal populated state. reference web horizontal-craft
type
none
state coverage
empty state
loading state
error state
forms
dashboard
interactive prototype
system
adapted-from-open-design/craft/state-coverage.md

State Coverage

The single most reliable AI-design failure is shipping only the populated state.

The active design system decides how states look. This file decides which states must exist and what they must contain.


The five required states

Every surface that fetches, transforms, filters, searches, or accepts data must handle all five.

State Triggered when Must contain
Loading Data is in flight skeleton, spinner, shell, or labelled progress
Empty no records yet or query returned nothing headline, explanation, primary next action
Error fetch failed, server failure, validation rejection plain cause, recovery action, preserved input
Populated data present the main designed state
Edge extreme volume, long strings, missing fields, partial network layout does not break

Render-and-screenshot test: every list, table, card, form, and panel has these states.


Edge matrix

Surface Edge scenario
Dashboard / table 10,000+ rows, numeric columns, sort + filter active
Mobile card / list 200-char title, missing avatar, missing secondary CTA
Form optional fields empty, required fields at max length
Search results one-character query, special-character query, 1,000+ results
Detail view missing optional metadata, mixed Chinese / Latin / long words
Tool H5 invalid input, user retries, result cannot be computed

Form-specific states

Forms add:

State Triggered when Behavior
Untouched field has not had focus default styling; no validation message
Dirty valid user typed and field passes validation helper remains; avoid excessive success coloring
Submitted pending submit clicked, waiting button loading state; prevent duplicate submit

Validation timing:

  • validate on blur by default;
  • for password / live fields, validate on keystroke only after first blur;
  • remove error when input becomes valid;
  • preserve input on server failure.

Empty state composition

Empty is not absence. It has a job.

  • First-use empty: illustration or simple mark + headline + value sentence + primary CTA.
  • No-results empty: echo the query, suggest alternatives.
  • Cleared empty: confirm completion, optional next action.
  • Error-as-empty: forbidden. Error is its own state.

Error state composition

Every error answers:

  1. What happened.
  2. Why, if knowable.
  3. What the user can do.

Bad:

Something went wrong.

Better:

未能保存表单。网络连接中断,请检查后重试。

Severity tiers:

  • field-level
  • form-level
  • section-level
  • page-level
  • app-level

Match severity to scope. A field error does not need a page-level error.


Loading thresholds

Duration Indicator
0–300ms none
300ms–2s subtle spinner or skeleton
2–10s skeleton or labelled spinner
10–30s progress with cancel option when possible
30s+ explicit longer-than-expected message
60s+ stop indefinite animation; show error / retry / cancel

Never leave a spinner running indefinitely.


ARIA and focus rules

Change ARIA Focus action
Inline error on submit role="alert" move focus to first error field
Toast confirmation role="status" do not move focus
Critical modal role="alertdialog" move focus to dialog
Loading begins role="status" do not focus spinner
User-triggered content appears usually none move focus only if it helps continuation

Common mistakes

  • only populated state exists
  • empty state says only “No data”
  • error says only “Something went wrong”
  • spinner has no timeout
  • submit clears fields on failure
  • validation fires on first keystroke
  • full-page loading replaces chrome for a section-level fetch
  • color alone conveys error state
  • toast cannot be paused on hover/focus

Quality checks

[ ] Loading, empty, error, populated, and edge states exist where relevant.
[ ] Empty state has explanation and next action.
[ ] Error state has cause and recovery.
[ ] Forms preserve input after failure.
[ ] Long strings and missing optional fields do not break layout.
[ ] Loading does not spin forever.
[ ] State changes are announced where appropriate.