Introduce FormSection, FieldErrorSummary, and InlineEdit components to address form layout and editing patterns missing from the design system. FormSection provides semantic grouping with optional collapsibility. FieldErrorSummary aggregates validation errors with scroll-to-first-error behavior. InlineEdit enables click-to-edit patterns for read-heavy UIs. All integrate with React Hook Form and Zod.
Introduction
Overview
The design system provides field-group.tsx (FieldGroup) for grouping label + input + message, but lacks higher-level form layout primitives. Teams building multi-section forms (settings pages, profile editors, onboarding wizards) duplicate section heading/description/divider patterns. Error summaries that scroll to the first invalid field are hand-built per project. Inline edit patterns (click a value to edit it in place) have no standard implementation.
Goals
Provide FormSection for visually and semantically grouping related form fields with heading, description, and optional collapse.
Provide FieldErrorSummary that renders a list of validation errors with click-to-scroll-to-field behavior.
Provide InlineEdit for toggling between a read-only display and an editable input, with save/cancel actions.
Demonstrate RHF + Zod integration patterns for all three in Storybook docs.
Non-Goals
Full form wizard (multi-step with routing) — PanelWizard exists for that.
Form state management library — RHF is the recommended solution.
Server-side form actions or progressive enhancement.
Scope
In Scope
Item
Description
FormSection
Semantic <fieldset>-based section with heading, description, optional collapse, divider control.
FieldErrorSummary
Error list component that accepts RHF errors object; each error is a clickable link scrolling to the field.
InlineEdit
Read-view / edit-view toggle with save/cancel buttons; supports TextField, Select, and TextArea as edit controls.
RHF + Zod examples
Storybook recipes showing form validation with these components.
Stories and tests
Full Storybook coverage; unit tests for scroll behavior, collapse, and edit toggling.
Recreate section heading + description + divider layout for every settings group.
Form-heavy application teams
No standard error summary; users must scroll to find invalid fields.
Admin dashboard developers
Inline editing of table cells or profile fields requires custom toggle logic per field.
Accessibility-focused teams
Error summaries without programmatic focus management fail screen reader users.
Definitions
Term
Definition
FormSection
A visually distinct group of related form fields, rendered as a <fieldset> with a <legend>.
FieldErrorSummary
A banner-like component listing all current form errors with links to the corresponding fields.
InlineEdit
A pattern where a value is displayed as read-only text; clicking it transitions to an editable input.
Scroll-to-error
Programmatically scrolling the viewport and focusing the first invalid field in a form.
Current State
FieldGroup (src/components/ui/field-group.tsx): Groups a label, input, hint, and error message for a single field. Does not handle multi-field sections.
FieldMessage: Renders error/success/hint messages with icons. Used within FieldGroup.
No FormSection, FieldErrorSummary, or InlineEdit exists.
RHF is used in consumer applications but the design system has no integration layer or error aggregation utilities.
Proposed Solution
FormSection
src/components/ui/form-section.tsx — Renders a <fieldset> with a styled <legend> (heading), optional description paragraph, and optional collapsible behavior (using the existing Collapsible primitive or a simple details/summary pattern). A divider prop controls whether a top border separates it from the previous section.
FieldErrorSummary
src/components/ui/field-error-summary.tsx — Accepts an errors object (compatible with RHF formState.errors) and an optional fieldLabels map (Record<string, string>) for human-readable field names. Renders an alert-styled card with a list of errors. Each error is a button that calls document.getElementById(fieldId)?.scrollIntoView({ behavior: "smooth" }) and then focuses the element. The component uses role="alert" and aria-live="assertive" for screen reader announcement.
InlineEdit
src/components/ui/inline-edit.tsx — A compound component with two states: read mode (renders the value as styled text with a pencil icon) and edit mode (renders the appropriate form control with save/cancel buttons). Props include value, onSave, onCancel, editControl (a render prop receiving { value, onChange }), and readView (optional custom read display). Pressing Escape cancels; Enter in a single-line input saves.
Requirements
Requirement Priorities
Must Have: FormSection, FieldErrorSummary with scroll-to-error, InlineEdit with save/cancel.
Should Have: FormSection collapsible mode, FieldErrorSummary field label mapping, InlineEdit keyboard shortcuts.
FormSection uses <fieldset> and <legend> for semantic grouping; collapse toggle has aria-expanded.
A11Y-02
FieldErrorSummary uses role="alert" and receives focus on mount for screen reader announcement.
A11Y-03
FieldErrorSummary error links use role="link" or native <a> with href="#fieldId" for navigation.
A11Y-04
InlineEdit read mode has role="button" or uses a <button> element; edit label is announced.
A11Y-05
InlineEdit edit mode traps focus within the edit region; cancel/save are keyboard accessible.
A11Y-06
All components pass axe-core automated checks with zero violations.
Content and Documentation Requirements
ID
Requirement
DOC-01
Storybook docs page for each component with usage guidelines, do/don’t, and prop tables.
DOC-02
RHF + Zod integration recipe showing FormSection grouping, FieldErrorSummary wired to formState.errors, and InlineEdit within a form.
DOC-03
Composition guide showing FormSection + FieldGroup + FieldErrorSummary working together in a settings page.
Dependencies
Dependency
Type
Risk
src/components/ui/field-group.tsx
Internal
Low — FormSection wraps multiple FieldGroups.
src/components/ui/text-field.tsx
Internal
Low — InlineEdit default edit control.
src/components/ui/button.tsx
Internal
Low — Save/cancel actions.
react-hook-form
Peer
Low — Error object shape is standard; no hard dependency.
src/styles/motion.css
Internal
Low — Collapse animation tokens.
Risks and Tradeoffs
Risk
Likelihood
Impact
Mitigation
FieldErrorSummary scroll-to-field fails if field IDs don’t match
Medium
Medium
Document ID convention; provide a fieldIdPrefix prop for namespaced forms.
FormSection collapsible state hides required fields from validation
Low
High
Expand collapsed sections containing errors when FieldErrorSummary is rendered.
InlineEdit save/cancel buttons conflict with table row click handlers
Medium
Medium
Stop propagation on InlineEdit button clicks; document this behavior.
Open Questions
#
Question
Owner
Status
OQ-01
Should FormSection support a required indicator showing that the section contains required fields?
David Holmes
Open
OQ-02
Should FieldErrorSummary auto-expand collapsed FormSections containing errors?
David Holmes
Open
OQ-03
Should InlineEdit support multi-field editing (e.g., first name + last name as a single inline edit)?
David Holmes
Open
Acceptance Criteria
#
Criterion
AC-01
FormSection renders a semantic <fieldset> with heading and optional description; collapsible mode works.
AC-02
FieldErrorSummary renders validation errors as clickable links that scroll to and focus the corresponding field.
AC-03
FieldErrorSummary receives focus on mount and announces errors to screen readers.
AC-04
InlineEdit toggles between read and edit modes; save and cancel work via buttons and keyboard shortcuts.
AC-05
All components render correctly in dark mode.
AC-06
All components pass axe-core checks with zero violations.
AC-07
Storybook stories exist with controls for all props.
AC-08
pnpm typecheck and pnpm vitest run --project unit pass with zero errors.
LLM Handoff Instructions
When implementing this FRD:
Start with FormSection — src/components/ui/form-section.tsx. Use <fieldset> and <legend>. For collapsible, use a simple useState with height animation via motion tokens. Follow the existing FieldGroup naming and file conventions.
Then FieldErrorSummary — src/components/ui/field-error-summary.tsx. Accept an errors object matching RHF’s formState.errors shape (Record<string, { message?: string }>). Use scrollIntoView + focus() for navigation. Render in an alert card using StatusCard or a styled div with role="alert".
Then InlineEdit — src/components/ui/inline-edit.tsx. Use a generic type parameter <T> for the value. Default readView renders the value as text in a button-like container. The editControl render prop gives consumers full control over the edit UI.
Stories in sibling .stories.tsx files. Include a “Full Settings Page” story composing all three.
Tests in sibling .test.tsx files. Test scroll-to-error with mocked scrollIntoView, collapse toggle, and InlineEdit mode switching.
Key files to reference:
src/components/ui/field-group.tsx — existing field grouping pattern.