GrCheckbox

  • core
  • forms
  • API · 14 props
  • ⌨ keys

Lets you toggle independent options and select multiple items.

import { GrCheckbox } from '@feugene/granularity/components/GrCheckbox'

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 indeterminate shows 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 with name and value.

When to take something else

NeedTake
The setting applies immediatelyGrSwitch
There are several related optionsGrCheckboxGroup
Only one option can be selectedGrRadio
Display modes are switchedGrSegmented

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

Stage

Loading…

0 changed· state is in the URL

Examples 4 each with a live demo and its code

  • 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 items
    PaymentsMatched and unmatched
    Credit notes12 documents
    AttachmentsAvailable on the Business plan
  • Select all, some or none

    The header box is computed from the rows: ticked when all are, empty when none are, and indeterminate in between — it is never stored, so it never lies. Rows have no visible label, so each box gets an aria-label naming its invoice. The action bar is always there, so selecting does not push the list down.

    2 of 4 overdue invoices selected
    InvoiceCustomerDueAmount
    INV-0412Contoso LtdSep 30€432.00
    INV-0415Northwind TradersOct 2€1,284.50
    INV-0417Tailspin ToysOct 3€2,150.00
    INV-0419Fabrikam LogisticsOct 5€96.00
  • A 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 indeterminate for a partial column; label-position="start" reads it as a header. Security notices are readonly: the state is visible, but it cannot be changed.

    NotificationsSaved with the profile · the column header switches a channel for every event
    Event
    Email
    Push
    Slack
    Invoice paid
    Invoice overdue
    Payment disputed
    Weekly summary
    Security notices · required

API generated from the package · cannot drift

Props

PropTypedefaultDescription
disabledboolean | undefinedundefinedUnavailable: it does not toggle and does not take focus. Combined by "or" with the field and the group.
modelValuev-modelboolean | undefinedundefinedUnset inside a `GrCheckboxGroup` — the state comes from the group.
readonlyboolean | undefinedfalseRead-only: the state is visible but is not toggled.
invalidboolean | undefinedfalseThe visual and ARIA state of an error.
requiredboolean | undefinedfalseA required field: it is announced as `aria-required`, there is no native check.
size"xs" | "sm" | "md" | "lg" | undefinedundefinedThe size of the control. Unset — it comes from the group, then from `GrConfigProvider`, otherwise `md`.
ariaLabelstring | undefinedundefinedThe name of the control when there is no label in the slot (or it is purely visual).
namestring | undefinedundefinedThe name of the hidden `<input>` in `FormData`. Inside `GrCheckboxGroup` it defaults to the name of the group.
valuestring | undefined"on"The value for the form and the key in the array of selected values of `GrCheckboxGroup`. `on` by default.
idstring | undefinedundefinedPassed onto the hidden native `<input>`, so that `<label for="...">` works.
tabindexnumber | undefinedundefinedA `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.
formstring | undefinedundefinedThe id of the form for the hidden `<input>` when the checkbox stands outside its markup.
indeterminateboolean | undefinedfalseThe 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

SlotTypeDescription
defaultanyThe label of the checkbox. It may contain links: the role stays on the control itself.

Events

EventSignatureDescription
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

MethodsSignatureDescription
focus() => voidPut focus on the control — the same one that `Tab` reaches.
blur() => voidRemove 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
import type { GrCheckboxProps, GrCheckboxEmits, GrCheckboxConfigurableProps } from '@feugene/granularity/components/GrCheckbox'

Filedist/types/components/GrCheckbox/GrCheckbox.vue.d.ts

GrCheckboxPropsinterface
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;
}
GrCheckboxEmitsinterface
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

grCheckboxConfigconst
const grCheckboxConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrCheckbox">;

Filedist/types/components/GrCheckbox/defaults.d.ts

GrCheckboxConfigurablePropsinterface
interface GrCheckboxConfigurableProps {
    size: GrCheckboxSize;
}

Filedist/types/components/GrCheckbox/grCheckboxStyles.d.ts

GrCheckboxSizetype
type GrCheckboxSize = GrButtonSize;
GrCheckboxLabelPositiontype
type GrCheckboxLabelPosition = typeof GR_CHECKBOX_LABEL_POSITIONS[number];

Filedist/types/components/GrCheckbox/grCheckboxGroupContext.d.ts

GrCheckboxGroupContexttype
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

grCheckboxSafelistconst
const grCheckboxSafelist: string[];

Filedist/types/components/GrCheckbox/index.d.ts

GrCheckboxInstancetype
type GrCheckboxInstance = ComponentExposed<typeof GrCheckboxComponent>;

Accessibility

APG pattern

checkbox

Keys

Space

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:

vue
<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.