Form
An enhanced native <form> supporting validation and submission of inner fields according to rules.
Cross-shadow submit entry:
oas-formwraps a native<form>inside its shadow root, so buttons in the light DOM (includingoas-button) do not have native submit semantics. Call the component's publicsubmit()method at submit time (e.g.this.closest('oas-form').submit()); it delegates to the inner form'srequestSubmit(), preserving submit-event semantics and triggering the same validation flow that dispatchesoas-submit/oas-validate-fail— do not reach intoshadowRoot.querySelector('form')to drive the inner form.
The data source is each field's
valueattribute (controlled mode). Fields validated by the form areoas-input/oas-textarea/oas-select/oas-auto-complete/oas-cascader/oas-tree-select/oas-input-number/oas-radio/oas-radio-group/oas-date-picker/oas-slider/oas-rate/oas-pin-input/oas-dynamic-tags/oas-combobox/oas-picker/oas-transfer(model-valuechannel) /oas-dynamic-input(model-valuechannel) /oas-checkbox/oas-switch(checkedchannel) with aname.oas-formhas a built-in listener foroas-input/oas-changeevents bubbling from inner fields and automatically writes back to that field'svalueattribute (null becomes an empty string) — no per-field wiring needed.
Feature Demo
The feature demo area only demonstrates field collection and submission, without validation rules.
Basic Usage
Collect & submit
Without rules, submission performs no validation and dispatches oas-submit directly, with detail.values carrying the collected results of all fields with a name.
Mixed Controls
Mixed controls
Nested name paths: nested values
Field
namesupports dot-path syntax: numeric segments are array indices, other segments are object keys —profile.cityassembles into{ profile: { city } };users.0.nameandusers[0].nameare equivalent spellings that assemble into{ users: [{ name }] }. On submit,oas-submit'sdetail.valuesis the nested structure;ruleskeys match by the full path string (e.g."users.0.name");initial-valueswrites in through the same nested paths andreset()restores them. Plain names without path syntax assemble exactly as before.
Nested name path submit
After submitting, values looks like {"name":"Alex","profile":{"city":"Hangzhou"}} — the path segments define the structure.
Dynamic field group form-list
oas-form-listmaintains an add/remove group of field rows inside the form: put a<template>inside the component as the row template; cloned row fields get theirnameindexed automatically — either use the{index}placeholder in the template (name="users.{index}.name"), or setname="users"on the component and write bare names in the template (auto-prefixed tousers.N.field).min/maxconstrain the row count (rows are padded up tomin; loweringminnever removes existing rows, and an invalidmaxis treated as unlimited), each row has a remove button, and after removal the remaining rows are renumbered with their filled values kept. Fields stay in the light DOM, so validation / submit /oas-values-changeall follow the host form; the component dispatchesoas-add/oas-remove(detail: { index }).
Dynamic field group form-list
After submitting, values looks like {"members":[{"name":"Alex","role":"self"},{"name":"Sam","role":"relative"}]}; in plain HTML write the row template as a literal <template>, while in md/Vue compiled contexts prefer injecting an HTMLTemplateElement built in JS (template content pass-through is unreliable under template compilation).
Native Form Integration (form-associated)
Fifteen common form components are form-associated custom elements (formAssociated: true): they work directly inside a native <form> — <label for> association works (clicking the label focuses/activates the control, screen readers announce the label text), values are collected via standard FormData (submitted only when name is set), form.reset() restores initial values, fieldset[disabled] disables them, and required joins the native validation chain (checkValidity() / :invalid pseudo-class).
native form + label for + FormData + reset
Supported (15 components): input / textarea / input-number / checkbox / radio / radio-group / switch / select / combobox / auto-complete / tree-select / mentions / date-picker / time-picker / upload. Value semantics follow the native ones: multiple mode (select / tree-select / date-picker multiple) submits same-name entries; date / time range mode submits name-start / name-end; upload submits File; unchecked checkables submit nothing. Works in parallel with oas-form's collectFields mechanism.
Validation
The validation area demonstrates rules-declared validation rules and failure feedback.
Validation rules:
{ required, message, minLength, maxLength, pattern, validator, validateTrigger }(validatoris a custom validation function; functions are not JSON-serializable, use therulesproperty channel;validateTriggeroverrides the form-level trigger per field). On failure, the field is markedaria-invalid(red-bordered input), an error message is shown in red below the field, andoas-validate-failis dispatched.
Required & Format Validation
Required & format validation
Length Validation
minLength validation
Disabled Fields Skip Validation
Disabled fields are not validated
Submit & Validation-fail Events
submit / validate-fail
Grid Form Layout
With
layout="grid", the form element becomes a 24-column grid andoas-form-itemspans columns viaspan(default 24 = full row);gapcontrols grid spacing,label-alignpositions the label (left/right/top, defaulttop), andlabel-widthsets the label column width forleft/right. On validation failure, error messages are collected into theoas-form-itemerror slot (role="alert").
Two-Column Grid with Validation
Two-column grid layout
label-align & label-width
Switch label-align
Inline Form Layout
With
inline, items are laid out horizontally: the label sits on the left of the control (auto-width), controls size to their content, item spacing followsgap(defaultvar(--oas-space-4)), and items wrap when the container is too narrow. Coexists withlayoutand takes precedence over it; forceslabel-aligntoleftandlabel-widthto auto. Suited to compact toolbars like login and search.
Inline Login Form
Inline login form
Inline Search Form
Inline search form
Controlled syncing and event listeners (wired in one <script> block):
Form-Level Capabilities
Whole-Form Disabled
The
disabledattribute aligns with the config-provider global-disable semantics: all fields are disabled through their own form-associated disable channel (inner controls disabled, interaction blocked), without writing back the field'sdisabledattribute (fields fully recover when released); while the form is disabled, submission skips all validation.
Whole-form disabled
Initial Values & Reset
initial-valuesare written into the corresponding fields after mount (input viavalue, switch viachecked, transfer/dynamic-input viamodel-value);reset()restores the initial values and clears validation error states without dispatching any events (consistent with the native form reset baseline). TheinitialValuesproperty channel is also supported (property takes precedence over attribute).
initial-values and reset
Custom Validation Function (validator)
validatorruns after the built-in checks of its rule: returntrueto pass, a string as the error message, or a Promise for async validation. Functions are not JSON-serializable, so set them through therulesproperty channel (form.rules = {...}in script).
Custom validation function
Validation Trigger (validate-trigger)
validate-triggercontrols when per-field live validation runs:input(default, validate as you type) /blur/change; a rule'svalidateTriggeroverrides the form level. Submission always validates everything. The default live-on-input validation settles errors before the user clicks submit, so an error appearing/disappearing at click time can never shift a control out from under the pointer and swallow the click. Once a field has an error, every subsequent keystroke re-validates it live (clearing as soon as it is corrected). The example below usesblur: type an invalid phone number, then click elsewhere (blur) to see the red error text; fix it and blur again to clear it.
Validate on blur
Scroll to First Error (scroll-to-first-error)
With
scroll-to-first-errorset, a failed submit focuses the first invalid field and smooth-scrolls it into view (degrades to an instant jump when the system prefers reduced motion). The long form below scrolls — the remark field sits at the very bottom; click submit and watch the container scroll down.
Scroll to first error
Values Change Event (oas-values-change)
Dispatched whenever any field value changes,
detail: { name, value, values }(valuesis a snapshot of all current values). Initial-value writes andreset()are silent channels and do not dispatch.
oas-values-change
Script wiring (controlled sync + event feedback for each demo):
Form-Level Size & Label Colon (size / colon)
sizedistributes the form-level size tier (small/medium/large) to field controls inside the form. Priority: a field's own explicitsize> the form-levelsize> config-provider global injection > the default tier (when both form-level and global injection are present, the form-level wins).colonrenders a colon after the label of everyoas-form-itemin the form. Click the buttons below to watch fields resize and the colon toggle live.
Form-level size + colon
Help Text & Status Icon (help / status-icon)
helprenders persistent helper text below the control, independent of validation errors. Withstatus-iconon a form-item, a danger status icon appears before the error text when validation fails (hidden again once the error clears).
help + status-icon
Custom Validation Messages (validate-messages)
validateMessages(property channel, recommended) orvalidate-messages(JSON attribute) overrides locale-default messages per rule type. Templates support${min}/${max}/${value}placeholders; an explicitrule.messagehas the highest priority.
validate-messages
API
Methods
| Method | Description |
|---|---|
submit() | Public submit entry: delegates to the inner form's requestSubmit() (preserves the submit event and submitter semantics); after validation dispatches oas-submit / oas-validate-fail. Across the shadow boundary light-DOM buttons have no native submit semantics, so use this method uniformly (e.g. this.closest('oas-form').submit()) |
validate() | Public validation entry: validates all fields and syncs error states (aria-invalid + error text), returns Promise<boolean>. Pure validation — dispatches no oas-submit / oas-validate-fail and performs no scroll-to-first-error; intended for external gating consumers such as step containers (awaits async validators, skips disabled fields, passes by default with no rules) |
getValues() | Public value entry: a snapshot of all current field values (nested structure), same shape as the detail.values dispatched by submit() — no validation, disabled fields not filtered out; zero-interaction fields prefilled via the value attribute are included. Returns Record<string, unknown> |
oas-form
Attributes
| Attribute | Description | Type | Default |
|---|---|---|---|
colon | Renders a colon after every form-item label in the form (CSS ::after, no DOM text) | — | — |
disabled | Whole-form disable: fields are disabled via their form-associated channel (no write-back of the disabled attribute); submission skips validation while disabled | boolean | — |
gap | Spacing (grid gap in grid mode; item spacing in inline mode), token value e.g. var(--oas-space-4); 0 by default in grid, var(--oas-space-4) by default in inline | string | 0 |
initial-values | Initial values JSON (the initialValues property takes precedence): written into fields after mount; reset() restores them | Record<string, unknown> | string | — |
inline | Inline layout: items laid out horizontally (label on the left of the control, controls auto-width, wraps to new lines); item spacing follows gap (default var(--oas-space-4)); coexists with layout and takes precedence over it; forces label-align to left and label-width to auto | boolean | — |
label-align | Label alignment: left / right / top (default top in grid mode; forced to left in inline mode) | string | top |
label-width | Label column width when label-align is left/right (ignored in inline mode, auto) | — | — |
layout | Layout mode: vertical (default, stacked) / grid (24-column grid); non-enum values fall back to vertical; inline attribute takes precedence when present | string | vertical |
rules | Validation rules JSON: { fieldName: [{ required, message, minLength, maxLength, pattern }] } | Rules | string | — |
scroll-to-first-error | On validation failure, focus the first invalid field and smooth-scroll it into view (instant jump under prefers-reduced-motion) | boolean | — |
size | Form-level size tier distributed to all fields via the data-form-size channel (a field's own explicit size wins; form-associated fields follow dynamically) | — | — |
validate-messages | Overrides locale-default validation messages per rule type (property/JSON channels, ${min}/${max}/${value} placeholders, default fallback) | ValidateMessages | string | — |
validate-trigger | When per-field live validation fires: input (default, validate as you type) / blur / change; a rule's validateTrigger overrides per field; submission always validates everything | string | input |
Events
| Event | Description |
|---|---|
oas-submit | Validation passed, detail: { values } |
oas-validate-fail | Validation failed, detail: { errors, values } |
oas-values-change | Dispatched when any field value changes, detail: { name, value, values } (values is a whole-form snapshot; initial-value writes and reset() are silent) |
Slots
| Name | Description |
|---|---|
| default | Form content (oas-form-item and friends) |
oas-form-item
Attributes
| Attribute | Description | Type | Default |
|---|---|---|---|
help | Persistent helper text below the control, independent of validation errors | string | — |
label | Label text (no label row when omitted) | string | — |
name | Field name (validation association) | — | — |
required | Required asterisk (visual only; validation is still driven by form rules) | boolean | — |
span | Columns spanned in the 24-column grid (only when form layout="grid"; non-integer in 1-24 → 24) | string | 24 |
status-icon | Shows a danger status icon before the error text while the error is present | boolean | — |
Slots
| Name | Description |
|---|---|
| default | Field control |
CSS Variables
| CSS Variable | Default |
|---|---|
--oas-form-label-width | 96px |
On validation failure, failed fields are marked aria-invalid; error messages can be retrieved via form.getErrors(). For fields wrapped in oas-form-item, the error text is collected into the form-item's error slot (role="alert").