Form UX skill for Codex

Forms people finish on the first try. Codex audits every field before building, uses a single column with visible labels, sets type, autocomplete and inputmode so autofill and mobile keyboards work, and validates at the right moment instead of on the first keystroke. Errors are specific, linked with aria-describedby and summarized on submit, and multi-step flows keep progress and offer a review step.

  • Design & UX
  • $form-ux
  • 184 lines
  • 2 files
  • ~2,100 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- form-ux

Installs to ~/.agents/skills/form-ux/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $form-ux, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Designs forms people finish - visible labels, single-column layout, the right input types, autocomplete and inputmode tokens, validation timing (on blur, then live after an error), accessible inline errors with aria-describedby and an error summary, multi-step flows with saved progress and a review step, password, date, address and phone patterns, and submit, pending and success states. Use when building or reviewing signup, checkout, settings or onboarding forms, when form completion or error rates are poor, or when asked how and when to show validation errors.

Use it when

  • Building signup, checkout, settings or application forms
  • Deciding validation timing and writing error messages
  • Splitting a long form into steps
  • Autofill, mobile keyboards or screen readers misbehave on a form

Use something else when

How to check the result

Submitting an empty form focuses a summary whose links reach each field, errors clear as soon as they are fixed, and browser autofill fills the right inputs.

The mistake it prevents

Disabling the submit button until the form is valid, which hides why it cannot be submitted.

~/.agents/skills/form-ux/SKILL.md

--- name: form-ux description: Designs forms people finish - visible labels, single-column layout, the right input types, autocomplete and inputmode tokens, validation timing (on… license: MIT ---

A form is right when people can complete it without guessing what a field wants, errors appear at the moment they are useful and say how to fix them, nothing they typed is lost (on error, on back, on refresh), and it works with keyboard, screen reader, password manager and autofill. The best field is the one you removed.

Before designing

  • Read AGENTS.md and the existing form stack (React Hook Form, Formik, server actions, native forms) and the field components in the design system.
  • List every field and justify it: who uses the value, and can it be inferred, deferred to later, or made optional? Remove or defer what you can.
  • Note server-side validation rules; client rules must mirror them, never replace them.

Layout rules

  • One column. Multi-column forms cause skipped fields; exceptions are tightly related short pairs (city + postcode, expiry + CVC).
  • Label above the input, always visible. Placeholder is for an example format at most, never the label.
  • Field width hints the expected length: postcode short, street full width.
  • Mark the minority: if most fields are required, mark optional ones “(optional)”; otherwise mark required ones. Do not rely on a red asterisk alone.
  • Group with <fieldset> + <legend> (shipping address, payment). Radio groups and checkbox groups always use fieldset.
  • Primary button left-aligned with the fields (or full width on mobile), labelled with the outcome: “Create account”, “Pay $48.00”, not “Submit”.

Input types and autofill

FieldMarkup
Emailtype="email" autocomplete="email" spellcheck="false"
New passwordtype="password" autocomplete="new-password" + show/hide toggle
Current passwordautocomplete="current-password"
One-time codeautocomplete="one-time-code" inputmode="numeric" (not type="number")
Nameautocomplete="name" (or given-name/family-name only if truly needed)
Phonetype="tel" autocomplete="tel"; accept any formatting, normalize on server
Addressautocomplete="address-line1", address-level2 (city), postal-code, country
Card numberautocomplete="cc-number" inputmode="numeric"; let the payment provider’s field handle it when possible
Quantity, ageinputmode="numeric" text input; type="number" scroll-wheel and spinner bugs bite
Date of birthThree text fields (day, month, year) or one with a clear format; not a calendar picker
Date near todayNative type="date" or an accessible picker with typed input allowed

Never block paste (passwords, codes, emails). Never disable autofill.

Validation timing

MomentDo
While typing, field not yet in errorNothing. Do not shout “invalid email” at the first character
On blurValidate format of that field; show error if wrong
After a field has shown an errorRe-validate on each input so the error clears as soon as it is fixed
On submitValidate everything; show error summary; move focus to it
Server responseMap server errors to fields; general errors go in the summary

Exceptions: password strength rules and username availability can show live progress as positive guidance, not errors.

Error markup

<div class="field">
  <label for="email">Work email</label>
  <p id="email-hint" class="hint">We'll send the invoice here.</p>
  <input id="email" name="email" type="email" autocomplete="email"
         aria-describedby="email-hint email-error" aria-invalid="true">
  <p id="email-error" class="error">
    <svg aria-hidden="true" class="icon"><use href="#icon-alert"/></svg>
    Enter an email address like [email protected]
  </p>
</div>
  • Error text says what to do, specific to the problem: “Enter a date in the past”, not “Invalid input”. Keep the user’s input; never clear a field on error.
  • Show error below the input (or between label and input), with icon plus color.
  • Set aria-invalid="true" only when in error; remove it when fixed.
  • Use novalidate on the form when you render your own messages, so browser bubbles do not compete, but keep required and type for semantics.

Error summary on submit, focused so screen readers announce it (focus alone announces it; adding role="alert" as well makes some readers announce it twice):

<div id="error-summary" tabindex="-1" aria-labelledby="error-summary-title">
  <h2 id="error-summary-title">There are 2 problems with your details</h2>
  <ul>
    <li><a href="#email">Enter an email address like [email protected]</a></li>
    <li><a href="#postcode">Enter a postcode</a></li>
  </ul>
</div>
form.addEventListener("submit", (e) => {
  const errors = validate(form);           // project validator
  if (errors.length) {
    e.preventDefault();
    renderSummary(errors);
    document.getElementById("error-summary").focus();
  }
});

For short forms (1–3 fields), focusing the first invalid field instead of a summary is fine. Implementation with React Hook Form and Zod: react-hook-form-zod.

Multi-step forms

Split when the form exceeds roughly 7–10 fields or has distinct topics; keep it on one page when fields depend on each other heavily.

  • One topic per step; title says what it is (“Delivery address”).
  • Progress indicator with step names, not just dots: “Step 2 of 4: Delivery”.
  • Validate each step before advancing; never show errors from steps not yet visited.
  • Back never loses data. Persist progress (server draft or sessionStorage) so a refresh does not restart. Do not store payment or sensitive data client-side.
  • Final review step with “Change” links back to each step.
  • Do not ask for the same information twice (WCAG 2.2 SC 3.3.7 Redundant Entry): “Billing address same as delivery” checkbox, prefilled values.

Submission states

  • Pending: keep the button enabled-looking but ignore repeat clicks (or aria-disabled plus spinner and “Creating account…” label). Prevent double submit on the server too (idempotency key).
  • Do not disable the submit button until the form is valid; users cannot discover why it is disabled. Let them submit and show the errors.
  • Success: confirm what happened and what is next; move focus to the confirmation heading. For destructive or payment actions, show a confirmation step before submit.
  • Network failure: keep all input, show a retry, explain whether anything was saved.

Specific patterns

  • Passwords: show requirements before typing, show/hide toggle, allow paste and password managers, no composition rules beyond length when policy allows (NIST 800-63B favors length and breach checks).
  • Authentication: do not require cognitive tests like transcribing characters without alternatives (WCAG 2.2 SC 3.3.8); support passkeys or magic links if the product does.
  • Long selects: over ~15 options, use a combobox with type-to-filter.
  • Checkbox for consent: unchecked by default, label is the full statement.

Workflow

- [ ] 1. Field audit: remove, defer or infer
- [ ] 2. Choose single page vs multi-step
- [ ] 3. Markup: labels, types, autocomplete, fieldsets
- [ ] 4. Validation rules shared with server; timing per the table
- [ ] 5. Error messages written per field and failure
- [ ] 6. Pending, success, network-failure states
- [ ] 7. Verify with keyboard, screen reader, autofill and mobile keyboard

Verification

  • Tab through the whole form: logical order, visible focus, every control reachable.
  • Submit empty: summary appears and is focused; each link moves to its field.
  • Fix one error: it clears immediately; others remain.
  • Autofill from the browser and a password manager fills the right fields.
  • On a phone (or --device "iPhone 13" in Playwright), numeric fields open the number pad and email fields the email keyboard.
  • Screen reader announces label, hint and error for an invalid field.
  • Refresh mid-way through a multi-step form: progress is kept.

Output contract

**Fields:** <n> (removed <list>, deferred <list>)
**Structure:** single page | <n> steps: <names>
**Validation:** on blur, live after error, summary on submit; rules shared via <schema path>
**Error messages:** <file or table>
**States:** pending, success, network failure, server field errors
**Checks:** keyboard ok, VoiceOver/NVDA ok, autofill ok, mobile keyboards ok

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Form UX"
  short_description: "Forms people finish on the first try"
  default_prompt: "$form-ux Our signup form has a 40% drop-off, review it and fix the UX"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem