HabCheckbox
A multi-select option, or a setting that only applies once the form is saved.
<HabCheckbox v-model="releases" label="Release notices" help="Sent when a version ships." />Checkbox or switch? A switch takes effect the moment it is flipped. If the change needs a Save button, it is a checkbox — a switch that does nothing until saved is a lie about immediacy.
The whole row is a <label>, so clicking the help line toggles the option too.
Indeterminate
<HabCheckbox
:model-value="all"
:indeterminate="some"
label="All scopes"
@update:model-value="toggleAll"
/>indeterminate is a mixed state, not an off one: it paints the checked treatment with a dash. It is also a DOM property rather than an attribute, which is what makes a native checkbox announce as "mixed" — Habitus sets it for you.
One box, or one of a group
v-model takes either shape.
A boolean is one independent box — a single setting.
<HabCheckbox v-model="notify" label="Email me about failures" />An array is one box in a group, and needs value. The box is checked when the array contains its value, and toggling adds or removes it:
<HabCheckbox v-for="scope in scopes" :key="scope" v-model="chosen" :value="scope" :label="scope" />This is the shape Vue's own v-model gives a native checkbox group, so a group needs no wrapper component and no handler per box. The component warns if it is given an array with no value, because every box then shares one identity and checking any of them checks all of them.
The emitted array is always a new array. The model belongs to the consumer, and mutating it in place would not trip a readonly or a store's write guard until much later. value also becomes the input's value attribute, so a native form submits it.
A write in flight
<HabCheckbox :model-value="attached" :loading="saving" label="Flag exclusion" />loading swaps the mark for a spinner inside the same 20px box, so a list of them does not shift by a few pixels every time one is ticked. That reflow is the reason call sites were hand-rolling a fixed-size span around a spinner of their own.
Neither the checked fill nor the disabled one is painted under it: both would assert an answer the write has not come back with.
It is the same word HabButton and HabCard use, for the same thing. It implies non-interactive, the way HabButton's does and for the same reason — the state is not readable while the spinner is there, so a second click would be aimed at something invisible. The control reports aria-busy, and the label keeps its normal ink with a wait cursor: this is mid-answer, not unavailable.
Read-only
readonly shows the state to be read: checked, unchecked or mixed in full contrast, the label in full ink. Clicking the box or its label and pressing Space change nothing, and update:modelValue is never emitted — in the group form too. The box stays focusable and is announced as read-only, unlike disabled, which says "not now" and is dimmed. See HabReadonly for the difference, and for making a whole region read-only at once.
<HabCheckbox v-model="notify" label="Email me about failures" readonly />disabled outranks it and keeps its own look. With loading the spinner is drawn as usual: a write already in flight is still reported.
Accessibility
- A real
<input type="checkbox">stays in the DOM, only visually hidden. Remove it and you lose the name/value pair on submit, autofill, native validation, and anything for a screen reader to report a state on. - Because that input is 0×0, the native focus indicator has nothing to draw on, so
base.cssputs the outline on the mark beside it. That is why the mark must remain the input's immediately following sibling, and why.hab-toggleis a load-bearing class. If you import/themedirectly, import/basetoo. - The label names the control and the help describes it, wired with
aria-labelledbyandaria-describedby. Without that split the row's whole text — help included — would become the control's name. - Give it a
label, or anaria-labelif the name is somewhere else on screen. Habitus warns when it has neither. name,requiredand every other attribute you pass reach the input, not the wrapper.classandstylestay on the wrapper.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | — |
help | string | undefined | Quiet second line — what the option actually does. |
indeterminate | boolean | false | Mixed state, for a parent of partly-checked children. Renders the checked treatment with a dash, and reports mixed to assistive technology. |
label | string | undefined | The option's name. |
loading | boolean | false | A write is in flight: the mark becomes a spinner inside the same 20px box. Named loading to match HabButton and HabCard, which mean the same thing by it. One concept, one word — a library whose components each coin their own is the incoherence this one exists to prevent. Same box, so the row does not reflow the moment it is ticked — a checkbox list that jumps by a few pixels on every click is the reason consumers were hand-rolling a fixed-size span around a spinner. It implies non-interactive, the way HabButton's does, and for the same reason: the state is not readable while the spinner is there, so a second click would be aimed at something invisible. The control reports aria-busy, and the label keeps its normal ink — this is mid-answer, not unavailable. |
modelValue | boolean | false | For v-model, in either of two forms. A boolean is one independent box — a single setting. An array is one box in a group, and requires value: the box is checked when the array contains its value, and toggling adds or removes it. This is the shape Vue's own v-model gives a native checkbox group, so a group does not need a wrapper component or a handler per box. |
readonly | boolean | undefined | The state is shown to be read, not changed: checked, unchecked or mixed in full contrast and the label in full ink. Clicking the box or its label and pressing Space change nothing, and update:modelValue is never emitted. Still focusable, and announced as read-only — unlike disabled, which says "not now", is dimmed, and outranks it. Unset, it follows a surrounding HabReadonly; false keeps this box editable inside one. |
value | string | number | undefined | This box's value in the group form. Required whenever modelValue is an array — without it every box in the group shares the same identity. Also becomes the input's value attribute, so a native form submits it. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | checked: boolean | (string | number)[] | — |