Web & UI pack
Claude Skill

Form Builder

Generates accessible HTML forms (signup, contact, multi-step) with validation and error states.

What it does

Given form fields and submission target, produces accessible HTML+Tailwind forms with proper labels, ARIA, native validation, error states, and keyboard support. Handles single-page and multi-step flows. Outputs HTML for vanilla apps OR a controlled React component if requested.

When to use

  • You need a signup, contact, lead-capture, or onboarding form
  • Existing form fails accessibility audits or has clunky error UX
  • Building a multi-step wizard (3-7 steps) with progress + back navigation

When not to use

  • Hosted form builder (Typeform, Tally, Formspark) is fine — use them
  • Highly dynamic form with conditional logic across 30+ fields — use a form library (React Hook Form, Formik, Tanstack Form)
  • Survey with branching logic — use a survey tool

Install

Download the .zip, then unzip into your Claude skills folder.

mkdir -p ~/.claude/skills
unzip ~/Downloads/form-builder.zip -d ~/.claude/skills/

# Restart Claude Code session.
# Skill is now available — Claude will use it when relevant.

SKILL.md

SKILL.md
---
name: form-builder
description: Use when generating an accessible HTML form (signup, contact, multi-step). Triggers on "build a form", "signup form", "contact form", "multi-step form", "form with validation".
---

# Form Builder

Generate accessible forms with real validation and error states. Most forms break accessibility because labels are placeholder-only, errors aren't announced, and keyboard users can't navigate. Build it right.

## Required inputs

1. **Form purpose** — signup / contact / onboarding / payment / etc.
2. **Fields** — for each: label, type (text/email/tel/select/textarea/checkbox/radio), required y/n, validation rules, helper text
3. **Submission target** — endpoint URL + method, or "client-side handler"
4. **Success/error UX** — redirect, inline message, toast?
5. **Framework** — vanilla HTML, React (controlled), or progressive-enhancement (works without JS)

If a field is required but unclear why ("phone number" for a newsletter signup), push back. Every required field is friction.

## Output format

**Default**: HTML+Tailwind with native validation (`required`, `type="email"`, `pattern`, `minlength`). Progressive enhancement — works without JS, better with.

**React**: controlled inputs with `useState` or React Hook Form. Output a single `.tsx` component.

**Multi-step**: state machine (`useReducer` or XState lite) tracking step + per-step validation.

## Field structure (the canonical pattern)

```
<div class="space-y-1">
  <label for="email" class="block text-sm font-medium text-gray-900">
    Work email
    <span class="text-red-600" aria-hidden="true">*</span>
  </label>
  <input
    id="email"
    name="email"
    type="email"
    required
    autocomplete="email"
    aria-describedby="email-help email-error"
    aria-invalid="false"
    class="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
  />
  <p id="email-help" class="text-sm text-gray-500">We'll never share your email.</p>
  <p id="email-error" class="text-sm text-red-600 hidden" role="alert"></p>
</div>
```

Every field gets:
- `<label for="...">` — never placeholder-only
- Real `type` (`email`, `tel`, `url`, `number`) — drives mobile keyboards
- `autocomplete` — `email`, `name`, `organization`, `tel`, `street-address`, `new-password`, etc.
- `aria-describedby` linking to help text and error
- `aria-invalid` flipped to `true` on validation failure

## Validation strategy

**Native first**: `required`, `type`, `pattern`, `min/max`, `minlength/maxlength`. The browser validates for free.

**On submit**: validate all, focus the first invalid field, set `aria-invalid="true"`, show inline error, scroll into view if off-screen.

**On blur**: validate that field only. Don't validate as user types — frustrating.

**Server errors**: announce with `role="alert"` so screen readers catch them. Don't just style red and hope.

## Error UX

- Inline below the field, never as a tooltip
- `role="alert"` on the error element so it's announced
- Tie via `aria-describedby` so the error reads when the field is focused
- Don't disable the submit button preemptively — let the user submit and see ALL errors at once
- After server error, refocus the offending field

## Multi-step flows

For 3+ steps:

1. **Progress indicator** — `<ol aria-label="Progress">` with `<li aria-current="step">` on the active one. Numbered, not just dots.
2. **Step routing** — query param (`?step=2`) or hash so back-button works
3. **Per-step validation** — block "Next" until current step passes
4. **Back button** — preserves entered data; never lose user input
5. **Review step** — final step shows all data with Edit links per section
6. **Save progress** — for 5+ step flows, save to localStorage so refresh doesn't wipe

## Accessibility

- `<form>` with `<fieldset>` + `<legend>` for grouped fields (e.g., billing address)
- Required fields: visible `*` AND `aria-required="true"` (or HTML `required`)
- Error summary at top for long forms: "There were 2 errors. [Email] is invalid. [Phone] is required." — each link focuses the field
- Submit button: not disabled until network in flight. Use `aria-busy="true"` during submit.
- Focus management: after submit success, focus the success message (`tabindex="-1"` + `focus()`)
- Honor `prefers-reduced-motion` for any transitions

## Anti-patterns

- **Placeholder as label**: invisible when typing, fails screen readers
- **Floating labels with poor contrast**: cute, often inaccessible
- **Real-time validation as user types**: shows errors before they finish typing
- **Disabled submit until valid**: user can't tell what's wrong
- **Generic "Something went wrong"**: be specific. "We couldn't reach the email server. Try again?"
- **Required asterisks without explanation**: include "* indicates required field" once at top
- **Stripping autocomplete**: Chrome's autofill is your friend; let it work
- **Captcha on every form**: rate-limit instead. If you must, use invisible reCAPTCHA or hCaptcha.

## Output

Return the form HTML/JSX file. Top comment lists:
- Fields and their types
- Validation rules
- Submit handler approach
- A11y checklist (labels present, errors announced, focus management, autocomplete)

Example prompts

Once installed, try these prompts in Claude:

  • Build a contact form: name, work email, company, message. Required fields, email validation, success/error states, Tailwind styling.
  • Build a 3-step signup wizard: account info → company details → plan selection. Progress bar, back button, validation per step.