Forms with Lumen Elements
Use a native form container. Lumen’s input elements participate in values, validity, disabled state, reset, and focus.
- Container
- <form>
- Values
- FormData
- Fallback
- Native control
Keep the form native
Use form data-ui-form, not lumen-form, as the submission boundary. The custom controls use ElementInternals where the platform supports it and a single native control fallback otherwise. Both paths submit the same value and avoid duplicate proxy fields.
<script type="module">
import { defineLumenElements } from '@santi020k/lumen-elements/define'
import '@santi020k/lumen-elements/styles.css'
defineLumenElements()
</script>
<form data-ui-form id="profile" method="POST">
<lumen-field>
<lumen-label for="profile-email">Email</lumen-label>
<lumen-input
id="profile-email"
name="email"
required
type="email"
></lumen-input>
<lumen-field-error id="profile-email-error"></lumen-field-error>
</lumen-field>
<lumen-password-field
autocomplete="current-password"
name="password"
required
></lumen-password-field>
<lumen-button type="submit">Sign in</lumen-button>
</form><script type="module">
import { defineLumenElements } from '@santi020k/lumen-elements/define'
import '@santi020k/lumen-elements/styles.css'
defineLumenElements()
</script>
<form data-ui-form id="profile" method="POST">
<lumen-field>
<lumen-label for="profile-email">Email</lumen-label>
<lumen-input
id="profile-email"
name="email"
required
type="email"
></lumen-input>
<lumen-field-error id="profile-email-error"></lumen-field-error>
</lumen-field>
<lumen-password-field
autocomplete="current-password"
name="password"
required
></lumen-password-field>
<lumen-button type="submit">Sign in</lumen-button>
</form>Native form API
Scalar controls expose value, name, form, validity, validationMessage, willValidate, checkValidity(), reportValidity(), setCustomValidity(), focus(), and checked properties where relevant.
const form = document.querySelector('#profile')
const data = new FormData(form)
data.get('email')
form.reset()
const email = document.querySelector('lumen-input')
email.value = 'maya@example.com'
email.setCustomValidity('')
email.reportValidity()const form = document.querySelector('#profile')
const data = new FormData(form)
data.get('email')
form.reset()
const email = document.querySelector('lumen-input')
email.value = 'maya@example.com'
email.setCustomValidity('')
email.reportValidity()Submission contracts
| Element | Submitted value |
|---|---|
| lumen-input | Scalar text, email, URL, telephone, and password values |
| lumen-number-field | Numeric value with min, max, and step |
| lumen-search-field | Search string |
| lumen-time-field | Native time string |
| lumen-slider | Range value |
| lumen-color-picker | Color value |
| lumen-checkbox | Checked value or no submitted value |
| lumen-switch | Checked value or no submitted value |
| lumen-textarea | Multi-line string |
| lumen-select / lumen-list-box | Native select-backed scalar or repeated values |
| lumen-password-field | Native password input with visibility enhancement |
| lumen-input-otp | Native one-time-code string |
Events and validation
Control events
Listen for bubbling, composed input and change events on the custom control or form.
Form events
The data-ui-form enhancement emits ui:validate, ui:invalid, and ui:valid while retaining the browser Constraint Validation API.
Migration from proxy controls
- Keep a native form element as the container; do not replace it with lumen-form.
- Read and write the custom control’s public value or checked property.
- Listen for ordinary bubbling input and change events instead of private proxy events.
- Do not query or submit an internal input; the host’s form contract is authoritative.
- Browsers with ElementInternals use form-associated custom elements. Other supported browsers receive one native fallback control with the same value and validation behavior.