Radio
Material Design 3 radio button component with multiple variants, colors, and sizes
Radio
Native Contract And Imports
Individual example prerequisites (including composition and demo actions):
@import "@duskmoon-dev/core/base.css";
@import "@duskmoon-dev/core/themes/sunshine";
@import "@duskmoon-dev/core/components/radio.css";
@import "@duskmoon-dev/core/components/form-group.css";
base.css supplies global focus/utility definitions and shared spacing/shape tokens;
use your framework reset when appropriate. Substitute the Moonlight theme for dark forms.
Aggregate: @duskmoon-dev/core with Tailwind v4; no-Tailwind:
@duskmoon-dev/core/standalone.css. Individual:
@duskmoon-dev/core/components/radio.css plus base/theme and
form-group.css for associated labels/help. Extensionless components/radio
exports generated css and constructable styles, not a behavior runtime.
Use native inputs for values, selection, constraints, submission and reset.
Actual disabled or fieldset disabled owns semantic disabling.
Use .validator for native interaction-based errors and add
.validator-success for native success. Explicit aria-invalid="true"
wins over those states and color modifiers, including focus.
Supply described error text as well as color. See
Validator and
Field composition.
Give choices one shared name, distinct value attributes and a native fieldset/legend.
Native arrow keys change selection; reset restores the initially checked choice.
Radio buttons allow users to select a single option from a set of mutually exclusive choices. @duskmoon-dev/core provides a complete set of Material Design 3 radio button variants.
Basic Usage
Simply add the .radio class to an <input type="radio">:
Basic Radio
<input type="radio" name="demo" class="radio" value="option" aria-label="Basic radio option" />With Label
Wrap the radio in a label for better UX:
Radio with Label
<label class="radio-label">
<input type="radio" name="option" class="radio" />
<span>Option 1</span>
</label>Checked State
Checked Radio
<label class="radio-label">
<input type="radio" name="checked-demo" class="radio" checked />
<span>Selected option</span>
</label>Sizes
Five sizes are available:
Radio Sizes
<div class="radio-group radio-group-horizontal">
<label class="radio-label">
<input type="radio" name="size" class="radio radio-xs" checked />
<span>Extra Small</span>
</label>
<label class="radio-label">
<input type="radio" name="size" class="radio radio-sm" />
<span>Small</span>
</label>
<label class="radio-label">
<input type="radio" name="size" class="radio radio-md" />
<span>Medium</span>
</label>
<label class="radio-label">
<input type="radio" name="size" class="radio radio-lg" />
<span>Large</span>
</label>
<label class="radio-label">
<input type="radio" name="size" class="radio radio-xl" />
<span>Extra Large</span>
</label>
</div>Color Variants
Primary (Default)
Primary Radio
<label class="radio-label">
<input type="radio" name="color-primary" class="radio radio-primary" checked />
<span>Primary</span>
</label>Secondary
Secondary Radio
<label class="radio-label">
<input type="radio" name="color-secondary" class="radio radio-secondary" checked />
<span>Secondary</span>
</label>Tertiary
Tertiary Radio
<label class="radio-label">
<input type="radio" name="color-tertiary" class="radio radio-tertiary" checked />
<span>Tertiary</span>
</label>Success
Success Radio
<label class="radio-label">
<input type="radio" name="color-success" class="radio radio-success" checked />
<span>Success</span>
</label>Warning
Warning Radio
<label class="radio-label">
<input type="radio" name="color-warning" class="radio radio-warning" checked />
<span>Warning</span>
</label>Error
Error Radio
<label class="radio-label">
<input type="radio" name="color-error" class="radio radio-error" checked />
<span>Error</span>
</label>Info
Info Radio
<label class="radio-label">
<input type="radio" name="color-info" class="radio radio-info" checked />
<span>Info</span>
</label>All Colors Comparison
All Color Variants
<div class="radio-group radio-group-horizontal">
<input type="radio" name="all-colors" class="radio radio-primary" checked aria-label="Primary radio option" />
<input type="radio" name="all-colors-2" class="radio radio-secondary" checked aria-label="Secondary radio option" />
<input type="radio" name="all-colors-3" class="radio radio-tertiary" checked aria-label="Tertiary radio option" />
<input type="radio" name="all-colors-4" class="radio radio-success" checked aria-label="Success radio option" />
<input type="radio" name="all-colors-5" class="radio radio-warning" checked aria-label="Warning radio option" />
<input type="radio" name="all-colors-6" class="radio radio-error" checked aria-label="Error radio option" />
<input type="radio" name="all-colors-7" class="radio radio-info" checked aria-label="Info radio option" />
</div>States
Disabled
Disabled Radio
<div class="radio-group radio-group-horizontal">
<label class="radio-label">
<input type="radio" name="disabled" class="radio" disabled />
<span>Disabled unchecked</span>
</label>
<label class="radio-label">
<input type="radio" name="disabled-checked" class="radio" checked disabled />
<span>Disabled checked</span>
</label>
</div>Radio Groups
Vertical Group (Default)
Vertical Radio Group
<div class="radio-group">
<span class="radio-group-label">Select a plan</span>
<label class="radio-label">
<input type="radio" name="plan" class="radio" value="free" checked />
<span>Free Plan</span>
</label>
<label class="radio-label">
<input type="radio" name="plan" class="radio" value="pro" />
<span>Pro Plan</span>
</label>
<label class="radio-label">
<input type="radio" name="plan" class="radio" value="enterprise" />
<span>Enterprise Plan</span>
</label>
</div>Horizontal Group
Horizontal Radio Group
<div class="radio-group radio-group-horizontal">
<label class="radio-label">
<input type="radio" name="horizontal" class="radio" value="yes" checked />
<span>Yes</span>
</label>
<label class="radio-label">
<input type="radio" name="horizontal" class="radio" value="no" />
<span>No</span>
</label>
<label class="radio-label">
<input type="radio" name="horizontal" class="radio" value="maybe" />
<span>Maybe</span>
</label>
</div>Form Integration
Radio in Form
<form>
<div class="radio-group">
<span class="radio-group-label">Shipping Method</span>
<label class="radio-label">
<input type="radio" name="shipping" class="radio" value="standard" checked />
<span>Standard Shipping (5-7 days)</span>
</label>
<label class="radio-label">
<input type="radio" name="shipping" class="radio" value="express" />
<span>Express Shipping (2-3 days)</span>
</label>
<label class="radio-label">
<input type="radio" name="shipping" class="radio" value="overnight" />
<span>Overnight Shipping</span>
</label>
</div>
</form>Best Practices
Usage Guidelines
- Use for mutually exclusive choices: Radio buttons are ideal when users can select only one option
- Provide clear labels: Each option should have a descriptive label
- Default selection: Consider providing a sensible default selection
- Limit options: Keep the number of options manageable (typically 2-7 items)
Accessibility
- Use semantic
<label>elements to associate labels with radio buttons - Ensure all radio buttons in a group share the same
nameattribute - Support keyboard navigation (built-in with native inputs)
- Provide meaningful labels
Touch Targets
The radio provides adequate touch targets. For mobile-first designs, use larger sizes:
Mobile-Friendly Radio
<label class="radio-label">
<input type="radio" name="mobile" class="radio radio-lg" />
<span>Mobile-friendly option</span>
</label>API Reference
Class Names
| Class | Description |
|---|---|
.radio |
Base radio styling (required, on input element) |
.radio-xs |
Extra small size (14px) |
.radio-sm |
Small size (16px) |
.radio-md |
Medium size (20px, default) |
.radio-lg |
Large size (24px) |
.radio-xl |
Extra large size (28px) |
.radio-primary |
Primary color variant (default) |
.radio-secondary |
Secondary color variant |
.radio-tertiary |
Tertiary color variant |
.radio-success |
Success color variant |
.radio-warning |
Warning color variant |
.radio-error |
Error color variant |
.radio-info |
Info color variant |
.radio-label |
Label wrapper for radio + text |
.radio-group |
Container for vertical radio group |
.radio-group-horizontal |
Horizontal layout modifier |
.radio-group-label |
Group label text |
HTML Structure
Simple radio:
<input type="radio" name="group" class="radio" value="option" aria-label="Example option" />
With label:
<label class="radio-label">
<input type="radio" name="group" class="radio" />
<span>Label text</span>
</label>
Radio group:
<div class="radio-group">
<span class="radio-group-label">Group Label</span>
<label class="radio-label">
<input type="radio" name="group" class="radio" value="a" />
<span>Option A</span>
</label>
<label class="radio-label">
<input type="radio" name="group" class="radio" value="b" />
<span>Option B</span>
</label>
</div>
States
| State | How to Apply |
|---|---|
| Checked | checked attribute |
| Disabled | disabled attribute |
| Focus | Automatic via :focus-visible |
Ghost Variant
The ghost variant removes the visible border when not selected — useful for minimal UI contexts.
Ghost Radio
<label class="radio-label">
<input type="radio" name="demo-ghost-radio" class="radio radio-ghost" />
<span>Ghost (no border unselected)</span>
</label>
<label class="radio-label">
<input type="radio" name="demo-ghost-radio" class="radio radio-ghost" checked />
<span>Ghost selected (color appears)</span>
</label>