Validation and accessible feedback
Build accessible forms with clear labels, browser validation, and Qubeforms API feedback. Preserve input and announce submission errors and success messages.
1. Label every visitor-facing control
Use visible labels connected with for and id, or wrap the control in its label. Give each ID a unique value on the page. A placeholder is a hint, not a replacement for a label. Group related checkboxes and radio buttons with fieldset and legend. State required fields in text and connect help text with aria-describedby.
<label for="visitor-email">Email (required)</label>
<input
id="visitor-email"
name="email"
type="email"
autocomplete="email"
aria-describedby="visitor-email-help"
required
>
<p id="visitor-email-help">Use an address where you can receive a reply.</p>
<label for="visitor-message">Message (required)</label>
<textarea id="visitor-message" name="message" maxlength="20000" required></textarea>2. Start with browser validation
Use required, appropriate input types, and realistic length limits. A normal submit runs native validation; in a custom workflow use reportValidity() before the request. Do not call form.submit() to trigger a normal interactive submission: it bypasses constraint validation and submit handlers. Use requestSubmit() when your script needs to invoke that flow. Do not add novalidate unless you implement equivalent accessible validation and feedback yourself.
3. Distinguish browser checks from server checks
Qubeforms checks payload limits, normalization, origins, spam policy, and upload or CAPTCHA requirements. It does not infer a custom required-field schema from your HTML. A client can bypass browser validation. If a workflow depends on authoritative business checks, enforce those in trusted server code before acting on the submission. Marking a consent checkbox required improves the browser flow; it does not prove every request included consent.
4. Announce progress and preserve input
Place an initially empty role="status" region in the page and update its text as sending starts and finishes. Use textContent for response messages. Capture FormData before disabling controls, prevent repeated sends while a request is pending, and enable controls again on completion. Clear fields only after confirmed success. A network error can mean receipt is uncertain, so explain that instead of claiming the data was lost.
<p id="form-feedback" role="status" aria-live="polite"></p>5. Make errors usable
Keep visible error text close to the relevant field and link it with aria-describedby. Set aria-invalid="true" only for a known invalid field and remove it after correction. Do not assign every server error to the email input: Qubeforms errors are not a universal field-error map. For a custom validation summary, move focus to the summary or the first invalid field after a failed submit. Keep ordinary sending and success updates in the status region without moving focus on every change.
6. Test the visitor experience
Use Tab, Shift+Tab, Enter, and Space to complete the form. Check visible focus, labels, error announcements, and retry controls with a screen reader, increased zoom, and a narrow viewport. Test empty required fields, invalid email, long text, and a rejected API response. Keep spam-trap fields out of the focus order and accessibility tree.
Public form keys can appear in browser code. Read keys, management credentials, and webhook signing secrets must stay on the server.