Phase 1: Session log archived (1096→91 lines), D031 token access convention
Phase 2: ESLint v9 + Prettier + jsx-a11y, initial config and lint fixes
Phase 3: 7 new skills (polish, harden, normalize, clarify, typeset, quieter, adapt)
+ Vercel reference docs, updated audit/review-component refs
Phase 4: Husky + lint-staged pre-commit hooks, preflight updated to 8 checks
Phase 5: Vitest + Testing Library + /write-tests skill
- Badge.tsx colour maps unified to CSS variables (D031)
- 5 empty interface→type alias fixes (Switch, Radio, Divider, IconButton, Link)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6.9 KiB
name, description, user-invocable, argument-hint
| name | description | user-invocable | argument-hint |
|---|---|---|---|
| harden | Edge case and robustness review — checks error states, empty states, loading states, boundary values, and disabled interactions. Critical for forms and arrangement flows. | true | [component or area to harden] |
Systematically review and fix edge cases, error states, and boundary conditions. Unlike audit/critique which only assess, harden finds AND fixes issues. This skill is especially critical for forms, stepped flows, and anything involving user input.
Target: $ARGUMENTS
Preparation
- Read
docs/design-system.mdfor FA design conventions - Read
docs/memory/decisions-log.mdfor design rationale (especially D024 on error states) - Read the target component/area source files and stories
- Reference
docs/reference/impeccable/interaction-design.mdfor interactive state requirements - Reference
docs/reference/impeccable/cognitive-load.mdfor cognitive load under stress - Reference
docs/conventions/component-conventions.mdfor structural rules
FA context: Funeral Arranger serves families in grief or distress. When something goes wrong, the interface must be gentle and guiding, never blaming or alarming. Error states use copper tones, not red (D024). Empty states should guide toward action, not leave users stranded. Loading states should reduce perceived wait time — grieving users have less patience for ambiguity.
Hardening Checklist
Work through each category. Fix issues as you find them — do not just document.
1. Error States
For every input, form field, and async operation:
- Error is visible: Error message appears near the source of the problem
- Error is gentle: Uses copper tones (
feedback.error), not aggressive red. Per D024, labels stay neutral - Error is specific: Identifies the exact problem ("Please enter a valid email" not "Invalid input")
- Error is actionable: Tells the user how to fix it
- Error is accessible: Connected via
aria-describedby, announced to screen readers - Error does not destroy work: Form data is preserved when validation fails
- Error timing: Validates on blur for individual fields, on submit for cross-field validation
- Network errors: Graceful message for failed API calls with retry option
- Unexpected errors: Catch-all error boundary that does not show a blank screen
FA-specific: Error copy should never blame the user. Use passive voice for errors ("A valid email is needed" not "You entered an invalid email"). Offer help where possible.
2. Empty States
For every list, collection, search result, and data display:
- Empty state exists: Not just blank space or a bare container
- Empty state guides: Tells the user what this area is for and how to populate it
- Empty state has a CTA: Primary action to add/create/search is visible
- Empty state feels warm: Consistent with FA's supportive tone
- Empty state is distinct: Clearly different from loading state — user should never confuse "no data" with "still loading"
3. Loading States
For every async operation and data fetch:
- Loading indicator exists: User sees feedback that something is happening
- Skeleton over spinner: Use skeleton screens for content areas, spinners only for actions
- No layout shift: Content area maintains its dimensions during loading (prevents CLS)
- Loading is fast-feeling: Skeleton previews the content shape; perceived wait is minimised
- Loading timeout: If loading takes >5s, show a reassuring message ("This is taking longer than usual")
- Button loading: Buttons show inline loading state, remain disabled, and preserve their width
- Optimistic updates: For low-stakes actions, show success immediately and rollback on failure
4. Disabled States
For every interactive element that can be disabled:
- Visually distinct: Reduced opacity (0.38-0.5) or muted treatment — clearly non-interactive
aria-disabled: Set alongside visual treatment for screen reader users- No pointer events:
pointer-events: noneor equivalent — no hover/active states - Tooltip on disabled: Explains WHY the element is disabled (e.g., "Complete the required fields first")
- Cursor: Shows
not-allowedcursor on hover
5. Boundary Values
For every input that accepts user data:
- Max length: Text inputs have sensible
maxLengthand show remaining characters if relevant - Min/max values: Number inputs have
min/maxattributes - Long content: Component handles very long names, descriptions, and values without breaking layout
- Short content: Component handles single-character or minimal content gracefully
- Special characters: Handles ampersands, quotes, HTML entities, and emoji without rendering issues
- Zero state: Numeric displays handle $0.00, 0 items, 0 results
- Large numbers: Handles $999,999+ with proper formatting
6. Overflow & Truncation
For every text container and layout:
- Text overflow: Long text truncates with ellipsis or wraps gracefully — never overflows container
- Responsive overflow: No horizontal scroll at any viewport width
- List overflow: Long lists scroll within a container, not the page
- Image overflow: Images are constrained to their containers with
object-fit
7. Keyboard & Focus
For every interactive element and flow:
- Tab order: Logical, matches visual order
- Focus trap: Modals and drawers trap focus correctly (use
inerton background) - Focus return: When a modal/popover closes, focus returns to the trigger element
- Escape to close: All overlays close on Escape key
- Enter to submit: Forms submit on Enter from the last field
- Arrow navigation: Tab lists, menus, and radio groups use roving tabindex with arrow keys
8. Concurrent & Async
For forms and flows with async operations:
- Double-submit prevention: Submit button disables after first click
- Rapid interaction: Debounced search, throttled scroll handlers
- Stale data: Component handles data that changes between render and interaction
- Unmount safety: Async operations clean up on component unmount (no state updates after unmount)
Hardening Report
After fixing issues, provide a summary:
Fixed
List each issue fixed with a one-line description.
Verified OK
List categories that passed inspection without changes needed.
Out of Scope
Note any structural issues found that require architectural changes (not hardening work).
NEVER:
- Use aggressive red for error states — always copper/warm tones per D024
- Show blank screens for empty or error states
- Leave async operations without loading feedback
- Allow double-submit on forms
- Remove focus indicators
- Make structural changes — hardening fixes edge cases within the existing architecture