Radio

A radio button component for single selection from a group. Supports four appearances: radio (default circular indicator), button (Panel-powered with full emphasis and feedback), tab-horizontal (bottom-border accent), and tab-vertical (left-border accent).

Default

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

<div class={stack({ gap: "md", direction: "column" })}>
  <Radio id="astro-def-1" name="astro-def" value="option1">Option 1</Radio>
  <Radio id="astro-def-2" name="astro-def" value="option2" checked
    >Option 2</Radio
  >
  <Radio id="astro-def-3" name="astro-def" value="option3" disabled
    >Disabled</Radio
  >
</div>

Appearance

All four appearance variants side by side.

radio (default)

button

tab-horizontal

tab-vertical

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

<div class={stack({ gap: "xl", direction: "column" })}>
  <div>
    <h3>radio (default)</h3>
    <div class={stack({ gap: "md", direction: "column" })}>
      <Radio id="astro-app-r1" name="astro-app-radio" value="r1" checked
        >Option 1</Radio
      >
      <Radio id="astro-app-r2" name="astro-app-radio" value="r2">Option 2</Radio
      >
    </div>
  </div>

  <div>
    <h3>button</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Radio
        id="astro-app-b1"
        name="astro-app-button"
        value="b1"
        appearance="button"
        checked
      >
        Option 1
      </Radio>
      <Radio
        id="astro-app-b2"
        name="astro-app-button"
        value="b2"
        appearance="button"
      >
        Option 2
      </Radio>
      <Radio
        id="astro-app-b3"
        name="astro-app-button"
        value="b3"
        appearance="button"
      >
        Option 3
      </Radio>
    </div>
  </div>

  <div>
    <h3>tab-horizontal</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Radio
        id="astro-app-th1"
        name="astro-app-tabh"
        value="th1"
        appearance="tab-horizontal"
        checked
      >
        Tab 1
      </Radio>
      <Radio
        id="astro-app-th2"
        name="astro-app-tabh"
        value="th2"
        appearance="tab-horizontal"
      >
        Tab 2
      </Radio>
      <Radio
        id="astro-app-th3"
        name="astro-app-tabh"
        value="th3"
        appearance="tab-horizontal"
      >
        Tab 3
      </Radio>
    </div>
  </div>

  <div>
    <h3>tab-vertical</h3>
    <div class={stack({ gap: "xs", direction: "column" })}>
      <Radio
        id="astro-app-tv1"
        name="astro-app-tabv"
        value="tv1"
        appearance="tab-vertical"
        checked
      >
        Dashboard
      </Radio>
      <Radio
        id="astro-app-tv2"
        name="astro-app-tabv"
        value="tv2"
        appearance="tab-vertical"
      >
        Settings
      </Radio>
    </div>
  </div>
</div>

Size

Three sizes are available across all appearances.

radio appearance

button appearance

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

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

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

Button

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

Primary (default)

Danger

Warning

Success

---
import Radio from "../Radio.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" })}>
      <Radio
        id="astro-btn-p1"
        name="astro-btn-primary"
        value="p1"
        appearance="button"
        checked
      >
        Option 1
      </Radio>
      <Radio
        id="astro-btn-p2"
        name="astro-btn-primary"
        value="p2"
        appearance="button"
      >
        Option 2
      </Radio>
      <Radio
        id="astro-btn-p3"
        name="astro-btn-primary"
        value="p3"
        appearance="button"
      >
        Option 3
      </Radio>
    </div>
  </div>

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

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

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

Tab

tab-horizontal and tab-vertical render a minimal accent-border indicator. Use feedback to change the accent color.

Tab Horizontal

Tab Vertical

Tab Horizontal — Danger feedback

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

<div class={stack({ gap: "xl", direction: "column" })}>
  <div>
    <h3>Tab Horizontal</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Radio
        id="astro-tab-h1"
        name="astro-tab-horiz"
        value="h1"
        appearance="tab-horizontal"
        checked
      >
        All
      </Radio>
      <Radio
        id="astro-tab-h2"
        name="astro-tab-horiz"
        value="h2"
        appearance="tab-horizontal"
      >
        Active
      </Radio>
      <Radio
        id="astro-tab-h3"
        name="astro-tab-horiz"
        value="h3"
        appearance="tab-horizontal"
      >
        Archived
      </Radio>
    </div>
  </div>

  <div>
    <h3>Tab Vertical</h3>
    <div class={stack({ gap: "xs", direction: "column" })}>
      <Radio
        id="astro-tab-v1"
        name="astro-tab-vert"
        value="v1"
        appearance="tab-vertical"
        checked
      >
        Dashboard
      </Radio>
      <Radio
        id="astro-tab-v2"
        name="astro-tab-vert"
        value="v2"
        appearance="tab-vertical"
      >
        Settings
      </Radio>
      <Radio
        id="astro-tab-v3"
        name="astro-tab-vert"
        value="v3"
        appearance="tab-vertical"
      >
        Profile
      </Radio>
    </div>
  </div>

  <div>
    <h3>Tab Horizontal — Danger feedback</h3>
    <div class={stack({ gap: "sm", direction: "row" })}>
      <Radio
        id="astro-tab-hd1"
        name="astro-tab-danger"
        value="hd1"
        appearance="tab-horizontal"
        feedback="danger"
        checked
      >
        Errors
      </Radio>
      <Radio
        id="astro-tab-hd2"
        name="astro-tab-danger"
        value="hd2"
        appearance="tab-horizontal"
        feedback="danger"
      >
        Warnings
      </Radio>
    </div>
  </div>
</div>

With Badge

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

radio appearance

button appearance

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

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

  <div>
    <h3>button appearance</h3>
    <div class={stack({ gap: "sm", direction: "row", align: "center" })}>
      <Radio
        id="astro-badge-btn-sm"
        name="astro-badge-btn"
        value="sm"
        appearance="button"
        size="sm"
        checked
      >
        Small <Badge feedback="primary" size="sm">3</Badge>
      </Radio>
      <Radio
        id="astro-badge-btn-md"
        name="astro-badge-btn"
        value="md"
        appearance="button"
        size="md"
      >
        Medium <Badge feedback="primary" size="sm">3</Badge>
      </Radio>
      <Radio
        id="astro-badge-btn-lg"
        name="astro-badge-btn"
        value="lg"
        appearance="button"
        size="lg"
      >
        Large <Badge feedback="primary" size="sm">3</Badge>
      </Radio>
    </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 root style via passThrough

Custom input style via passThrough

Custom props via passThrough

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

<div class={stack({ gap: "xl", direction: "column" })}>
  <div>
    <h3>Custom root style via passThrough</h3>
    <div class={stack({ gap: "md", direction: "column" })}>
      <Radio
        id="astro-cust-1"
        name="astro-cust"
        value="c1"
        passThrough={{
          root: {
            style: { gap: "lg", padding: "sm" },
          },
        }}
      >
        Extra padding
      </Radio>
    </div>
  </div>

  <div>
    <h3>Custom input style via passThrough</h3>
    <div class={stack({ gap: "md", direction: "column" })}>
      <Radio
        id="astro-cust-2"
        name="astro-cust"
        value="c2"
        passThrough={{
          input: {
            style: { width: "2em", height: "2em" },
          },
        }}
      >
        Larger indicator
      </Radio>
    </div>
  </div>

  <div>
    <h3>Custom props via passThrough</h3>
    <div class={stack({ gap: "md", direction: "column" })}>
      <Radio
        id="astro-cust-3"
        name="astro-cust"
        value="c3"
        passThrough={{
          root: {
            props: { "data-testid": "my-radio" },
          },
          input: {
            props: { "aria-label": "Custom radio button" },
          },
        }}
      >
        With data attributes
      </Radio>
    </div>
  </div>
</div>

Props

Prop
appearance
appearance

Visual style of the radio. radio: Default circular indicator. button: Styled as a button using Panel composition. tab-horizontal: Bottom-border accent tab. tab-vertical: Left-border accent tab.

Type "radio" | "button" | "tab-horizontal" | "tab-vertical"
Default "radio"
Required No
emphasis
emphasis

Emphasis level for button appearance. primary: Solid accent background. secondary: Surface background with border. ghost: Transparent background.

Type "primary" | "secondary" | "ghost"
Default "primary"
Required No
feedback
feedback

Color scheme applied via colorPalette. For radio and tab appearances this sets the indicator accent color. For button appearance this sets the Panel feedback color.

Type "primary" | "neutral" | "success" | "danger" | "warning"
Default "primary"
Required No
size
size

Size of the component.

Type "sm" | "md" | "lg"
Default "md"
Required No
fullWidth
fullWidth

Expand the radio to fill its container width.

Type boolean
Default false
Required No
id
id

Unique identifier for the radio input, used for label association.

Type string
Default -
Required Yes
passThrough
passThrough

Per-slot style and attribute overrides.

Type { root?: { style?: SystemStyleObject; props?: HTMLLabelAttributes }; input?: { style?: SystemStyleObject; props?: HTMLInputAttributes }; text?: { style?: SystemStyleObject; props?: HTMLAttributes } }
Default undefined
Required No
children
children

Label content rendered next to the radio indicator.

Type Snippet
Default undefined
Required No
...rest
...rest

Standard HTML input attributes (excluding type and size).

Type Omit<HTMLInputAttributes, 'type' | 'size'>
Default -
Required No