Aksara UI

Checkbox

Checkbox and radio controls for picking options, with a linked label and description.

import { Checkbox, Radio, RadioGroup } from '@aksornproject/aksara-ui'

Label and description

label renders a real <label> linked to the control, so clicking the text toggles it. description renders under the label and is linked with aria-describedby. A long label wraps and the control stays on its first line.

<div className="flex flex-col gap-spacing-12">
  <Checkbox
    label="Email me about new lessons"
    description="A weekly digest, every Monday."
    defaultChecked
  />
  <Checkbox label="Send reminders before class" />
</div>

Indeterminate

indeterminate draws a dash for a parent whose children are partly selected. The library does not wrap Base UI CheckboxGroup, so you derive checked and indeterminate from your own state. onCheckedChange receives the next checked boolean first.

<div className="flex flex-col gap-spacing-12">
  <Checkbox
    label="All subjects"
    checked={all}
    indeterminate={some}
    onCheckedChange={(checked) => setSelected(checked ? SUBJECTS : [])}
  />
  <div className="flex flex-col gap-spacing-12 ps-spacing-32">
    {SUBJECTS.map((subject) => (
      <Checkbox
        key={subject}
        label={subject}
        checked={selected.includes(subject)}
        onCheckedChange={(checked) => toggle(subject, checked)}
      />
    ))}
  </div>
</div>

Sizes

medium is a 20px control and the default. large is 24px. The label and description are 16px at both sizes.

<Checkbox label="Medium" defaultChecked />
<Checkbox size="large" label="Large" defaultChecked />

States

disabled greys the control and its text and sets a not-allowed cursor. readOnly keeps the default look without hover and blocks changes. loading turns the control, label and description into skeleton blocks, sets aria-busy and blocks changes.

<div className="flex flex-col gap-spacing-12">
  <Checkbox label="Disabled" disabled />
  <Checkbox label="Disabled and checked" disabled defaultChecked />
  <Checkbox label="Read-only" readOnly defaultChecked />
  <Checkbox label="Loading" description="Fetching your settings" loading />
</div>

Required

required marks the hidden input required and draws a red star after the label. Without a label there is no star. A Checkbox submits with its form through a hidden native input, which carries name, value and required.

<Checkbox
  required
  name="terms"
  label="I accept the terms of use"
  description="You can read them on the Help page."
/>

Without a label

Without label or description the Checkbox renders only the control and its hidden input. Name it with aria-label, or point an external <label htmlFor> at its id. className lands on the control in both cases.

<Checkbox aria-label="Select row" />
<span className="flex items-center gap-spacing-8">
  <Checkbox id="select-all-students" />
  <label htmlFor="select-all-students">Select all students</label>
</span>

Radio

mode="radio" renders a radio with the same sizes, states, label and description. A radio needs a value and a surrounding RadioGroup, which owns the value. TypeScript rejects checked, defaultChecked, onCheckedChange and indeterminate on a radio. RadioGroup adds no layout, so pass the classes you need. A disabled or read-only RadioGroup styles its items.

<RadioGroup
  defaultValue="monthly"
  aria-label="Billing"
  className="flex flex-col gap-spacing-12"
>
  <Checkbox mode="radio" value="monthly" label="Monthly" />
  <Checkbox
    mode="radio"
    value="yearly"
    label="Yearly"
    description="Two months free"
  />
  <Checkbox
    mode="radio"
    value="school"
    label="School licence"
    description="Contact sales"
    disabled
  />
</RadioGroup>

Radio namespace

Radio groups the radio parts under one name. Radio.Group is RadioGroup, and Radio.Item is a Checkbox with mode="radio". The two forms render the same thing, so pick one and use it across the app.

<Radio.Group
  defaultValue="th"
  aria-label="Language"
  className="flex flex-col gap-spacing-12"
>
  <Radio.Item value="th" label="ภาษาไทย" />
  <Radio.Item value="en" label="English" />
</Radio.Group>

Accessibility

  • Give every control a name with label, an external <label htmlFor> or aria-label.
  • An indeterminate checkbox announces aria-checked="mixed".
  • The required star is aria-hidden. The control announces aria-required.
  • loading sets aria-busy and blocks changes.
  • Arrow keys move the selection inside a RadioGroup.
  • A decorative control inside a clickable card can take tabIndex={-1}, aria-hidden and pointer-events-none. The card then owns the role and the keyboard.

API

Checkbox

Prop

Type

RadioGroup

Radio.Group takes the same props.

Prop

Type

Radio.Item

Prop

Type

Edit on GitHub

On this page