Forms and validation
Every Oblyx form control renders a real native input inside its light DOM: an <input>, an <input type="checkbox">, an <input type="radio">. That native element is what joins form.elements, FormData, and the browser's own constraint validation, identically whether it was on the page at load or arrived later in an HTMX swap. Nothing collects or serializes a value on the client; a plain <form> POST finds every control's value the same way it would find a hand-written <input>'s.
A labeled input
label, help and name are plain attributes. The label is always a real <label for> pointing at the control, not decorative text, and help is wired into aria-describedby automatically.
Input with label and help text
<oblyx-input label="Display name" help="Shown on your public profile." name="displayName"></oblyx-input>oblyxInput(label = "Display name", help = "Shown on your public profile.", name = "displayName")Constraint validation
required, pattern, minlength, maxlength and type forward verbatim onto the underlying native <input>, so the browser's own validation applies: :invalid styling, reportValidity(), and submit-blocking all work with zero Oblyx JavaScript. Submit the form below empty to see the browser's native validation message.
Required email input
Submitting with this field empty triggers the browser's own validation UI.
<oblyx-input label="Work email" name="email" type="email" required></oblyx-input>oblyxInput(label = "Work email", name = "email", type = OblyxInputType.EMAIL, required = true)The error state
error is a separate, purely informational attribute: setting it puts the control in its error visual state and sets aria-invalid="true". It does not call setCustomValidity() and does not, on its own, block submission. Use it to display a validation result that came back from elsewhere, such as a server response to a previous submission, alongside or instead of native constraint validation.
Input showing a server-reported error
<oblyx-input label="Username" name="username" value="a" error="Must be at least 3 characters."></oblyx-input>oblyxInput(label = "Username", name = "username", value = "a", error = "Must be at least 3 characters.")No field wrapper
There is no oblyx-field element. Every form control takes label,help and error directly as its own attributes, rather than being wrapped in a separate labeling element. Astryx's own Field/FieldLabel/FieldStatus family contributes little beyond static layout, a label, a description and a status box in fixed order, and its one genuinely coordinated behavior (a horizontal-labels layout mode) is expressed here as a plain CSS descendant selector instead of the wrapper's own coordination logic. Every control's label, help and error markup shares one class convention (.ox-field__label, .ox-field__help,.ox-field__error) so the visual result is identical across controls without a wrapper element gluing them together.
Checkbox
A checkbox submits its value attribute (default: the browser's own "on") under its name only when checked, exactly like a hand-written <input type="checkbox">; an unchecked checkbox contributes nothing to FormData at all.
Checkbox
<oblyx-checkbox label="Send me product updates" name="updates"></oblyx-checkbox>oblyxCheckbox(label = "Send me product updates", name = "updates")Radio grouping via name
There is no oblyx-radio-group element. Any number of <oblyx-radio> instances that share a name attribute are automatically one group, exactly the way plain <input type="radio"> elements group: mutual exclusion, arrow-key roving navigation between them, and which one's value ends up in FormData are all native browser behavior, not Oblyx bookkeeping. Setting required on any radio in the group requires one member of that group to be checked before the form submits, enforced by the browser across the whole group.
This grouping is deliberate for HTMX: a fragment routinely swaps in a single <oblyx-radio> with no siblings and no group wrapper present, which is the normal shape of an HTMX response, not an edge case. Because grouping lives entirely in the name attribute, read by the browser rather than by any Oblyx JavaScript, a lone swapped-in radio already has everything it needs. See HTMX and server-rendered fragments for the general version of this rule.
A radio group
<oblyx-radio label="Email" name="contact-method" value="email" checked></oblyx-radio>
<oblyx-radio label="Phone" name="contact-method" value="phone"></oblyx-radio>
<oblyx-radio label="No contact" name="contact-method" value="none"></oblyx-radio>oblyxRadio(label = "Email", name = "contact-method", value = "email", checked = true)
oblyxRadio(label = "Phone", name = "contact-method", value = "phone")
oblyxRadio(label = "No contact", name = "contact-method", value = "none")A complete form, no JavaScript required to submit
The example below reads out its own submitted FormData so you can see exactly what reaches the server. The inline script that does the reading is here purely for this demo, so the page doesn't navigate away; collecting the values themselves is entirely native browser behavior, not Oblyx code.
Name, subscribe checkbox, and a contact-method radio group
Submit the form to see FormData's contents printed below it.
<form onsubmit="event.preventDefault(); var data = new FormData(this); var pairs = []; data.forEach(function (v, k) { pairs.push(k + '=' + v); }); this.querySelector('output').textContent = pairs.length ? pairs.join(', ') : 'No data submitted.';">
<oblyx-input label="Full name" name="name" required></oblyx-input>
<oblyx-checkbox label="Subscribe to the newsletter" name="subscribe"></oblyx-checkbox>
<oblyx-radio label="Email me" name="contact-method" value="email" checked></oblyx-radio>
<oblyx-radio label="Don't contact me" name="contact-method" value="none"></oblyx-radio>
<oblyx-button type="submit">Submit</oblyx-button>
<output></output>
</form>form {
oblyxInput(label = "Full name", name = "name", required = true)
oblyxCheckbox(label = "Subscribe to the newsletter", name = "subscribe")
oblyxRadio(label = "Email me", name = "contact-method", value = "email", checked = true)
oblyxRadio(label = "Don't contact me", name = "contact-method", value = "none")
oblyxButton(type = OblyxButtonType.SUBMIT) { +"Submit" }
}Reference
<oblyx-input>
| Attribute | Type | Default | Description |
|---|---|---|---|
label | string | undefined | – | Label text, always rendered and always associated with the input via a real <label for>. Required for an accessible name; omitting it is a defect and is flagged loudly rather than silently producing an unlabeled input. |
help | string | undefined | – | Help text, rendered between the label and the input box and wired into aria-describedby. |
error | string | undefined | – | Error message. Presence alone, independent of error being non-empty text, puts the input in its error visual state and sets aria-invalid="true". When non-empty, also renders the message wired into aria-describedby. Purely informational: this does not call setCustomValidity or block submission. |
size | smmdlg | md | Form-control height. |
type | textpasswordemail | text | Native <input type>, forwarded verbatim. |
name | string | undefined | – | Forwarded verbatim to the native <input name>. This is what makes the value appear in FormData/receiveParameters(). |
value | string | undefined | – | Initial value only, applied as an attribute binding rather than a property binding so it never clobbers what the user has typed on a later re-render. |
placeholder | string | undefined | – | Forwarded verbatim to the native <input placeholder>. |
required | boolean | false | Forwarded to native required. Enforces real constraint validation. |
pattern | string | undefined | – | Forwarded verbatim to native <input pattern>. Enforces real constraint validation, matching required/minlength/maxlength. |
minlength | string | undefined | – | Forwarded verbatim to native <input minlength>. Enforces real constraint validation, matching required/pattern/maxlength. |
maxlength | string | undefined | – | Forwarded verbatim to native <input maxlength>. Enforces real constraint validation, matching required/pattern/minlength. |
disabled | boolean | false | Native disabled state, forwarded verbatim to the inner <input>. |
readonly | boolean | false | Native readonly state, forwarded verbatim to the inner <input>. |
autocomplete | string | undefined | – | Forwarded verbatim to the native <input autocomplete>. |
<oblyx-checkbox>
| Attribute | Type | Default | Description |
|---|---|---|---|
label | string | undefined | – | Label text, always rendered and associated with the native input via a real <label for>. Required for an accessible name; omitting it is a defect and is flagged loudly rather than silently producing an unlabeled control. |
help | string | undefined | – | Help text, rendered below the control and wired into aria-describedby. |
error | string | undefined | – | Error message. Presence alone (independent of error being non-empty text) sets aria-invalid="true"; non-empty text also renders the message wired into aria-describedby, matching oblyx-input's error contract. |
size | smmd | md | Box size. |
name | string | undefined | – | Forwarded verbatim to the native <input name>. Makes the checked state appear in FormData/receiveParameters(). |
value | string | undefined | – | Forwarded verbatim to the native <input value>. Native checkboxes submit "on" when checked if this is left unset. Set it only when a consumer genuinely needs a non-default submitted value. |
checked | boolean | false | Initial checked state only. Kept in sync with the live native state on every change event, so reading checkboxEl.checked afterward reflects reality. |
indeterminate | boolean | false | Tri-state mixed indicator. Cleared automatically by native browser behavior the moment the user toggles the control, and kept in sync via the change listener. |
required | boolean | false | Forwarded to native required. An unchecked required checkbox blocks submission natively. |
disabled | boolean | false | Native disabled state, forwarded verbatim to the inner <input>. A disabled checkbox is excluded from FormData, matching native behavior. |
<oblyx-radio>
| Attribute | Type | Default | Description |
|---|---|---|---|
label | string | undefined | – | Label text, always rendered after the control and always associated with the input via a real <label for>. Required for an accessible name; omitting it is a defect and is flagged loudly rather than silently producing an unlabeled radio. |
help | string | undefined | – | Help text, rendered under the control and wired into aria-describedby. |
error | string | undefined | – | Error message. Presence alone puts the control in its error state (aria-invalid="true") and, when non-empty, renders the message wired into aria-describedby, same contract as oblyx-input.error. |
size | smmd | md | Visual size. |
name | string | undefined | – | Forwarded verbatim to the native <input name>, the entire grouping mechanism. Radios sharing this value form one native group with mutual exclusion and arrow-key navigation. |
value | string | undefined | – | Forwarded verbatim to the native <input value>. What actually appears in FormData under name when this radio is the checked one in its group. |
checked | boolean | false | Initial checked state only, bound as the boolean checked HTML attribute (?checked=${...}), never as the .checked IDL property. Per the HTML spec, a radio's checkedness has its own "dirty checkedness flag": once the user clicks it (or the browser un-checks it because a same-named sibling was clicked instead), the live .checked property detaches from the checked content attribute. Re-rendering with toggleAttribute('checked', ...) on every unrelated property change (e.g. toggling error) can therefore never fight the user's or the browser's own native radio-group selection. |
required | boolean | false | Forwarded to native required, enforcing real constraint validation. Per the HTML spec, required on any radio in a name group requires one member of that group to be checked before the group's form can submit; the browser enforces this natively across the whole group. |
disabled | boolean | false | Native disabled state, forwarded verbatim to the inner <input>. A disabled radio is excluded from FormData and from the group's arrow-key navigation, matching plain HTML. |