form-field
Accessible form field wrapper with label, input, and validation message. Enables CSS-only validation feedback using native HTML5 validation and the <output> element.
Attributes
| Attribute | Values | Default | Description |
|---|---|---|---|
data-type |
checkbox, radio, file |
- | Special styling for non-text inputs |
data-no-icon |
boolean | - | Disable validation status icons |
data-required |
boolean | - | Visual indicator (auto-detected from input) |
data-valid |
boolean | - | Force valid state styling |
data-invalid |
boolean | - | Force invalid state styling |
Input Attributes (for JS enhancements)
| Attribute | Values | Default | Description |
|---|---|---|---|
data-type (on input) |
otp, pin |
- | Enable OTP/PIN multi-box enhancement |
data-length (on input) |
4, 6, 8 |
6 |
Number of digits for OTP/PIN input |
Element Structure
The form-field element groups a label, input control, and optional validation message.
| Child Element | Purpose | Key Attributes |
|---|---|---|
<label> |
Accessible field label | for (matches input id) |
<input> / <textarea> / <select> |
Form control | id, name, validation attrs, aria-describedby |
<output> |
Validation/help message | id, for, aria-live="polite" |
Basic Text Input
Code
Input Types
Email Input
Password Input
Textarea
Select Dropdown
Password Toggle
Password inputs are automatically enhanced with a show/hide toggle button when JavaScript is available. No extra markup is needed - just use type="password".
The toggle button:
- Appears automatically when JavaScript loads
- Uses accessible ARIA attributes (
aria-pressed,aria-label) - Works alongside validation icons (positions adjust automatically)
- Preserves tab order and keyboard navigation
Code
Validation Status Icons
Form fields automatically display validation status icons after user interaction:
- Checkmark appears when the field is valid
- X appears when the field is invalid
Icons are positioned inside the input's padding and adjust automatically for password fields with toggle buttons.
Disabling Icons
Use data-no-icon on the form-field to disable validation icons. Useful for search fields or other contexts where icons aren't needed.
Code
OTP/PIN Input
For verification codes, use data-type="otp" on the input. This creates a progressive enhancement pattern:
- Without JavaScript: Works as a standard text input
- With JavaScript: Enhances to a multi-box input with automatic focus management
Features
- Auto-focus moves to the next box after entering a digit
- Backspace moves focus to the previous box
- Arrow keys navigate between boxes
- Paste support fills multiple boxes at once
- Original input becomes hidden and stores the combined value
Code
4-Digit PIN Variant
Output Message Classes
Use class="hint" and class="error" on output elements for different message types:
| Class | Visibility | Use Case |
|---|---|---|
.hint |
Always visible (hides when valid) | Format requirements, help text |
.error |
Only when invalid | Error messages |
Code
Checkbox and Radio
Single Checkbox
Radio Group
Code
CSS-Only Validation
The form-field element uses the :user-valid and :user-invalid pseudo-classes to show validation states only after user interaction. This prevents premature error states on page load.
Try It - Type and then clear each field
How It Works
JS-Enhanced Validation
Add data-validate to a <form> to layer JS validation on top of the CSS foundation. This enables features CSS can't handle: custom error messages, cross-field validation, checkbox group constraints, and error summaries.
Without data-validate, everything works exactly as before — pure CSS validation via :user-valid/:user-invalid.
JS Validation Attributes
| Attribute | On | Description |
|---|---|---|
data-validate |
<form> |
Opt-in. No value = field-level errors (+ summary if present). "summary" = summary only. |
data-message-required |
input / textarea / select | Custom "required" error text |
data-message-type |
input | Custom "type mismatch" error text (email, url, etc.) |
data-message-minlength |
input / textarea | Custom "too short" error text |
data-message-maxlength |
input / textarea | Custom "too long" error text |
data-message-min |
input | Custom "range underflow" error text |
data-message-max |
input | Custom "range overflow" error text |
data-message-pattern |
input | Custom "pattern mismatch" error text |
data-match |
input | ID of field that must have the same value |
data-message-match |
input | Custom message when data-match fails |
data-min-checked |
fieldset | Minimum number of checked checkboxes |
data-max-checked |
fieldset | Maximum number of checked checkboxes |
data-state="validating" |
form-field | CSS hook for async validation spinner (CSS-only, no JS) |
Custom Error Messages
Use data-message-* attributes on inputs to specify per-constraint error messages. The right message appears for the right validation failure.
Code
Password Confirmation
Use data-match to require two fields to have the same value. The most common use case is password confirmation.
Code
Checkbox Group Constraints
Use data-min-checked and data-max-checked on a <fieldset> to constrain how many checkboxes can be checked.
Code
Error Summary
Add an <output class="error-summary"> inside your form for a consolidated list of all errors on submit. Three display modes:
| Mode | Attribute | Behavior |
|---|---|---|
| Field-level only | data-validate (no summary output) |
Errors appear inline next to each field |
| Summary only | data-validate="summary" |
Summary at top, field-level errors hidden |
| Both | data-validate + summary output present |
Summary at top AND inline errors on each field |
On submit failure, the summary lists all errors as links. Clicking a link scrolls to and focuses the relevant field. The summary auto-clears when all errors are fixed.
Code
Why Use <output> for Messages?
The <output> element is semantically ideal for validation messages:
- Purpose-built: Represents the result of a calculation or user action
- Native association: Has
forattribute to link to input(s) - Accessible: Works naturally with
aria-livefor screen readers - Semantic distinction: Clearly different from static help text
- No JavaScript required: Can show different states via CSS
| Approach | Semantics | Accessibility | Recommendation |
|---|---|---|---|
<output> |
Result of action | Native support | Recommended |
<span class="error"> |
None | Manual ARIA needed | Avoid |
<div class="message"> |
None | Manual ARIA needed | Avoid |
Complete Contact Form
Code
Accessibility Checklist
When building forms with form-field, ensure:
- Every
<input>has a<label>with matchingfor/id - Required fields use the
requiredattribute - Validation messages use
<output>witharia-live="polite" - Inputs link to messages via
aria-describedby - Form controls have appropriate
autocompletevalues - Focus states are clearly visible
- Error states don't rely on color alone (include text)
- First input has
autofocusfor immediate interaction
HTML5 Validation Attributes
Use native validation attributes for client-side validation:
| Attribute | Purpose | Example |
|---|---|---|
required |
Field must have value | <input required /> |
minlength |
Minimum character count | <input minlength="2" /> |
maxlength |
Maximum character count | <input maxlength="100" /> |
pattern |
Regex pattern | <input pattern="[A-Za-z]+" /> |
type |
Input type validation | type="email", type="url" |
min/max |
Number/date range | <input type="number" min="0" max="100" /> |
CSS Implementation
Related Elements
- layout-stack - Stack form fields vertically
- layout-cluster - Group buttons horizontally
- form - Native form element
- input - Native input element