component

Checkbox

A checkbox component for binary choices and multiple selections. Supports indeterminate state and provides proper accessibility with label association. Supports three appearances: checkbox (default square indicator), button (Panel-powered with full emphasis and feedback), and switch (horizontal toggle with sliding knob).

Default

---
import Checkbox from "../Checkbox.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "md", direction: "column" })}>
  <Checkbox id="checkbox-unchecked">Unchecked</Checkbox>
  <Checkbox id="checkbox-checked" checked>Checked</Checkbox>
  <Checkbox id="checkbox-indeterminate" indeterminate>Indeterminate</Checkbox>
  <Checkbox id="checkbox-disabled" disabled>Disabled</Checkbox>
</div>

Size

Three sizes are available across all appearances.

checkbox appearance

button appearance

---
import Checkbox from "../Checkbox.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "xl", direction: "column" })}>
  <div>
    <h3>checkbox appearance</h3>
    <div class={stack({ gap: "sm", direction: "row", align: "center" })}>
      <Checkbox id="astro-csize-sm" size="sm">Small</Checkbox>
      <Checkbox id="astro-csize-md" size="md" checked>Medium</Checkbox>
      <Checkbox id="astro-csize-lg" size="lg">Large</Checkbox>
    </div>
  </div>

  <div>
    <h3>button appearance</h3>
    <div class={stack({ gap: "sm", direction: "row", align: "center" })}>
      <Checkbox id="astro-csbtn-sm" appearance="button" size="sm"
        >Small</Checkbox
      >
      <Checkbox id="astro-csbtn-md" appearance="button" size="md" checked
        >Medium</Checkbox
      >
      <Checkbox id="astro-csbtn-lg" appearance="button" size="lg"
        >Large</Checkbox
      >
    </div>
  </div>
</div>

Button

appearance="button" composes with Panel — use feedback (primary, success, danger, warning, neutral) to control the surface.

Primary (default)

Danger

Warning

Success

---
import Checkbox from "../Checkbox.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "xl", direction: "column" })}>
  <div>
    <h3>Primary (default)</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox id="astro-cbtn-p1" appearance="button" checked
        >Option 1</Checkbox
      >
      <Checkbox id="astro-cbtn-p2" appearance="button">Option 2</Checkbox>
      <Checkbox id="astro-cbtn-p3" appearance="button">Option 3</Checkbox>
    </div>
  </div>

  <div>
    <h3>Danger</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox id="astro-cbtn-d1" appearance="button" feedback="danger" checked
        >Option 1</Checkbox
      >
      <Checkbox id="astro-cbtn-d2" appearance="button" feedback="danger"
        >Option 2</Checkbox
      >
    </div>
  </div>

  <div>
    <h3>Warning</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox
        id="astro-cbtn-w1"
        appearance="button"
        feedback="warning"
        checked>Option 1</Checkbox
      >
      <Checkbox id="astro-cbtn-w2" appearance="button" feedback="warning"
        >Option 2</Checkbox
      >
    </div>
  </div>

  <div>
    <h3>Success</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox
        id="astro-cbtn-s1"
        appearance="button"
        feedback="success"
        checked>Option 1</Checkbox
      >
      <Checkbox id="astro-cbtn-s2" appearance="button" feedback="success"
        >Option 2</Checkbox
      >
    </div>
  </div>
</div>

Emphasis

For appearance="button", the emphasis prop controls how the unchecked and checked states relate to each other. primary (default) keeps today’s filled-accent look. secondary rides the colorPalette.surface.* ramp and swaps to the opposite ramp position when checked. tertiary stays on a neutral surface with accent-colored text and a muted border. The background prop still picks the starting position; hover and active step through the ramp from there.

The border prop owns the resting border. Secondary and tertiary default to "muted" and promote to "default" on the checked state; primary defaults to "default" everywhere (and the accent ring on checked is part of primary’s filled identity). Pass border="none" to remove the resting border entirely (great with tertiary for a borderless segmented look) or border="bold" | "accent" to intensify it.

Primary (default)

Secondary

Tertiary

Secondary + feedback

---
import Checkbox from "../Checkbox.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "xl", direction: "column" })}>
  <div>
    <h3>Primary (default)</h3>
    <div class={stack({ gap: "lg", direction: "row" })}>
      <div class={stack({ gap: "sm", direction: "row" })}>
        <Checkbox id="astro-cbtn-e-p1" appearance="button" checked
          >Option 1</Checkbox
        >
        <Checkbox id="astro-cbtn-e-p2" appearance="button">Option 2</Checkbox>
      </div>
      <div class={stack({ gap: "sm", direction: "row" })}>
        <Checkbox
          id="astro-cbtn-e-pn1"
          appearance="button"
          feedback="neutral"
          checked>Neutral 1</Checkbox
        >
        <Checkbox id="astro-cbtn-e-pn2" appearance="button" feedback="neutral"
          >Neutral 2</Checkbox
        >
      </div>
    </div>
  </div>

  <div>
    <h3>Secondary</h3>
    <div class={stack({ gap: "lg", direction: "row" })}>
      <div class={stack({ gap: "sm", direction: "row" })}>
        <Checkbox
          id="astro-cbtn-e-s1"
          appearance="button"
          emphasis="secondary"
          checked>Option 1</Checkbox
        >
        <Checkbox id="astro-cbtn-e-s2" appearance="button" emphasis="secondary"
          >Option 2</Checkbox
        >
      </div>
      <div class={stack({ gap: "sm", direction: "row" })}>
        <Checkbox
          id="astro-cbtn-e-sn1"
          appearance="button"
          emphasis="secondary"
          feedback="neutral"
          checked>Neutral 1</Checkbox
        >
        <Checkbox
          id="astro-cbtn-e-sn2"
          appearance="button"
          emphasis="secondary"
          feedback="neutral">Neutral 2</Checkbox
        >
      </div>
    </div>
  </div>

  <div>
    <h3>Tertiary</h3>
    <div class={stack({ gap: "lg", direction: "row" })}>
      <div class={stack({ gap: "sm", direction: "row" })}>
        <Checkbox
          id="astro-cbtn-e-t1"
          appearance="button"
          emphasis="tertiary"
          checked>Option 1</Checkbox
        >
        <Checkbox id="astro-cbtn-e-t2" appearance="button" emphasis="tertiary"
          >Option 2</Checkbox
        >
      </div>
      <div class={stack({ gap: "sm", direction: "row" })}>
        <Checkbox
          id="astro-cbtn-e-tn1"
          appearance="button"
          emphasis="tertiary"
          feedback="neutral"
          checked>Neutral 1</Checkbox
        >
        <Checkbox
          id="astro-cbtn-e-tn2"
          appearance="button"
          emphasis="tertiary"
          feedback="neutral">Neutral 2</Checkbox
        >
      </div>
    </div>
  </div>

  <div>
    <h3>Secondary + feedback</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox
        id="astro-cbtn-e-fbn"
        appearance="button"
        emphasis="secondary"
        feedback="neutral"
        checked>Neutral</Checkbox
      >
      <Checkbox
        id="astro-cbtn-e-fbd"
        appearance="button"
        emphasis="secondary"
        feedback="danger"
        checked>Danger</Checkbox
      >
      <Checkbox
        id="astro-cbtn-e-fbw"
        appearance="button"
        emphasis="secondary"
        feedback="warning"
        checked>Warning</Checkbox
      >
      <Checkbox
        id="astro-cbtn-e-fbs"
        appearance="button"
        emphasis="secondary"
        feedback="success"
        checked>Success</Checkbox
      >
    </div>
  </div>
</div>

Switch

appearance="switch" renders a horizontal toggle with a sliding knob — ideal for instant on/off settings. It still uses <input type="checkbox"> under the hood, so standard form semantics and checked/disabled behavior are unchanged.

Primary (default)

Success

Warning

Danger

Sizes

Disabled

---
import Checkbox from "../Checkbox.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "xl", direction: "column" })}>
  <div>
    <h3>Primary (default)</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox id="astro-csw-p1" appearance="switch" checked>Wi-Fi</Checkbox>
      <Checkbox id="astro-csw-p2" appearance="switch">Bluetooth</Checkbox>
    </div>
  </div>

  <div>
    <h3>Success</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox id="astro-csw-s1" appearance="switch" feedback="success" checked
        >Notifications</Checkbox
      >
      <Checkbox id="astro-csw-s2" appearance="switch" feedback="success"
        >Sync</Checkbox
      >
    </div>
  </div>

  <div>
    <h3>Warning</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox id="astro-csw-w1" appearance="switch" feedback="warning" checked
        >Beta features</Checkbox
      >
      <Checkbox id="astro-csw-w2" appearance="switch" feedback="warning"
        >Experimental</Checkbox
      >
    </div>
  </div>

  <div>
    <h3>Danger</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox id="astro-csw-d1" appearance="switch" feedback="danger" checked
        >Delete on save</Checkbox
      >
      <Checkbox id="astro-csw-d2" appearance="switch" feedback="danger"
        >Purge cache</Checkbox
      >
    </div>
  </div>

  <div>
    <h3>Sizes</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox id="astro-csw-sm" appearance="switch" size="sm" checked
        >Small</Checkbox
      >
      <Checkbox id="astro-csw-md" appearance="switch" size="md" checked
        >Medium</Checkbox
      >
      <Checkbox id="astro-csw-lg" appearance="switch" size="lg" checked
        >Large</Checkbox
      >
    </div>
  </div>

  <div>
    <h3>Disabled</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Checkbox id="astro-csw-dis1" appearance="switch" disabled
        >Unchecked</Checkbox
      >
      <Checkbox id="astro-csw-dis2" appearance="switch" disabled checked
        >Checked</Checkbox
      >
    </div>
  </div>
</div>

With Badge

Badges inside checkbox components scale with the component’s size via fontSize: 1rem.

checkbox appearance

button appearance

---
import Checkbox from "../Checkbox.astro";
import PindobaBadge from "@pindoba/astro-badge";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "xl", direction: "column" })}>
  <div>
    <h3>checkbox appearance</h3>
    <div class={stack({ gap: "sm", direction: "row", align: "center" })}>
      <Checkbox id="astro-badge-cb-sm" size="sm" checked>
        Small <PindobaBadge feedback="primary" size="sm">3</PindobaBadge>
      </Checkbox>
      <Checkbox id="astro-badge-cb-md" size="md" checked>
        Medium <PindobaBadge feedback="primary" size="sm">3</PindobaBadge>
      </Checkbox>
      <Checkbox id="astro-badge-cb-lg" size="lg" checked>
        Large <PindobaBadge feedback="primary" size="sm">3</PindobaBadge>
      </Checkbox>
    </div>
  </div>

  <div>
    <h3>button appearance</h3>
    <div class={stack({ gap: "sm", direction: "row", align: "center" })}>
      <Checkbox id="astro-badge-cbtn-sm" appearance="button" size="sm" checked>
        Small <PindobaBadge feedback="primary" size="sm">3</PindobaBadge>
      </Checkbox>
      <Checkbox id="astro-badge-cbtn-md" appearance="button" size="md" checked>
        Medium <PindobaBadge feedback="primary" size="sm">3</PindobaBadge>
      </Checkbox>
      <Checkbox id="astro-badge-cbtn-lg" appearance="button" size="lg" checked>
        Large <PindobaBadge feedback="primary" size="sm">3</PindobaBadge>
      </Checkbox>
    </div>
  </div>
</div>

Leading & Trailing slots

Use the leading and trailing slots to position icons, stamps, or badges at the start and end of the label. With emphasis="adaptive", badges and stamps follow the parent’s color in both unchecked and checked states. The trailing slot pins to the end of the row, which pairs well with fullWidth.

button appearance — both slots

button appearance — leading only

button appearance — trailing only

checkbox appearance

---
import Checkbox from "../Checkbox.astro";
import Badge from "@pindoba/astro-badge";
import Stamp from "@pindoba/astro-stamp";
import { Bell } from "@lucide/astro";
import { stack } from "@pindoba/styled-system/patterns";

const sizes = ["sm", "md", "lg"] as const;
---

<div class={stack({ gap: "lg", direction: "column" })}>
  <div>
    <h3>button appearance — both slots</h3>
    <div class={stack({ gap: "sm", direction: "column" })}>
      {
        sizes.map((size) => (
          <Checkbox
            id={`astro-lt-cbtn-${size}`}
            appearance="button"
            size={size}
            fullWidth
            checked={size === "md"}
          >
            <Stamp slot="leading" emphasis="adaptive" size="xs" shape="circle">
              <Bell />
            </Stamp>
            Notifications
            <Badge slot="trailing" emphasis="adaptive" size="sm">
              12
            </Badge>
          </Checkbox>
        ))
      }
    </div>
  </div>

  <div>
    <h3>button appearance — leading only</h3>
    <div class={stack({ gap: "sm", direction: "column" })}>
      {
        sizes.map((size) => (
          <Checkbox
            id={`astro-lt-cbtn-leading-${size}`}
            appearance="button"
            size={size}
            fullWidth
            checked={size === "md"}
          >
            <Stamp slot="leading" emphasis="adaptive" size="xs" shape="circle">
              <Bell />
            </Stamp>
            Notifications
          </Checkbox>
        ))
      }
    </div>
  </div>

  <div>
    <h3>button appearance — trailing only</h3>
    <div class={stack({ gap: "sm", direction: "column" })}>
      {
        sizes.map((size) => (
          <Checkbox
            id={`astro-lt-cbtn-trailing-${size}`}
            appearance="button"
            size={size}
            fullWidth
            checked={size === "md"}
          >
            Notifications
            <Badge slot="trailing" emphasis="adaptive" size="sm">
              12
            </Badge>
          </Checkbox>
        ))
      }
    </div>
  </div>

  <div>
    <h3>checkbox appearance</h3>
    <div class={stack({ gap: "sm", direction: "column" })}>
      {
        sizes.map((size) => (
          <Checkbox
            id={`astro-lt-cb-${size}`}
            size={size}
            checked={size === "md"}
          >
            Tasks
            <Badge slot="trailing" emphasis="adaptive" size="sm">
              5
            </Badge>
          </Checkbox>
        ))
      }
    </div>
  </div>
</div>

Custom Styling

The passThrough prop provides per-slot escape hatches: style accepts a Panda CSS SystemStyleObject merged with the base styles, and props forwards arbitrary HTML attributes onto the element.

Custom input style via passThrough

Custom props via passThrough

---
import Checkbox from "../Checkbox.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "xl", direction: "column" })}>
  <div>
    <h3>Custom input style via passThrough</h3>
    <Checkbox
      id="checkbox-custom-style"
      passThrough={{
        input: {
          style: { borderRadius: "full" },
        },
      }}
    >
      Rounded checkbox
    </Checkbox>
  </div>

  <div>
    <h3>Custom props via passThrough</h3>
    <Checkbox
      id="checkbox-custom-props"
      passThrough={{
        root: {
          props: { "data-testid": "my-checkbox" },
        },
        input: {
          props: { "aria-label": "Accept terms and conditions" },
        },
      }}
    >
      Accept terms
    </Checkbox>
  </div>
</div>
props · 15 shown · 15 total
appearance
"button""checkbox""switch"

Visual appearance variant of the control.

as
"div""section""article""aside""main""header""footer""nav""dialog""form""fieldset""ul""ol""li""a""button""label""span""kbd"
default "label"

Root element tag. Set `"span"` for a label-less indicator.

background
"surface.peak""surface.hill""surface.base""surface.valley""surface.ground""transparent"

Surface background from the Panel scale (`surface.peak` → `surface.ground`, or `transparent`).

border
"none""bold""default""muted""accent"

Resting border style for the `button` appearance. Secondary/tertiary default to `"muted"` and promote to `"default"` when checked; pass `"none"` to remove it or `"bold" | "accent"` to intensify it.

children slot svelte
Snippet

No description yet.

element binding svelte
HTMLElementnull

No description yet.

emphasis
"primary""secondary""tertiary"
default primary

Visual emphasis / prominence level.

feedback
"primary""neutral""success""warning""danger""inherit"
default "primary"

Color scheme applied via `colorPalette`. For the `checkbox` appearance this sets the indicator accent color; for the `button` appearance it sets the Panel feedback color.

fullWidth
boolean

Stretch the component to fill the available width.

leading slot svelte
Snippet

No description yet.

passThrough
CheckboxPassThrough< RootElementAttributes, InputElementAttributes >

Per-slot style and attribute override bag. Each slot's `style` accepts a Panda CSS `SystemStyleObject` merged with the base styles, and `props` forwards arbitrary HTML attributes onto the element.

radius
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"

Corner radius of the `appearance="button"` surface. Defaults to the size-derived radius from the control sizing scale. Pass `"inner"` to compute a concentric radius from a parent Panel's exported `--panel-out-radius`/`--panel-out-padding` (this is how `Choice` keeps its segmented items nested inside the group container).

size
"sm""md""lg"

Size variant.

trailing slot svelte
Snippet

No description yet.

translucent
boolean

Renders the surface with a translucent (frosted) background.

Plus all standard <input> HTML attributes.

Type

  • Components
  • Blocks