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

    See also: Form documentation.

    Full supportSupported since v105.Full supportSupported since v121.Full supportSupported since v15.4.

    Anatomy

    • <Radio>

      Container element.

    • <input>

      The radio input.

    • slot="default"

      The label.

    • slot="end-text"

      Supporting text displayed below the label.

    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="radio-group">
    <Radio value="1" checked>Radio 1</Radio>
    <Radio value="2">Radio 2</Radio>
    <Radio value="3">Radio 3</Radio>
    </FieldGroup>
    </FieldSet>
    </Form>
    <form class="ui-form">
    <fieldset class="ui-fieldset">
    <legend>Legend</legend>
    <div class="ui-field-group" role="group">
    <label class="ui-radio"
    ><input name="radio-group" type="radio" value="1" checked /><span
    class="ui-label"
    >Radio 1</span
    ></label
    ><label class="ui-radio"
    ><input name="radio-group" type="radio" value="2" /><span
    class="ui-label"
    >Radio 2</span
    ></label
    ><label class="ui-radio"
    ><input name="radio-group" type="radio" value="3" /><span
    class="ui-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="radio-group-direction">
    <Radio value="1" checked>Radio 1</Radio>
    <Radio value="2">Radio 2</Radio>
    <Radio value="3">Radio 3</Radio>
    </FieldGroup>
    </FieldSet>
    </Form>
    <form class="ui-form">
    <fieldset class="ui-fieldset">
    <legend>Legend</legend>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-radio"
    ><input
    name="radio-group-direction"
    type="radio"
    value="1"
    checked
    /><span class="ui-label">Radio 1</span></label
    ><label class="ui-radio"
    ><input name="radio-group-direction" type="radio" value="2" /><span
    class="ui-label"
    >Radio 2</span
    ></label
    ><label class="ui-radio"
    ><input name="radio-group-direction" type="radio" value="3" /><span
    class="ui-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="radio-group-field-description-1">
    <Radio value="1" checked>Radio 1</Radio>
    <Radio value="2">Radio 2</Radio>
    <Radio value="3">Radio 3</Radio>
    </FieldGroup>
    </FieldSet>
    <FieldSet>
    <FieldLegend>Legend</FieldLegend>
    <FieldGroup direction="row" name="radio-group-field-description-2">
    <Radio value="1" checked>Radio 1</Radio>
    <Radio value="2">Radio 2</Radio>
    <Radio value="3">Radio 3</Radio>
    </FieldGroup>
    <FieldDescription>Field description below fields</FieldDescription>
    </FieldSet>
    </Form>
    <form class="ui-form">
    <fieldset class="ui-fieldset">
    <legend>Legend</legend>
    <p class="ui-field-description">Field description above fields</p>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-radio"
    ><input
    name="radio-group-field-description-1"
    type="radio"
    value="1"
    checked
    /><span class="ui-label">Radio 1</span></label
    ><label class="ui-radio"
    ><input
    name="radio-group-field-description-1"
    type="radio"
    value="2"
    /><span class="ui-label">Radio 2</span></label
    ><label class="ui-radio"
    ><input
    name="radio-group-field-description-1"
    type="radio"
    value="3"
    /><span class="ui-label">Radio 3</span></label
    >
    </div>
    </fieldset>
    <fieldset class="ui-fieldset">
    <legend>Legend</legend>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-radio"
    ><input
    name="radio-group-field-description-2"
    type="radio"
    value="1"
    checked
    /><span class="ui-label">Radio 1</span></label
    ><label class="ui-radio"
    ><input
    name="radio-group-field-description-2"
    type="radio"
    value="2"
    /><span class="ui-label">Radio 2</span></label
    ><label class="ui-radio"
    ><input
    name="radio-group-field-description-2"
    type="radio"
    value="3"
    /><span class="ui-label">Radio 3</span></label
    >
    </div>
    <p class="ui-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="radio-group-disabled">
    <Radio value="1" checked>Radio 1</Radio>
    <Radio value="2">Radio 2</Radio>
    <Radio value="3">Radio 3</Radio>
    </FieldGroup>
    </FieldSet>
    </Form>
    <form class="ui-form">
    <fieldset disabled class="ui-fieldset">
    <legend>Legend</legend>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-radio"
    ><input
    name="radio-group-disabled"
    type="radio"
    value="1"
    checked
    /><span class="ui-label">Radio 1</span></label
    ><label class="ui-radio"
    ><input name="radio-group-disabled" type="radio" value="2" /><span
    class="ui-label"
    >Radio 2</span
    ></label
    ><label class="ui-radio"
    ><input name="radio-group-disabled" type="radio" value="3" /><span
    class="ui-label"
    >Radio 3</span
    ></label
    >
    </div>
    </fieldset>
    </form>

    Required

    Set required on at least one Radio in the group.

    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="radio-group-required">
    <Radio value="1" required>Radio 1</Radio>
    <Radio value="2" required>Radio 2</Radio>
    <Radio value="3" required>Radio 3</Radio>
    </FieldGroup>
    </FieldSet>
    </Form>
    <form class="ui-form">
    <fieldset class="ui-fieldset">
    <legend>These are required!</legend>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-radio"
    ><input
    name="radio-group-required"
    type="radio"
    value="1"
    required
    /><span class="ui-label">Radio 1</span></label
    ><label class="ui-radio"
    ><input
    name="radio-group-required"
    type="radio"
    value="2"
    required
    /><span class="ui-label">Radio 2</span></label
    ><label class="ui-radio"
    ><input
    name="radio-group-required"
    type="radio"
    value="3"
    required
    /><span class="ui-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="radio-group-validation">
    <Radio value="1" checked>Radio 1</Radio>
    <Radio value="2">Radio 2</Radio>
    <Radio value="3">Radio 3</Radio>
    </FieldGroup>
    <span class="ui-end-text">Something went wrong!</span>
    </FieldSet>
    </Form>
    <form class="ui-form">
    <fieldset data-invalid class="ui-fieldset">
    <legend>Legend</legend>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-radio"
    ><input
    name="radio-group-validation"
    type="radio"
    value="1"
    checked
    /><span class="ui-label">Radio 1</span></label
    ><label class="ui-radio"
    ><input name="radio-group-validation" type="radio" value="2" /><span
    class="ui-label"
    >Radio 2</span
    ></label
    ><label class="ui-radio"
    ><input name="radio-group-validation" type="radio" value="3" /><span
    class="ui-label"
    >Radio 3</span
    ></label
    >
    </div>
    <span class="ui-end-text">Something went wrong!</span>
    </fieldset>
    </form>

    API

    Radio API

    PropTypeDefaultDescription
    errorbooleanfalseShows error styles.
    hideLabelbooleanfalseVisually hides the label.
    size"small", "large"-The size of the element.
    stackbooleanfalseStacks the label under the input.

    Slots

    SlotDescription
    defaultThe label.
    end-textSupporting text displayed below the label.

    Other attributes, such as checked, disabled, name and value, go to the <input>.

    Field group API

    PropTypeDefaultDescription
    direction"row", "column"-The orientation of the element.
    namestring-Sets name on every input, select and textarea inside.

    Slots

    SlotDescription
    defaultThe fields, such as checkboxes, radios or switches.

    Browser support

    Full supportSupported since v105.Full supportSupported since v121.Full supportSupported since v15.4.

    See also the full browser support guide.

    Installation

    See also

    @layer components.root {
    label.ui-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) {
    .ui-label:after {
    color: var(--red);
    content: "*";
    inset: 0 -0.25ex auto auto;
    position: absolute;
    }
    }
    /* Label */
    .ui-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(.ui-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 */
    &.ui-stack {
    grid-auto-columns: unset;
    justify-items: center;
    .ui-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;
    }
    }
    .ui-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 */
    &.ui-small {
    input[type="radio"] {
    --_input-size: var(--size-3);
    }
    }
    &.ui-large {
    input[type="radio"] {
    --_input-size: var(--size-4);
    }
    }
    /* Validation */
    &[data-invalid],
    &:has(:user-invalid) {
    --primary: var(--critical);
    :where(.ui-end-text) {
    color: var(--critical);
    }
    }
    /* 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(.ui-form) {
    display: grid;
    gap: var(--size-8);
    hr {
    margin-block: 0;
    }
    }
    /* Fieldset */
    :where(.ui-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, .ui-legend) {
    all: unset;
    color: var(--text-primary);
    font-weight: 600;
    margin-block-end: var(--size-3);
    padding: 0;
    &:has(+ :where(.ui-field-description)) {
    margin-block-end: 0;
    }
    }
    /* Field description / supporting text */
    :where(.ui-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(.ui-end-text) {
    color: var(--text-muted);
    font-size: var(--font-size-0);
    line-height: var(--font-lineheight-3);
    }
    &:has(.ui-text-field.ui-row) {
    row-gap: var(--size-7);
    }
    /* Disabled */
    &[disabled] {
    opacity: 0.64;
    user-select: none;
    input,
    label,
    .ui-label {
    cursor: not-allowed;
    }
    }
    /* Error / validation */
    &[data-invalid] {
    :where(.ui-end-text) {
    color: var(--critical);
    }
    :where(.ui-checkbox, .ui-radio, .ui-switch) {
    --primary: var(--critical);
    :where(.ui-end-text) {
    color: var(--critical);
    }
    }
    :where(.ui-switch) {
    input {
    border-radius: var(--radius-round);
    outline: 2px solid var(--critical);
    }
    }
    }
    /* Required */
    &:has(:invalid) {
    :where(legend, .ui-legend) {
    padding-inline-end: 1ex;
    position: relative;
    &::after {
    color: var(--red);
    content: "*";
    inset: 0 -0.25ex auto auto;
    position: absolute;
    }
    }
    }
    }
    /* Field group */
    :where(.ui-field-group) {
    display: flex;
    flex-direction: column;
    gap: var(--size-4);
    /* If it only has checkboxes, radios, or switches */
    &:has(> :where(.ui-checkbox, .ui-radio, .ui-switch)):not(
    :has(> :not(.ui-checkbox, .ui-radio, .ui-switch))
    ) {
    gap: var(--size-2);
    }
    /* If it only has buttons */
    &:has(.ui-button):not(:has(*:not(.ui-button))) {
    align-items: center;
    flex-direction: row;
    gap: var(--size-2);
    /* If it's not preceded by an hr */
    &:not(hr + &) {
    margin-block-start: var(--size-4);
    }
    }
    & + & {
    margin-block-start: var(--size-5);
    }
    /* Directions */
    &.ui-row {
    flex-direction: row;
    flex-wrap: wrap;
    &:has(> :where(.ui-checkbox, .ui-radio, .ui-switch)) {
    gap: var(--size-4);
    }
    }
    }
    }