Checkbox
The custom element is <hui-checkbox>.
Overview
A form-associated checkbox in the nova treatment. An unchecked box submits nothing at all, exactly like a native checkbox, and indeterminate draws a mixed state until the next toggle.
Example
Source
<hui-checkbox name="agree" value="yes" aria-label="I agree"></hui-checkbox>The Go template that renders it:
<hui-checkbox name="agree" value="yes" {{ if .Agreed }}checked{{ end }} aria-label="I agree"></hui-checkbox>API
Attributes6
| Name | Type | Default | Description |
|---|---|---|---|
name | string | '' | The form field name. With no name the control contributes no entry to FormData. |
disabled | boolean | false | Disables the internal button and clears the form value. |
checked | boolean | false | The checked state. Reflected, so it can be styled and read from the attribute. |
indeterminate | boolean | false | Draws the mixed glyph and reports aria-checked="mixed". Cleared on the next toggle. Reflected. |
value | string | 'on' | The value submitted while checked. Reflected. |
required | boolean | false | Reflected; marks the checkbox required for constraint validation. |
Properties6
| Name | Type | Default | Description |
|---|---|---|---|
name | string | '' | Reflects to the name attribute. |
disabled | boolean | false | Reflects to the disabled attribute. |
checked | boolean | false | Reflects to the checked attribute. |
indeterminate | boolean | false | Reflects to the indeterminate attribute. |
value | string | 'on' | Reflects to the value attribute. |
required | boolean | false | Reflects to the required attribute. |
Events3
| Name | Description |
|---|---|
input | Emitted from the host after the checked flag flips. bubbles and composed; no detail. |
change | Emitted from the host in the same turn as input. bubbles and composed; no detail. |
click | The internal button click is consumed with stopPropagation(), so a native click does not cross the shadow boundary. Bind to change instead. |
CSS custom properties5
| Name | Default | Description |
|---|---|---|
--hui-primary | oklch(0.496 0.265 301.924) | Fill while checked or indeterminate. |
--hui-background | oklch(1 0 0) | Unchecked box fill. |
--hui-input | oklch(0.62 0.019 323.02) | Unchecked box border. |
--hui-destructive | oklch(0.56 0.245 27.325) | Border while aria-invalid="true". |
--hui-focus-ring-width | 3px | Width of the keyboard focus ring. |
::part() hooks1
| Name | Description |
|---|---|
control | The internal native <button> that carries role="checkbox". |
States
Checked and indeterminate
The two states a bare checkbox cannot both express: checked, and indeterminate for a parent whose children are partly selected, which is announced as mixed.
Source
<hui-checkbox name="checked" checked value="yes" aria-label="Checked"></hui-checkbox><hui-checkbox name="mixed" indeterminate aria-label="Mixed"></hui-checkbox>Disabled
Disabled in both states. The control keeps its name for a screen reader but cannot be reached or changed.
Source
<hui-checkbox name="off" disabled aria-label="Disabled"></hui-checkbox><hui-checkbox name="on" checked disabled aria-label="Checked and disabled"></hui-checkbox>Server-side mechanics
The server owns name, value, checked, indeterminate, required and disabled. A checked box submits its value under name; an unchecked or unnamed box submits nothing, exactly like a native checkbox. A swap must re-send checked, and indeterminate if the mixed state is still wanted, because those attributes are the only state the server can restore, and swapping mid-toggle discards the flip. Validation comes from required, which blocks submission, and from aria-invalid="true" or a hui-field invalid wrapper for the border.
Accessibility
- The internal button exposes
role="checkbox"witharia-checkedoftrue,falseormixed. - The accessible name comes from an
aria-label; wrapping the host in a<label>does not name the shadow button. - Indeterminate is visual and announced; the next activation clears it and sets a definite checked state.
- Because
requiredand validity live on the host through ElementInternals, a required unchecked box blocks submission like the native control.
Keyboard
| Keys | Action |
|---|---|
Tab | Moves focus to and from the checkbox; it is a single tab stop. |
Space, Enter | Toggles the checked state. The internal native button supplies both keys. |
Gotchas
- The internal click is stopped at the shadow boundary, so a native
clicklistener on the host never fires; bind toinputorchangeinstead.