---
name: Form System (Figma)
description: Multi-step forms, complex settings pages, or data-entry flows — fields, validation states, error handling, and progress as a coherent system.
triggers:
  - form
  - form system
  - signup form
  - settings page
  - checkout form
  - data entry
  - multi-step form
  - wizard
cd:
  mode: form
  platform: figma-responsive
  scenario: product
  preview: figma_frames
  fidelity: high
  speaker_notes: false
  animations: false
  featured: true
  design_system:
    requires: true
    sections: [palette, type, spacing, components]
  craft:
    requires:
      - typography
      - typography-hierarchy
      - color
      - anti-ai-slop
      - accessibility-baseline
      - state-coverage
      - form-validation
      - laws-of-ux
  inputs:
    - form purpose (signup / checkout / settings / data-entry / survey)
    - field set (what the form actually asks for)
    - step count (single page vs multi-step; default single unless > 8 fields)
  parameters:
    - canvas (default 768 centered card on 1440; also 375 mobile)
    - label_style (above / floating — default above)
  outputs:
    primary: figma_frames
    capabilities_required:
      - figma_execute
  default_for:
    - form
    - forms
    - signup
    - checkout
  example_prompt: "3-step checkout form — contact info, shipping, payment — with progress indicator and inline validation"
---

## A form is a conversation, not a checklist

Every field asks the user for something; every field they quit on is a cost. Before composing, answer:

1. **What is the minimum set of fields that lets us deliver value?** Anything else moves to a later step, to settings, or gets cut.
2. **What order minimises cognitive load?** Known-facts first (name, email), judgement-needed later (preferences, tiering choices).
3. **Where can the system do work for the user?** Detect country from phone prefix. Auto-fill city from ZIP. Default the next field.

A 5-field form that respects these rules converts better than a 10-field form that doesn't.

## Layout rules

- **Single column.** Two-column field layouts break tab order and confuse mobile. The only acceptable side-by-side pair is short related fields (First name / Last name, City / ZIP) and only on desktop.
- **Label above field, not floating.** Floating labels save a line but hurt scanning, accessibility, and the "which field am I in" moment. Top labels are the mature default. Use floating only when the DS explicitly mandates it.
- **Spacing:** 16–24 between field groups, 8 between label and input, 4 between input and helper text. Hold the three values across every form in the product.
- **Required vs optional:** mark optional fields (`(optional)` muted text after the label), not required ones. Most fields are required; marking the minority is less noise.
- **CTA placement:** primary action bottom-right on desktop (agrees with reading flow), full-width bottom on mobile. Secondary action as a text link to the left, not a second button.

## Multi-step forms

Use steps when:

- The form has > 8 fields total, OR
- Fields naturally group by concern (contact → shipping → payment), OR
- A later step depends on an earlier answer (branching flow).

When stepping:

- Show a progress indicator: numbered steps with labels at the top (desktop) or a "Step 2 of 4" caption + progress bar (mobile).
- Each step must be revisitable — clicking a completed step jumps back without losing the current step's data.
- Do not auto-advance. Pressing Enter in the last field of a step should submit that step explicitly via a "Next" button.
- First step gets the highest-priority fields. Don't front-load marketing consent.

## Field anatomy

Every text input is a system of six things — define all six in the DS:

1. **Label** — `color.fg`, body-size, 500 weight. Above the input, 4–8 below.
2. **Input default** — 44pt tall minimum (touch target), 12–14pt horizontal padding, 1px border at `color.border`, 6–8pt radius per DS, background `color.surface`.
3. **Input focused** — border shifts to `color.accent`, 2px wide or with a 2px outer ring. Inner background unchanged.
4. **Input filled** — identical to default. Don't change color just because it has content.
5. **Helper text** — 12–13pt, `color.muted`, 4 below the input. Used for format hints ("MM / YY") and legal copy.
6. **Error state** — border shifts to `color.error` (or DS semantic error token), helper text replaced by error message at `color.error`. The error message is actionable: "Enter a valid 10-digit phone number." not "Invalid input."

Dropdowns, toggles, checkboxes, radios, date pickers all inherit this six-part contract — render each state explicitly in the Figma file so the engineering hand-off has no gaps.

## Validation

- **Inline and immediate-ish:** validate on blur, not on keystroke, except for password strength and availability checks.
- **Resolve on fix:** if the user corrects an error, clear it immediately — don't wait for blur.
- **Submit-time validation is a backstop, not the primary path.** Catch it inline. If server-side validation fails after submit, scroll to the first field with an error and focus it.
- **Never use color alone** to signal error. Always pair with an icon and text. A11y failure otherwise.
- **Specific, human error messages.** Never "Error" or "Invalid". "Your password needs a number." is the bar.

## Copy discipline

- Labels are nouns, not sentences. "Email" not "What is your email address?"
- Helper text is terse and useful: format hint, privacy reassurance, or nothing. Don't pad.
- Button label describes the outcome: "Create account", "Pay $49.00", "Send reset link". Not "Submit" and not "Continue" when a more specific verb exists.
- Error text addresses the user and suggests a fix. "We couldn't find that email — check for typos or [create an account]."

## State coverage

Every form must define (and render in Figma) these states:

1. **Default** — empty fields, primary CTA enabled if the form is fill-as-you-go, disabled if all fields are required to enable.
2. **Partially filled** — a mix of filled/empty/focused fields. This is the typical lived state.
3. **Error** — at least one field in error, error summary at the top (optional), CTA still visible.
4. **Submitting** — CTA shows a spinner with label ("Creating…"). Fields disabled but not hidden.
5. **Success** — confirmation screen or inline success state with clear next step.

## Accessibility

- Label is programmatically associated with input (`<label for>` on web, semantic wiring in the design spec).
- Tab order follows visual order. No skipping across columns.
- Error messages linked via `aria-describedby` (document in Figma notes).
- Minimum tap target 44×44 for any control.
- Color contrast ≥ 4.5:1 for all copy; ≥ 3:1 for the input border against its background.

## Anti-patterns

- A "Clear form" button adjacent to "Submit" — one misclick wipes a 10-minute fill. If reset must exist, make it a text link, far from submit.
- Masked password with no "show password" toggle. Everyone mistypes.
- Floating labels combined with required-field asterisks — two signals competing for the same pixel.
- Error state that shifts field geometry (field grows taller when error appears). Reserve the helper-text space at default state.
- Progress indicator that's decorative but not interactive. If you can't tap a previous step to return, don't show it.
- Animated placeholder text ("Try: name@company.com" fading in and out). Jittery and hurts read speed.

## Quality bar

Tab through the form from the first field to the submit button. Every focus state must be clearly visible without squinting — that's the single most common a11y failure in form UIs. Run `craft_lint` over the label/helper/error copy.
