FormField
A label, a hint and an error around a control that has none of its own: a native input, or a control you built. It connects them for assistive technology, so the control is named, described and marked invalid.
The preview needs JavaScript. The code is in the Code tab.
<template>
<div>
<FormField>
<span slot="label">Start date</span>
<input type="date" />
<span slot="hint">The first day of the trial.</span>
</FormField>
<FormField required error="Pick a time.">
<span slot="label">Reminder</span>
<input type="time" />
</FormField>
</div>
</template>
Installation
npx @pixelscriptio/tessera add FormFieldpnpm exec tessera add FormFieldyarn tessera add FormFieldbunx @pixelscriptio/tessera add FormField- Works with Tessera 0.9.0 or newer.
Copy and paste the code into your project
Put it in
system/components.system/components/FormField.tess <contract> { "name": "FormField", "role": "generic", "description": "A label, a hint and an error around a control that has none of its own: a native input, or a control you built. It connects them for assistive technology, so the control is named, described and marked invalid.", "since": "0.8.0", "parts": ["root", "label", "control", "hint", "error"], "slots": { "label": "the visible label, required", "default": "the control: an input, a textarea, a select or an element with a role such as radiogroup", "hint": "help under the control" }, "preview": { "slots": { "label": "Start date", "default": { "tag": "input", "attrs": { "type": "date" } }, "hint": "The first day of the trial." } } } </contract> <script> const { error, required } = props({ error: '', required: false }) // Ids of its own, so a page can have any number of fields. const uid = `t-field-${Math.random().toString(36).slice(2, 9)}` const box = state(null) const labelEl = state(null) // The control is whatever the slot holds. Name it with the label, describe it with the hint and the error, and mark it // invalid and required, as the component's own state says. effect(() => { const root = box.value const label = labelEl.value const message = error.value const must = required.value if (!root) return const control = root.querySelector('input, textarea, select, [role]') if (!control) return if (!control.id) control.id = `${uid}-control` if (label) label.setAttribute('for', control.id) control.setAttribute('aria-describedby', `${uid}-hint ${uid}-error`) if (message) control.setAttribute('aria-invalid', 'true') else control.removeAttribute('aria-invalid') if (must) control.setAttribute('aria-required', 'true') else control.removeAttribute('aria-required') }) </script> <template> <div class="root"> <label class="label" ref="labelEl"> <slot name="label" /> <span class="required" if="required" aria-hidden="true">*</span> </label> <div class="control" ref="box"><slot /></div> <span class="hint" :id="uid + '-hint'"><slot name="hint" /></span> <span class="error" :id="uid + '-error'" aria-live="polite">{error}</span> </div> </template> <style> .root { display: flex; flex-direction: column; gap: $space.1; color: $color.text.default; font-family: var(--t-font-family-sans); font-size: $font.size.sm; } .label { font-weight: $font.weight.bold; } .required { margin-left: $space.1; color: $color.action.danger.text; } .control { display: flex; flex-direction: column; } .hint { color: $color.text.muted; } .error { color: $color.action.danger.text; } .hint:empty, .error:empty { display: none; } </style>Check your design tokens
Your design system needs these.
tessera addreports the ones that are missing; by hand, runnpx @pixelscriptio/tessera check.color.action.danger.textcolor.text.defaultcolor.text.mutedfont.size.smfont.weight.boldspace.1
Usage
Using FormField
A label, a hint and an error around a control that has none of its own.
TextField, Textarea, Select, Combobox and RadioGroup already have a label, a hint and an error. Use FormField for the rest: a native <input type="date">, a range slider, or a control of your own.
- The label is required, in the
labelslot. It is connected to the control, so clicking it focuses the control and a screen reader reads it. - The control goes in the default slot.
FormFieldfinds the firstinput,textarea,selector element with arolein it. - The hint is read with the control. The
erroris announced when it appears, is read with the control, and marks it invalid. requiredshows a star and marks the control required for assistive technology. It does not stop anything being submitted: check the value yourself (see Form).
The preview needs JavaScript. The code is in the Code tab.
<template>
<div>
<FormField>
<span slot="label">Start date</span>
<input type="date" />
<span slot="hint">The first day of the trial.</span>
</FormField>
<FormField required error="Pick a time.">
<span slot="label">Reminder</span>
<input type="time" />
</FormField>
</div>
</template>
API reference
Slots
| Slot | Content |
|---|---|
label | the visible label, required |
default | the control: an input, a textarea, a select or an element with a role such as radiogroup |
hint | help under the control |
Anatomy
| Role | generic |
| Parts | root label control hint error |
| Since | 0.8.0 |
Design tokens it uses
Your design system needs these. tessera add checks that it has them, and can add the ones it lacks.
color.action.danger.text color.text.default color.text.muted font.size.sm font.weight.bold space.1
Checksum
f8e7a1909d405237448d406e1bae662d4775e1b02e6f448d036fd5d86abcf195