GrCheckbox
Lets you toggle independent options and select multiple items.
Overview
When to take it
- consent or a flag in a form — “remember me”, “I agree to the terms”: the value leaves with the submission;
- selecting rows of a list — a checkbox in the header with
indeterminateshows a partial selection; - the label is interactive — it lives outside the role, so a link inside it stays a link;
- a native form is needed — the hidden
<input type="checkbox">leaves withnameandvalue.
When to take something else
| Need | Take |
|---|---|
| The setting applies immediately | GrSwitch |
| There are several related options | GrCheckboxGroup |
| Only one option can be selected | GrRadio |
| Display modes are switched | GrSegmented |
The border with GrSwitch runs along the moment of application: a checkbox is a
value of a form and leaves with the save; a switch changes the state of the system
at once, and there must be no “Save” button next to it.
Playground controls come from the API, not from hand
Loading…
0 changed· state is in the URL
<GrCheckbox v-model="value">Email me when an invoice is paid</GrCheckbox>Examples 4 each with a live demo and its code
Consent that blocks the submit
requiredis the rule andinvalidits verdict — two separate props. Submit without ticking and the page sets the verdict; the note under the box turns into the error in the same line, so the form does not jump. The link inside the label stays a link: clicking it opens the terms instead of ticking the box. Inside aGrFormthe same rule is avalidatorwithvalue === true, because a cleared checkbox is a legitimatefalse.Options that explain themselves
The label is the checkbox’s own content, so a name with a hint under it is one hit area. A disabled option keeps its text readable — states are drawn with tokens, not opacity — because the text is what says why the option is not there.
Export SeptemberOne ZIP with a CSV per section Invoices418 documents with line itemsPaymentsMatched and unmatchedCredit notes12 documentsAttachmentsAvailable on the Business planSelect all, some or none
The header box is computed from the rows: ticked when all are, empty when none are, and
indeterminatein between — it is never stored, so it never lies. Rows have no visible label, so each box gets anaria-labelnaming its invoice. The action bar is always there, so selecting does not push the list down.2 of 4 overdue invoices selectedInvoiceCustomerDueAmountINV-0412Contoso LtdSep 30€432.00INV-0415Northwind TradersOct 2€1,284.50INV-0417Tailspin ToysOct 3€2,150.00INV-0419Fabrikam LogisticsOct 5€96.00A matrix of events and channels
Each cell is a value of the profile, saved together with it — that is why these are checkboxes and not switches. A column header ticks the channel for every event and shows
indeterminatefor a partial column;label-position="start"reads it as a header. Security notices arereadonly: the state is visible, but it cannot be changed.NotificationsSaved with the profile · the column header switches a channel for every event Event EmailPushSlackInvoice paid Invoice overdue Payment disputed Weekly summary Security notices · required
API generated from the package · cannot drift
Props
| Prop | Type | default | Description |
|---|---|---|---|
disabled | boolean | undefined | undefined | Unavailable: it does not toggle and does not take focus. Combined by "or" with the field and the group. |
modelValuev-model | boolean | undefined | undefined | Unset inside a `GrCheckboxGroup` — the state comes from the group. |
readonly | boolean | undefined | false | Read-only: the state is visible but is not toggled. |
invalid | boolean | undefined | false | The visual and ARIA state of an error. |
required | boolean | undefined | false | A required field: it is announced as `aria-required`, there is no native check. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | The size of the control. Unset — it comes from the group, then from `GrConfigProvider`, otherwise `md`. |
ariaLabel | string | undefined | undefined | The name of the control when there is no label in the slot (or it is purely visual). |
name | string | undefined | undefined | The name of the hidden `<input>` in `FormData`. Inside `GrCheckboxGroup` it defaults to the name of the group. |
value | string | undefined | "on" | The value for the form and the key in the array of selected values of `GrCheckboxGroup`. `on` by default. |
id | string | undefined | undefined | Passed onto the hidden native `<input>`, so that `<label for="...">` works. |
tabindex | number | undefined | undefined | A `Tab` stop on the toggle itself. Needed by composite widgets that take the tab order for themselves: in a `GrDataTable` grid (`cell-navigation`) `Tab` belongs to the table as a whole, and a checkbox in a cell must not keep a stop of its own — it is reached with an arrow to the cell and `Enter`. A disabled toggle stays out of the traversal in any case. |
form | string | undefined | undefined | The id of the form for the hidden `<input>` when the checkbox stands outside its markup. |
indeterminate | boolean | undefined | false | The intermediate ("mixed") state: `aria-checked="mixed"`, and a dash as the indicator. |
labelPosition | "end" | "start" | undefined | "end" | The side the label is on relative to the control. |
Slots
| Slot | Type | Description |
|---|---|---|
default | any | The label of the checkbox. It may contain links: the role stays on the control itself. |
Events
| Event | Signature | Description |
|---|---|---|
update:modelValue | (event: "update:modelValue", value: boolean): void | — |
change | (event: "change", value: boolean): void | — |
focus | (event: "focus", event: FocusEvent): void | — |
blur | (event: "blur", event: FocusEvent): void | — |
Methods
| Methods | Signature | Description |
|---|---|---|
focus | () => void | Put focus on the control — the same one that `Tab` reaches. |
blur | () => void | Remove focus from the control. |
Types
Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.
import type { GrCheckboxProps, GrCheckboxEmits, GrCheckboxConfigurableProps } from '@feugene/granularity/components/GrCheckbox'Filedist/types/components/GrCheckbox/GrCheckbox.vue.d.ts
interface GrCheckboxProps {
tabindex?: number;
modelValue?: boolean;
disabled?: boolean;
name?: string;
value?: string;
required?: boolean;
readonly?: boolean;
invalid?: boolean;
form?: string;
id?: string;
indeterminate?: boolean;
ariaLabel?: string;
labelPosition?: GrCheckboxLabelPosition;
size?: GrCheckboxSize;
}interface GrCheckboxEmits {
(e: 'update:modelValue', value: boolean): void;
(e: 'change', value: boolean): void;
(e: 'focus', event: FocusEvent): void;
(e: 'blur', event: FocusEvent): void;
}Filedist/types/components/GrCheckbox/config.d.ts
const grCheckboxConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrCheckbox">;Filedist/types/components/GrCheckbox/defaults.d.ts
interface GrCheckboxConfigurableProps {
size: GrCheckboxSize;
}Filedist/types/components/GrCheckbox/grCheckboxStyles.d.ts
type GrCheckboxSize = GrButtonSize;type GrCheckboxLabelPosition = typeof GR_CHECKBOX_LABEL_POSITIONS[number];Filedist/types/components/GrCheckbox/grCheckboxGroupContext.d.ts
type GrCheckboxGroupContext = {
modelValue: ComputedRef<string[]>;
name: ComputedRef<string | undefined>;
disabled: ComputedRef<boolean>;
readonly: ComputedRef<boolean>;
invalid: ComputedRef<boolean>;
required: ComputedRef<boolean>;
size: ComputedRef<GrCheckboxSize>;
toggle: (value: string, checked: boolean) => void;Filedist/types/components/GrCheckbox/safelist.d.ts
const grCheckboxSafelist: string[];Filedist/types/components/GrCheckbox/index.d.ts
type GrCheckboxInstance = ComponentExposed<typeof GrCheckboxComponent>;Accessibility
APG pattern
checkbox
Keys
Space
Full contract of the package: docs/keyboard.md ↗
Usage from the library prose
required is declared but not checked by the browser
required reaches aria-required on the span[role="checkbox"] and is not set
on the hidden input. A native check would demand that the browser focus an invalid
control, and that control is invisible and aria-hidden: in such a case Chrome
cancels the submission of the whole form and writes “An invalid form control … is
not focusable” into the console — that is, required would not prevent submission,
it would break submission silently.
A form rule checks whether the field is required:
<script setup lang="ts">
// `required` counts `null`/`''`/`[]` as empty, but not `false`: an unchecked
// checkbox is a legitimate value of the field. "Consent is required" is a
// `validator`.
const rules: GrFormRules = {
terms: [{ validator: value => value === true || 'Accept the terms' }],
}
</script>
<template>
<GrForm :model="model" :rules="rules">
<GrFormField name="terms" label="Terms">
<GrCheckbox v-model="model.terms" required />
</GrFormField>
</GrForm>
</template>The label lives outside the widget role
role="checkbox" declares its descendants presentational, so there is neither a
native input nor slot content inside it: the name of the widget arrives through
aria-labelledby. That is exactly why the label is moved outside — it may contain a
link to a policy or a “show changes” button.
The consequence: a click on the label toggles the checkbox, and a click on its
interactive content (a, button, input, select, textarea, a nested label,
[role="button"], [role="link"]) does not — such an element addresses itself.
labelPosition="start" puts the label before the control.
States
disabled and invalid are shown with background and border tokens rather than
with transparency: opacity dilutes text tokens tuned to AA. readonly leaves the
value in the form and returns the native input to the model if it was toggled by a
click on an external <label for>.
Inside GrFormField the control gets an id on the widget, aria-describedby,
aria-invalid and aria-required from the context of the field.
Props in this example