Skip to main content

Theme config

Theme mode

Color palette

Grays

Border radii/radiuses/radiopedes/you know
Border radius
Field border radius
Button border radius
All
Components
Guides
API
Recent

Components

Radio

Use field groups to group related radio buttons. See also: Form documentation.

Full support Supported since v105. Full support Supported since v121. Full support Supported since v15.4.

The name prop will get passed down to each radio button in the group.

Legend
---
import { Radio } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet>
<FieldLegend>Legend</FieldLegend>
<FieldGroup name="fieldset-1-astro">
<Radio checked>Radio 1</Radio>
<Radio>Radio 2</Radio>
<Radio>Radio 3</Radio>
</FieldGroup>
</FieldSet>
</Form>
<form class="form">
<fieldset class="fieldset">
<legend>Legend</legend>
<div class="field-group" role="group">
<label class="radio">
<input type="radio" checked name="fieldset-1-astro" /><span
class="label"
>Radio 1</span
>
</label>
<label class="radio">
<input type="radio" name="fieldset-1-astro" /><span class="label"
>Radio 2</span
>
</label>
<label class="radio">
<input type="radio" name="fieldset-1-astro" /><span class="label"
>Radio 3</span
>
</label>
</div>
</fieldset>
</form>

Direction

Legend
---
import { Radio } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet>
<FieldLegend>Legend</FieldLegend>
<FieldGroup direction="row" name="fieldset-direction-astro">
<Radio checked>Radio 1</Radio>
<Radio>Radio 2</Radio>
<Radio>Radio 3</Radio>
</FieldGroup>
</FieldSet>
</Form>
<form class="form">
<fieldset class="fieldset">
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="radio">
<input type="radio" checked name="fieldset-direction-astro" /><span
class="label"
>Radio 1</span
>
</label>
<label class="radio">
<input type="radio" name="fieldset-direction-astro" /><span
class="label"
>Radio 2</span
>
</label>
<label class="radio">
<input type="radio" name="fieldset-direction-astro" /><span
class="label"
>Radio 3</span
>
</label>
</div>
</fieldset>
</form>

Field description

Can be placed above and below the fields.

Legend

Field description above fields

Legend

Field description below fields

---
import { Radio } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldDescription } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet>
<FieldLegend>Legend</FieldLegend>
<FieldDescription>Field description above fields</FieldDescription>
<FieldGroup direction="row" name="fieldset-field-description-1-astro">
<Radio checked>Radio 1</Radio>
<Radio>Radio 2</Radio>
<Radio>Radio 3</Radio>
</FieldGroup>
</FieldSet>
<FieldSet>
<FieldLegend>Legend</FieldLegend>
<FieldGroup direction="row" name="fieldset-field-description-2-astro">
<Radio checked>Radio 1</Radio>
<Radio>Radio 2</Radio>
<Radio>Radio 3</Radio>
</FieldGroup>
<FieldDescription>Field description below fields</FieldDescription>
</FieldSet>
</Form>
<form class="form">
<fieldset class="fieldset">
<legend>Legend</legend>
<p class="field-description">Field description above fields</p>
<div class="field-group row" role="group">
<label class="radio">
<input
type="radio"
checked
name="fieldset-field-description-1-astro"
/><span class="label">Radio 1</span>
</label>
<label class="radio">
<input type="radio" name="fieldset-field-description-1-astro" /><span
class="label"
>Radio 2</span
>
</label>
<label class="radio">
<input type="radio" name="fieldset-field-description-1-astro" /><span
class="label"
>Radio 3</span
>
</label>
</div>
</fieldset>
<fieldset class="fieldset">
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="radio">
<input
type="radio"
checked
name="fieldset-field-description-2-astro"
/><span class="label">Radio 1</span>
</label>
<label class="radio">
<input type="radio" name="fieldset-field-description-2-astro" /><span
class="label"
>Radio 2</span
>
</label>
<label class="radio">
<input type="radio" name="fieldset-field-description-2-astro" /><span
class="label"
>Radio 3</span
>
</label>
</div>
<p class="field-description">Field description below fields</p>
</fieldset>
</form>

Disabled

Attach the disabled attribute to the <fieldset> element.

Legend
---
import { Radio } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet disabled>
<FieldLegend>Legend</FieldLegend>
<FieldGroup direction="row" name="fieldset-disabled-1-astro">
<Radio checked>Radio 1</Radio>
<Radio>Radio 2</Radio>
<Radio>Radio 3</Radio>
</FieldGroup>
</FieldSet>
</Form>
<form class="form">
<fieldset disabled class="fieldset">
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="radio">
<input type="radio" checked name="fieldset-disabled-1-astro" /><span
class="label"
>Radio 1</span
>
</label>
<label class="radio">
<input type="radio" name="fieldset-disabled-1-astro" /><span
class="label"
>Radio 2</span
>
</label>
<label class="radio">
<input type="radio" name="fieldset-disabled-1-astro" /><span
class="label"
>Radio 3</span
>
</label>
</div>
</fieldset>
</form>

Required

Attach the required attribute to at least one of your <input> elements.

These are required!
---
import { Radio } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet>
<FieldLegend>These are required!</FieldLegend>
<FieldGroup direction="row" name="fieldset-required-1-astro">
<Radio required>Radio 1</Radio>
<Radio required>Radio 2</Radio>
<Radio required>Radio 3</Radio>
</FieldGroup>
</FieldSet>
</Form>
<form class="form">
<fieldset class="fieldset">
<legend>These are required!</legend>
<div class="field-group row" role="group">
<label class="radio">
<input type="radio" name="fieldset-required-1-astro" required /><span
class="label"
>Radio 1</span
>
</label>
<label class="radio">
<input type="radio" name="fieldset-required-1-astro" required /><span
class="label"
>Radio 2</span
>
</label>
<label class="radio">
<input type="radio" name="fieldset-required-1-astro" required /><span
class="label"
>Radio 3</span
>
</label>
</div>
</fieldset>
</form>

Validation

Attach the data-invalid attribute to your Fieldset component.

Legend
Something went wrong!
---
import { Radio } from "@opui/astro"
import { FieldSet } from "@opui/astro"
import { FieldLegend } from "@opui/astro"
import { FieldGroup } from "@opui/astro"
import { Form } from "@opui/astro"
---
<Form>
<FieldSet data-invalid>
<FieldLegend>Legend</FieldLegend>
<FieldGroup direction="row" name="field-group-validation-1-astro">
<Radio checked>Radio 1</Radio>
<Radio>Radio 2</Radio>
<Radio>Radio 3</Radio>
</FieldGroup>
<span class="end-text">Something went wrong!</span>
</FieldSet>
</Form>
<form class="form">
<fieldset data-invalid="true" class="fieldset">
<legend>Legend</legend>
<div class="field-group row" role="group">
<label class="radio">
<input
type="radio"
checked
name="field-group-validation-1-astro"
/><span class="label">Radio 1</span>
</label>
<label class="radio">
<input type="radio" name="field-group-validation-1-astro" /><span
class="label"
>Radio 2</span
>
</label>
<label class="radio">
<input type="radio" name="field-group-validation-1-astro" /><span
class="label"
>Radio 3</span
>
</label>
</div>
<span class="end-text">Something went wrong!</span>
</fieldset>
</form>

API

Prop Type Default Description
critical boolean false Field group error state.
legend string - The text for the legend element.
direction "row" - The orientation of the field group.

Browser support

Full support Supported since v105. Full support Supported since v121. Full support Supported since v15.4.

See also the full browser support guide.

Installation

See also

@layer components.root {
label.radio {
--_input-size: 1.125rem;
--_indicator-size: 50%;
align-items: center;
color: var(--text-primary);
cursor: pointer;
display: inline-grid;
gap: 0 var(--size-2);
grid-auto-columns: auto;
grid-auto-flow: column;
inline-size: fit-content;
line-height: 1.5;
transform: translateZ(0);
user-select: none;
/* Disabled */
&:has([disabled]) {
cursor: not-allowed;
opacity: 0.64;
user-select: none;
input {
cursor: not-allowed;
}
}
/* Required dot */
&:has(:invalid) {
.label:after {
color: var(--red);
content: "*";
inset: 0 -0.25ex auto auto;
position: absolute;
}
}
/* Label */
.label {
color: var(--text-primary);
font-size: var(--font-size-05);
grid-column: 2;
grid-row: 1;
padding-inline: 0 1ex;
position: relative;
}
/* End text */
:where(.end-text) {
color: var(--text-muted);
font-size: var(--font-size-0);
grid-column: 2;
grid-row: 2;
line-height: 1.5;
z-index: 1;
}
/* Stacked layout */
&.stack {
grid-auto-columns: unset;
justify-items: center;
.label {
grid-column: 1/-1;
grid-row: 2;
margin-block-start: var(--size-1);
padding-inline: 1ex;
/* Required dot */
&::after {
inset: 0 -0.25ex auto auto;
}
}
.end-text {
grid-column: 1/-1;
grid-row: 3;
}
}
/* Input */
input[type="radio"] {
appearance: none;
aspect-ratio: 1;
background-color: var(--surface-default);
block-size: var(--_input-size);
border: 1px solid var(--field-border-color);
border-radius: var(--radius-round);
box-sizing: border-box;
cursor: pointer;
display: grid;
inline-size: var(--_input-size);
margin: 0;
padding: 0;
place-items: center;
position: relative;
&:checked {
background-color: var(--primary);
border-color: var(--primary);
}
/* Dot */
&::after {
background-color: var(--primary-contrast);
block-size: var(--_indicator-size);
border-radius: var(--radius-round);
content: "";
inline-size: var(--_indicator-size);
margin: auto;
opacity: 0;
}
&:checked::after {
opacity: 1;
}
&::before {
--highlight-size: 175%;
}
}
/* Sizes */
&.small {
input[type="radio"] {
--_input-size: var(--size-3);
}
}
&.large {
input[type="radio"] {
--_input-size: var(--size-4);
}
}
/* Validation */
&[data-invalid],
&:has(:user-invalid) {
--palette-source: oklch(0.58 0.21 var(--hue-red));
:where(.end-text) {
color: var(--primary);
}
}
/* Touch devices */
@media (pointer: coarse) {
input[type="radio"] {
block-size: var(--size-4);
inline-size: var(--size-4);
}
}
@media (forced-colors: active) {
input[type="radio"] {
border: 1px solid CanvasText;
&:checked {
background-color: SelectedItem;
border-color: SelectedItem;
&::after {
background-color: SelectedItemText;
}
}
}
}
}
}
@layer components.extended {
/* Form */
:where(.form) {
display: grid;
gap: var(--size-8);
hr {
margin-block: 0;
}
}
/* Fieldset */
:where(.fieldset) {
all: unset;
border: 0;
border-radius: 0;
display: grid;
gap: var(--size-1);
margin: 0;
min-inline-size: 0;
padding: 0;
/* Legend */
:where(legend, .legend) {
all: unset;
color: var(--text-primary);
font-weight: 600;
margin-block-end: var(--size-3);
padding: 0;
&:has(+ :where(.field-description)) {
margin-block-end: 0;
}
}
/* Field description / supporting text */
:where(.field-description) {
color: var(--text-muted);
font-size: var(--font-size-05);
line-height: var(--font-lineheight-3);
&:has(+ *) {
margin-block-end: var(--size-3);
}
}
/* End text */
:where(.end-text) {
color: var(--text-muted);
font-size: var(--font-size-0);
line-height: var(--font-lineheight-3);
}
&:has(.text-field.row) {
row-gap: var(--size-7);
}
/* Disabled */
&[disabled] {
opacity: 0.64;
user-select: none;
input,
label,
.label {
cursor: not-allowed;
}
}
/* Error / validation */
&[data-invalid] {
:where(.end-text) {
color: var(--primary);
}
:where(.checkbox, .radio, .switch) {
--palette-source: oklch(0.58 0.21 var(--hue-red));
:where(.end-text) {
color: var(--primary);
}
}
:where(.switch) {
input {
border-radius: var(--radius-round);
outline: 2px solid var(--primary);
}
}
}
/* Required */
&:has(:invalid) {
:where(legend, .legend) {
padding-inline-end: 1ex;
position: relative;
&::after {
color: var(--red);
content: "*";
inset: 0 -0.25ex auto auto;
position: absolute;
}
}
}
}
/* Field group */
:where(.field-group) {
display: flex;
flex-direction: column;
gap: var(--size-4);
/* If it only has checkboxes, radios, or switches */
&:has(> :where(.checkbox, .radio, .switch)):not(:has(> :not(.checkbox, .radio, .switch))) {
gap: var(--size-2);
}
/* If it only has buttons */
&:has(.button):not(:has(*:not(.button))) {
align-items: center;
flex-direction: row;
gap: var(--size-2);
/* If it's not preceeded by an hr */
&:not(hr + &) {
margin-block-start: var(--size-4);
}
}
&+& {
margin-block-start: var(--size-5);
}
/* Directions */
&.row {
flex-direction: row;
flex-wrap: wrap;
&:has(> :where(.checkbox, .radio, .switch)) {
gap: var(--size-4);
}
}
}
}