Liking cljdoc? Tell your friends :D

x-form-field

A self-contained, form-associated text-input field with a label, optional hint, and optional error message. The component owns its internal <input> element inside shadow DOM. Consumers control validity by setting the error attribute.


Tag name

<x-form-field></x-form-field>

Observed attributes

AttributeTypeDefaultNotes
labelstring""Visible label text (hidden when empty)
typeenum"text"One of: text, email, password, url, number, tel
namestring""Form field name (passed to ElementInternals)
valuestring""Current input value
placeholderstring""Input placeholder
hintstring""Helper text below input (hidden when empty)
errorstring""Error message (hidden when empty; marks invalid)
disabledbooleanfalseDisables the field
readonlybooleanfalseMakes the field read-only
requiredbooleanfalseMarks required (native + ARIA)
autocompletestring""Passed through to inner <input>

Properties

PropertyTypeReflects attribute
valuestringvalue
labelstringlabel
typestringtype
namestringname
placeholderstringplaceholder
autocompletestringautocomplete
hintstringhint
errorstringerror
disabledbooleandisabled
readOnlybooleanreadonly
requiredbooleanrequired

Value sync note

The value property setter updates both the value attribute and the shadow <input> value. On user input, the component does not reflect back to the value attribute (avoids cursor jumps). The attribute only changes when set programmatically.


Events

EventBubblesComposedCancelableDetail
x-form-field-change-requesttruetruetrue{ name: string, value: string, previousValue: string }
x-form-field-inputtruetruefalse{ name: string, value: string }
x-form-field-changetruetruefalse{ name: string, value: string }

x-form-field-change-request fires on every keystroke, before the form value changes and before x-form-field-input. Call preventDefault() to refuse the change. The input, the form value and the validity then go back to the value attribute, and x-form-field-input does not fire. previousValue is the value attribute. An application that refuses requests writes each value it accepts to value.

x-form-field-input fires on every keystroke (mirrors native input event). x-form-field-change fires on blur/commit (mirrors native change event).


Shadow DOM structure

<style>…</style>
<div part="field">
  <label part="label" id="label" for="input">…</label>
  <div part="input-wrapper">
    <input part="input" id="input"
           aria-labelledby="label"
           aria-describedby="hint error" />
  </div>
  <span part="hint"  id="hint"  aria-live="polite">…</span>
  <span part="error" id="error" role="alert" aria-live="assertive">…</span>
</div>

aria-describedby is set conditionally: includes only present ids (hint, error, or both).


CSS custom properties

PropertyDefault (light)
--x-form-field-label-colorvar(--x-color-text-muted,#374151)
--x-form-field-label-font-sizevar(--x-font-size-sm,0.875rem)
--x-form-field-input-bgvar(--x-color-surface,#ffffff)
--x-form-field-input-colorvar(--x-color-text,#111827)
--x-form-field-input-bordervar(--x-border-width,1px) solid var(--x-color-border,#d1d5db)
--x-form-field-input-border-radiusvar(--x-radius-md,6px)
--x-form-field-input-padding0.5rem 0.75rem
--x-form-field-focus-ring-colorvar(--x-color-primary,#2563eb)
--x-form-field-error-colorvar(--x-color-danger,#dc2626)
--x-form-field-hint-colorvar(--x-color-text-muted,#6b7280)
--x-form-field-disabled-opacity0.45

Dark-mode defaults are applied automatically via @media (prefers-color-scheme: dark).


Form association

x-form-field sets formAssociated = true. It participates in <form> submit via ElementInternals.setFormValue(). The field name comes from the name attribute.

CallbackBehaviour
formDisabledCallback(d)A disabled fieldset disables the control. The control gets no disabled attribute.
formResetCallback()Clears input value, the value attribute and the error attribute, calls setFormValue("")

Validity is set via setValidity:

  • error attribute non-empty → { customError: true } with the error message
  • required attribute set + empty value → { valueMissing: true } with "Please fill in this field."
  • otherwise → {} (valid)

Validation API

Like a native form control, x-form-field exposes read-only validity, validationMessage, willValidate, form and labels, plus checkValidity() and reportValidity(). All seven delegate to the element's ElementInternals, so el.checkValidity() answers the same question a submit attempt would.


Accessibility

  • Inner <input> is labelled via aria-labelledby="label".
  • aria-describedby links to hint and/or error spans when present.
  • aria-invalid="true" is set on the input when an error is present.
  • aria-required mirrors the required attribute.
  • Error span has role="alert" and aria-live="assertive".
  • Hint span has aria-live="polite".
  • Animations respect @media (prefers-reduced-motion: reduce).

Usage examples

Basic

<x-form-field
  label="Email address"
  type="email"
  name="email"
  placeholder="you@example.com"
  hint="We will never share your email.">
</x-form-field>

With error

<x-form-field
  label="Password"
  type="password"
  name="password"
  error="Password must be at least 8 characters.">
</x-form-field>

Listening to events

const field = document.querySelector('x-form-field');

field.addEventListener('x-form-field-input', e => {
  console.log('typing:', e.detail.name, e.detail.value);
});

field.addEventListener('x-form-field-change', e => {
  console.log('committed:', e.detail.name, e.detail.value);
});

Setting value programmatically

// Via property (also syncs the shadow input)
field.value = 'hello@example.com';

// Via attribute
field.setAttribute('value', 'hello@example.com');

Setting error programmatically

// Show error
field.setAttribute('error', 'Invalid email address.');

// Clear error
field.removeAttribute('error');

Inside a form

<form id="my-form">
  <x-form-field label="Name" name="name"></x-form-field>
  <button type="submit">Submit</button>
</form>

<script>
  document.getElementById('my-form').addEventListener('submit', e => {
    e.preventDefault();
    const data = Object.fromEntries(new FormData(e.target));
    console.log(data); // { name: "…" }
  });
</script>

CSS theming

x-form-field {
  --x-form-field-input-border-radius: 0;
  --x-form-field-focus-ring-color: #7c3aed;
  --x-form-field-error-color: #b91c1c;
}

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close