Westy Design Systemv0.2.7
OverviewComponentsExamplesStorybook

Foundations

  • Typography
  • Colors
  • Spacing
  • Radius
  • Shadows
  • Icons

Components

  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Data Table
  • Date Picker
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Menubar
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Questionnaire
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toggle
  • Toggle Group
  • Tooltip

Chat

  • Attachment
  • Bubble
  • Marker
  • Message
  • Message Scroller

Components

Checkbox

A control that allows the user to toggle between checked and not checked.

Playground

—label
—description
—checked
—disabled
—aria-invalid
Usage
Import Checkbox from the design system and pair it with Field and FieldLabel for proper layout and labeling. The hit area extends invisibly past the 16px box, so the target stays comfortable on touch screens.
import { Checkbox } from "@workspace/ui/components/checkbox"
import { Field, FieldLabel } from "@workspace/ui/components/field"

<Field orientation="horizontal">
  <Checkbox id="terms" />
  <FieldLabel htmlFor="terms">Accept terms and conditions</FieldLabel>
</Field>
Checked State
Use defaultChecked for uncontrolled checkboxes, or checked and onCheckedChange to control the state. The callback receives boolean | "indeterminate", so narrow it before storing a boolean.
const [checked, setChecked] = React.useState(false)

<Field orientation="horizontal">
  <Checkbox
    id="newsletter"
    checked={checked}
    onCheckedChange={(value) => setChecked(value === true)}
  />
  <FieldLabel htmlFor="newsletter">
    {checked ? "Subscribed" : "Subscribe to the newsletter"}
  </FieldLabel>
</Field>
Description
Wrap the label in FieldContent and add a FieldDescription for helper text — the checkbox stays aligned with the first line of the label.

By clicking this checkbox, you agree to the terms and conditions.

<Field orientation="horizontal">
  <Checkbox id="terms-2" defaultChecked />
  <FieldContent>
    <FieldLabel htmlFor="terms-2">Accept terms and conditions</FieldLabel>
    <FieldDescription>
      By clicking this checkbox, you agree to the terms and conditions.
    </FieldDescription>
  </FieldContent>
</Field>
Invalid
Set aria-invalid on the checkbox and data-invalid on the field wrapper to show the invalid styles — a destructive border and ring on the box, and destructive text on the label.
<Field orientation="horizontal" data-invalid>
  <Checkbox id="terms-invalid" aria-invalid />
  <FieldLabel htmlFor="terms-invalid">
    Accept terms and conditions
  </FieldLabel>
</Field>
Disabled
Use the disabled prop to prevent interaction and add data-disabled to the Field so the label dims with the box. The checked state is preserved, so a disabled checkbox can stay checked.
<Field orientation="horizontal" data-disabled>
  <Checkbox id="notifications" disabled />
  <FieldLabel htmlFor="notifications">Enable notifications</FieldLabel>
</Field>
<Field orientation="horizontal" data-disabled>
  <Checkbox id="marketing" disabled defaultChecked />
  <FieldLabel htmlFor="marketing">Marketing emails</FieldLabel>
</Field>
Group
Use multiple fields inside a FieldSet with a FieldLegend to create a checkbox list. Set variant="label" on the legend so it renders at label size.
Show these items on the desktop:

Select the items you want to show on the desktop.

<FieldSet>
  <FieldLegend variant="label">Show these items on the desktop:</FieldLegend>
  <FieldDescription>
    Select the items you want to show on the desktop.
  </FieldDescription>
  <FieldGroup className="gap-3">
    <Field orientation="horizontal">
      <Checkbox id="hard-disks" defaultChecked />
      <FieldLabel htmlFor="hard-disks" className="font-normal">
        Hard disks
      </FieldLabel>
    </Field>
    <Field orientation="horizontal">
      <Checkbox id="external-disks" defaultChecked />
      <FieldLabel htmlFor="external-disks" className="font-normal">
        External disks
      </FieldLabel>
    </Field>
    <Field orientation="horizontal">
      <Checkbox id="cds-dvds" />
      <FieldLabel htmlFor="cds-dvds" className="font-normal">
        CDs, DVDs, and iPods
      </FieldLabel>
    </Field>
    <Field orientation="horizontal">
      <Checkbox id="connected-servers" />
      <FieldLabel htmlFor="connected-servers" className="font-normal">
        Connected servers
      </FieldLabel>
    </Field>
  </FieldGroup>
</FieldSet>
Table
Checkboxes drive row selection in tables — a header checkbox selects all rows, and each row sets data-state="selected" on its TableRow to pick up the selected background.
NameEmailRole
Sarah Chensarah.chen@example.comAdmin
Marcus Rodriguezmarcus.rodriguez@example.comUser
Priya Patelpriya.patel@example.comUser
David Kimdavid.kim@example.comEditor
const [selected, setSelected] = React.useState<Set<string>>(new Set(["1"]))

<Table>
  <TableHeader>
    <TableRow>
      <TableHead className="w-8">
        <Checkbox
          aria-label="Select all rows"
          checked={selected.size === rows.length}
          onCheckedChange={(checked) => handleSelectAll(checked === true)}
        />
      </TableHead>
      <TableHead>Name</TableHead>
      ...
    </TableRow>
  </TableHeader>
  <TableBody>
    {rows.map((row) => (
      <TableRow
        key={row.id}
        data-state={selected.has(row.id) ? "selected" : undefined}
      >
        <TableCell>
          <Checkbox
            aria-label={`Select ${row.name}`}
            checked={selected.has(row.id)}
            onCheckedChange={(checked) =>
              handleSelectRow(row.id, checked === true)
            }
          />
        </TableCell>
        ...
      </TableRow>
    ))}
  </TableBody>
</Table>
RTL
Checkboxes need no extra classes in right-to-left layouts — Field lays the box and label out with logical properties, so the checkbox moves to the right edge and the text aligns accordingly.

بالنقر على هذا المربع، فإنك توافق على الشروط.

<FieldGroup dir="rtl" className="gap-3">
  <Field orientation="horizontal">
    <Checkbox id="terms-rtl" defaultChecked />
    <FieldContent>
      <FieldLabel htmlFor="terms-rtl">قبول الشروط والأحكام</FieldLabel>
      <FieldDescription>
        بالنقر على هذا المربع، فإنك توافق على الشروط.
      </FieldDescription>
    </FieldContent>
  </Field>
  <Field orientation="horizontal">
    <Checkbox id="notifications-rtl" />
    <FieldLabel htmlFor="notifications-rtl">تفعيل الإشعارات</FieldLabel>
  </Field>
</FieldGroup>
API Reference
Checkbox wraps the Radix Checkbox primitive and accepts all of its props — uncontrolled via defaultChecked or controlled via checked + onCheckedChange. Inside a form it submits value under name like a native checkbox.
PropTypeDefault
checkedboolean | "indeterminate"—
defaultCheckedboolean | "indeterminate"false
onCheckedChange(checked: boolean | "indeterminate") => void—
disabledboolean—
requiredbooleanfalse
namestring—
valuestring"on"

Tokens used

6 design tokens consumed by the Checkbox component.

  • --primary

    Primary

    data-checked:border-primarydata-checked:bg-primarydark:data-checked:bg-primaryaria-invalid:aria-checked:border-primary
  • --primary-foreground

    Primary Foreground

    data-checked:text-primary-foreground
  • --input

    Input

    border-inputdark:bg-input/30
  • --ring

    Ring

    focus-visible:border-ringfocus-visible:ring-ring/50
  • --destructive

    Destructive

    aria-invalid:border-destructivearia-invalid:ring-destructive/20dark:aria-invalid:border-destructive/50dark:aria-invalid:ring-destructive/40
  • --spacing

    Spacing unit (4px)

    size-4[&>svg]:size-3.5after:-inset-x-3