Phi
Radio
@dicehub/phiv1.0.0-beta.1

Radio

A control that allows the user to select one option from a set. Always used within a Radio.Group.

<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("email");
</script>

<template>
  <Radio.Group
    v-model="value"
    legend="Notification preference"
  >
    <Radio.Item label="Email" value="email" />
    <Radio.Item label="SMS" value="sms" />
    <Radio.Item label="Push notification" value="push" />
  </Radio.Group>
</template>

Installation

Barrel

import { Radio } from "@dicehub/phi";

Granular

import { Radio } from "@dicehub/phi/components/radio";

Usage

<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("a");
</script>

<template>
  <Radio.Group v-model="value" legend="Choose an option">
    <Radio.Item label="Option A" value="a" />
    <Radio.Item label="Option B" value="b" />
  </Radio.Group>
</template>

Examples

Default (Vertical)

Radio groups display vertically by default. Each radio has a label displayed to its right.

<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("personal");
</script>

<template>
  <Radio.Group v-model="value" legend="Account type">
    <Radio.Item label="Personal" value="personal" />
    <Radio.Item label="Business" value="business" />
    <Radio.Item label="Enterprise" value="enterprise" />
  </Radio.Group>
</template>

Horizontal

Use orientation="horizontal" for inline layouts. Items wrap when there is not enough space.

<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("md");
</script>

<template>
  <Radio.Group
    v-model="value"
    legend="Size"
    orientation="horizontal"
  >
    <Radio.Item label="Small" value="sm" />
    <Radio.Item label="Medium" value="md" />
    <Radio.Item label="Large" value="lg" />
  </Radio.Group>
</template>

With Description

Add helper text below the radio items using the description prop.

<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("standard");
</script>

<template>
  <Radio.Group
    v-model="value"
    legend="Shipping method"
    description="Choose how you'd like to receive your order"
  >
    <Radio.Item label="Standard (5-7 days)" value="standard" />
    <Radio.Item label="Express (2-3 days)" value="express" />
    <Radio.Item label="Overnight" value="overnight" />
  </Radio.Group>
</template>

Control Position

Use controlPosition="end" to place labels before radio buttons.

<script setup>
import { Radio } from "@dicehub/phi/components/radio";
</script>

<template>
  <Radio.Group
    legend="Preferences"
    control-position="end"
    default-value="a"
  >
    <Radio.Item label="Label before radio" value="a" />
    <Radio.Item label="Another option" value="b" />
  </Radio.Group>
</template>

Radio Card

Use appearance="card" to join options inside one card with dividers. Each item can include a description. Selected rows keep their tint when hovered.

<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("free");
</script>

<template>
  <Radio.Group v-model="value" legend="Choose a plan" appearance="card">
    <Radio.Item
      label="Free"
      description="For personal or hobby projects that aren't business-critical."
      value="free"
    />
    <Radio.Item
      label="Pro"
      description="For professional websites that aren't business-critical."
      value="pro"
    />
    <Radio.Item
      label="Business"
      description="For small businesses operating online."
      value="business"
    />
    <Radio.Item
      label="Contract"
      description="For mission-critical applications that are core to your business."
      value="contract"
    />
  </Radio.Group>
</template>

Radio Card (Control on the Left)

Use controlPosition="start" on a card radio group to place the radio control on the left of the label and description.

<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("free");
</script>

<template>
  <Radio.Group
    v-model="value"
    legend="Choose a plan"
    appearance="card"
    control-position="start"
  >
    <Radio.Item
      label="Free"
      description="For personal or hobby projects that aren't business-critical."
      value="free"
    />
    <Radio.Item
      label="Pro"
      description="For professional websites that aren't business-critical."
      value="pro"
    />
  </Radio.Group>
</template>

Rich Label Content

The label slot on Radio.Item accepts rich content, so you can embed icons, badges, or other markup alongside the text.

<script setup>
import { ref } from "vue";
import { Badge } from "@dicehub/phi/components/badge";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("pro");
</script>

<template>
  <Radio.Group v-model="value" legend="Choose a plan" appearance="card">
    <Radio.Item
      description="For personal or hobby projects."
      value="free"
    >
      <template #label>
        <span class="radio-rich-label">Free <Badge variant="neutral">$0</Badge></span>
      </template>
    </Radio.Item>
    <Radio.Item
      description="For professional websites."
      value="pro"
    >
      <template #label>
        <span class="radio-rich-label">Pro <Badge variant="primary">Popular</Badge></span>
      </template>
    </Radio.Item>
  </Radio.Group>
</template>

Radio Card (Horizontal)

Combine appearance="card" with orientation="horizontal" for a joined two-column card with row and column dividers. Below 641px, the options form one column.

<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("free");
</script>

<template>
  <Radio.Group
    v-model="value"
    legend="Choose a plan"
    appearance="card"
    orientation="horizontal"
  >
    <Radio.Item
      label="Free"
      description="For personal or hobby projects that aren't business-critical."
      value="free"
    />
    <Radio.Item
      label="Pro"
      description="For professional websites that aren't business-critical."
      value="pro"
    />
    <Radio.Item
      label="Business"
      description="For small businesses operating online."
      value="business"
    />
    <Radio.Item
      label="Contract"
      description="For mission-critical applications that are core to your business."
      value="contract"
    />
  </Radio.Group>
</template>

With Error

Show validation errors at the group level using the error prop.

<script setup>
import { Radio } from "@dicehub/phi/components/radio";
</script>

<template>
  <div class="radio-demo-grid">
    <Radio.Group
      legend="Payment method"
      error="Please select a payment method to continue"
    >
      <Radio.Item label="Credit Card" value="card" variant="error" />
      <Radio.Item label="PayPal" value="paypal" variant="error" />
    </Radio.Group>

    <Radio.Group
      legend="Payment method"
      appearance="card"
      error="Please select a payment method to continue"
    >
      <Radio.Item
        label="Credit Card"
        description="Pay with Visa, Mastercard, American Express, or Elo."
        value="card"
        variant="error"
      />
      <Radio.Item
        label="PayPal"
        description="Pay with your PayPal account."
        value="paypal"
        variant="error"
      />
    </Radio.Group>
  </div>
</template>

Disabled

Use the disabled prop to disable the entire group or individual items.

<script setup>
import { Radio } from "@dicehub/phi/components/radio";
</script>

<template>
  <div class="radio-demo-grid">
    <Radio.Group legend="Disabled group" disabled default-value="a">
      <Radio.Item label="Option A" value="a" />
      <Radio.Item label="Option B" value="b" />
    </Radio.Group>

    <Radio.Group legend="Individual disabled" default-value="available">
      <Radio.Item label="Available" value="available" />
      <Radio.Item label="Unavailable" value="unavailable" disabled />
    </Radio.Group>

    <Radio.Group
      legend="Disabled card group"
      appearance="card"
      disabled
      default-value="a"
    >
      <Radio.Item
        label="Option A"
        description="This option is disabled."
        value="a"
      />
      <Radio.Item
        label="Option B"
        description="This option is disabled."
        value="b"
      />
    </Radio.Group>

    <Radio.Group
      legend="Individual disabled card"
      appearance="card"
      default-value="available"
    >
      <Radio.Item
        label="Available"
        description="This option can be selected."
        value="available"
      />
      <Radio.Item
        label="Unavailable"
        description="This option is not available."
        value="unavailable"
        disabled
      />
    </Radio.Group>
  </div>
</template>

Visually Hidden Legend

Use Radio.Legend with phi-sr-only to keep context available to screen readers.

<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("all");
</script>

<template>
  <Radio.Group v-model="value" appearance="card" orientation="horizontal">
    <Radio.Legend class="phi-sr-only">Paths</Radio.Legend>
    <Radio.Item label="Allow all paths" value="all" />
    <Radio.Item label="Restrict to specific paths" value="specific" />
  </Radio.Group>
</template>

Custom Legend Styling

Use Radio.Legend when you need custom typography, colors, or layout.

<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const value = ref("email");
</script>

<template>
  <Radio.Group v-model="value">
    <Radio.Legend style="font-size: 0.8125rem; font-weight: 400; color: var(--phi-subtle);">
      Notification preference
    </Radio.Legend>
    <Radio.Item label="Email" value="email" />
    <Radio.Item label="SMS" value="sms" />
    <Radio.Item label="Push notification" value="push" />
  </Radio.Group>

  <Radio.Group v-model="value" appearance="card" orientation="horizontal">
    <Radio.Legend style="font-size: 0.8125rem; font-weight: 400; color: var(--phi-subtle);">
      Notification preference
    </Radio.Legend>
    <Radio.Item label="Email" value="email" />
    <Radio.Item label="SMS" value="sms" appearance="default" />
    <Radio.Item label="Push notification" value="push" />
  </Radio.Group>
</template>

Typed Values

Radio.Group preserves string, number, and boolean values passed through v-model.

<script setup lang="ts">
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";

const pageSize = ref(10);
const theme = ref("system");
</script>

<template>
  <div class="radio-demo-grid">
    <Radio.Group v-model="pageSize" legend="Items per page">
      <Radio.Item label="10" :value="10" />
      <Radio.Item label="25" :value="25" />
      <Radio.Item label="50" :value="50" />
    </Radio.Group>

    <Radio.Group v-model="theme" legend="Theme">
      <Radio.Item label="Light" value="light" />
      <Radio.Item label="Dark" value="dark" />
      <Radio.Item label="System" value="system" />
    </Radio.Group>
  </div>
</template>

API Reference

Radio.Group

Container for radio buttons with legend, description, and error support.

Prop / EventTypeDefaultDescription
legendstring-Legend text for the group. For custom styling, omit this prop and pass Radio.Legend as a direct child. Legends render above the items; this prop takes precedence.
default slotunknown-Child Radio.Item components and optionally a Radio.Legend.
orientation"vertical" | "horizontal""vertical"Layout direction. Horizontal card groups use two columns, or one column below 641px.
appearance"default" | "card""default"Card groups share one outline with internal dividers. Individual items can override this with appearance; default items retain padding inside a card group, and a card item in a default group keeps its own border.
descriptionstring-Helper text displayed below the group.
errorstring-Validation message displayed below the group.
defaultValueRadioValue-Initial selected value for uncontrolled usage.
modelValueRadioValue-Controlled selected value for v-model.
valueRadioValue-Controlled selected value alias for modelValue.
disabledboolean-Disables every item in the group.
controlPosition"start" | "end"-start places the radio before the label; end places the label before the radio. Defaults to start for default appearance and end for card appearance.
namestring-Native radio group name for form submission.
classstring-Additional CSS classes applied to the fieldset.
@update:model-value(value: RadioValue) => void-v-model update event.
@value-change(value: RadioValue, details: RadioValueChangeDetails) => void-Emitted when the selected value changes. The second argument carries the native event.

Radio.Legend

Composable legend sub-component for Radio.Group.

Prop / EventTypeDefaultDescription
default slotunknown-Legend content.
classstring-Additional CSS classes, for example phi-sr-only to visually hide the legend.

Radio.Item

Individual radio button within Radio.Group.

Prop / EventTypeDefaultDescription
variant"default" | "error""default"Visual variant for validation states.
appearance"default" | "card"-Overrides the group-level appearance.
labelstring-Visible label rendered next to the radio item.
label slotunknown-Rich Vue content for the item label.
descriptionstring-Description text displayed below the label in card appearance.
description slotunknown-Rich description content displayed in card appearance.
valueRadioValue-Item value used by Radio.Group.
classstring-Additional CSS classes applied to the label wrapper.
disabledboolean-Disables this radio item.
@value-change(value: RadioValue, details: RadioValueChangeDetails) => void-Emitted when this item is selected.

Accessibility

Semantic HTML

Radio.Group uses semantic fieldset and legend elements for proper grouping and screen reader announcement.

Keyboard Navigation

Arrow keys move between options. Space selects the focused option. Tab moves focus to and from the radio group.

Screen Readers

Each radio is announced with its label and selection state. The group legend provides context for all options.