Accessible Form Builder
Build a form visually and get markup that’s genuinely accessible — not just an input next to a div. Every field is tied to its <label for>; radio and checkbox groups are wrapped in <fieldset><legend>; help text and an inline error region are wired with aria-describedby; required fields get aria-required; and a focusable error-summary sits at the top. It emits the right type/inputmode and an autocomplete token per field for WCAG 1.3.5, in HTML or JSX, and audits your form for the bugs people ship every day.
About this ToolHow it works, benefits & use casesTap to collapse
Most “form builders” hand you an input sitting next to some text and call it accessible. This one emits markup that a screen reader can actually use. Add fields visually — text, email, password, phone, URL, number, date, textarea, select, radio groups, checkbox groups, or a single checkbox — and the generator ties every control to a real <label for>, wraps radio and checkbox sets in <fieldset><legend>, wires help text and an inline error region through aria-describedby, marks required fields with both the native required attribute and aria-required, and drops a focusable error-summary region at the top of the form. It also sets the right type and inputmode for each field and emits an autocomplete token (WCAG 1.3.5, Identify Input Purpose) so browsers and password managers can fill it. A live preview renders the form as you build it, an audit panel flags the classic mistakes (missing labels, placeholder-as-label, known fields without autocomplete, choice groups without a fieldset, duplicate names), and you can copy the whole thing as HTML or JSX. The full configuration is captured in a shareable URL.
How to Use
- 1Give the form a title — it becomes the form’s <h2> heading and accessible name via aria-labelledby.
- 2Add fields and pick a type for each; the editor shows only the relevant options (e.g. an options list for selects and groups).
- 3Set the label and name, toggle Required, and add help text — it’s wired to the field with aria-describedby.
- 4For known fields, choose an autocomplete token (email, name, tel, postal-code…) so the form satisfies WCAG 1.3.5.
- 5Reorder fields with the up/down controls, watch the live preview update, and read the audit panel for issues.
- 6Toggle HTML or JSX, copy the generated markup, or share the URL.
Key Benefits
- Every field gets a real <label for> tied by id — never a placeholder masquerading as a label
- Radio and checkbox groups are wrapped in <fieldset><legend> so the group has one shared accessible name
- Help text and a per-field role="alert" error region are wired with aria-describedby
- Required fields get the native required attribute plus aria-required and a visible marker
- A focusable error-summary region (role="alert", aria-live) sits at the top of the form
- Correct type/inputmode and an autocomplete token per field for WCAG 1.3.5 (Identify Input Purpose)
- Live audit catches missing labels, placeholder-as-label, missing autocomplete, group-without-fieldset, and duplicate names
- Copy as HTML or JSX, with a shareable URL that captures the whole form
Common Use Cases
- Scaffolding a contact, signup, or checkout form whose fields are correctly labelled and grouped
- Adding autocomplete tokens to an existing form to pass WCAG 1.3.5 and enable browser autofill
- Getting a fieldset/legend pattern for a set of radio buttons or checkboxes without writing it by hand
- Producing a reference example of an inline error pattern (aria-describedby + role="alert") and error summary
- Generating accessible React/JSX form markup with htmlFor and self-closing inputs
Accessible markup — HTML
<form id="contact-us" class="accessible-form" aria-labelledby="contact-us-title" novalidate>
<h2 id="contact-us-title">Contact us</h2>
<p class="form-intro">Send us a message and we’ll get back to you.</p>
<div
id="contact-us-errors"
class="error-summary"
role="alert"
aria-live="polite"
tabindex="-1"
hidden
>
<h3>There’s a problem</h3>
<ul></ul>
</div>
<div class="field">
<label for="name" class="field-label">Full name <span class="req" aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" required aria-required="true" autocomplete="name" aria-describedby="name-error">
<p class="field-error" id="name-error" role="alert" aria-live="polite"></p>
</div>
<div class="field">
<label for="email" class="field-label">Email address <span class="req" aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" required aria-required="true" autocomplete="email" inputmode="email" aria-describedby="email-help email-error">
<p class="field-help" id="email-help">We’ll only use this to reply.</p>
<p class="field-error" id="email-error" role="alert" aria-live="polite"></p>
</div>
<div class="field">
<label for="message" class="field-label">Message <span class="req" aria-hidden="true">*</span></label>
<textarea id="message" name="message" rows="4" required aria-required="true" aria-describedby="message-error"></textarea>
<p class="field-error" id="message-error" role="alert" aria-live="polite"></p>
</div>
<button type="submit" class="submit">Contact us</button>
</form>Live preview
Contact us
Send us a message and we’ll get back to you.
We’ll only use this to reply.
Form
Fields (3)
Field 1Text
Field 2Email
Field 3Textarea
An input next to text isn’t a labelled field
A screen reader only announces a field’s name when the <label> is programmatically tied to it — usually with for/id. A placeholder isn’t a label: it disappears the moment someone types, fails colour-contrast, and isn’t reliably read. This builder always emits a real label, wires help text and an inline error region through aria-describedby, and adds an autocomplete token so browsers and password managers can fill the field (WCAG 1.3.5).
What it gets right that hand-rolled forms miss
- Groups — radio and checkbox sets are wrapped in
<fieldset><legend>so the group has one shared name. - Required — both the native
requiredattribute andaria-required, plus a visible marker. - Errors — a per-field
role="alert"region tied byaria-describedby, and a focusable error-summary at the top of the form. - Input purpose — the right
type/inputmodeand an autocomplete token per field.
Was this tool helpful?
Share Your Experience
Help others discover this tool!
Related tools
- HTML FormatterFormat and beautify HTML code
- Badge GeneratorBuild shields.io README badges with live preview, assemble a badge row, and export Markdown, HTML, reStructuredText or AsciiDoc
- Progress Bar GeneratorGenerate linear, circular, stepped & indeterminate bars (gradient/striped/pulse) with ARIA and a live preview
- ARIA Label GeneratorGenerate accessible names and preview what a screen reader announces
- Focus Order ValidatorVisualize DOM order vs tab order and catch positive-tabindex bugs
- Screen Reader Text GeneratorGenerate sr-only / visually-hidden text + CSS, with a focusable variant
A placeholder isn’t programmatically a label, so many screen readers don’t announce it as the field name, and it disappears the instant someone starts typing — taking the only hint with it. It also typically fails colour-contrast requirements because placeholder text is rendered in a low-contrast grey. WCAG asks for a persistent, associated label (a <label for> or a legend). This builder always emits one and lets you use the placeholder only for an example value, which is its actual purpose.
A group of radio buttons (or related checkboxes) answers a single question, so it needs a single group name. The <fieldset>/<legend> pattern provides exactly that: assistive tech announces the legend as the group’s name and then each option, so users hear “Shipping speed, Standard, radio button, 1 of 3” instead of three orphaned controls. Individual <label> elements on each option still name the options themselves. A single yes/no checkbox doesn’t need a fieldset and the builder won’t add one.

