Radio buttons
Radio buttons let people select one option from a set, when every option should be visible at once. Keep to five options or fewer, stacked vertically, with one selected by default; for more, use a select. See the M3 radio button guidelines.
Usage
import { createRadios } from 'mtrl';
const radios = createRadios({
name: 'size',
value: 'm',
options: [
{ value: 's', label: 'Small' },
{ value: 'm', label: 'Medium' },
{ value: 'l', label: 'Large' },
],
});
radios.on('change', ({ value }) => setSize(value));
document.body.append(radios.element);
<m-radios value="m" name="size">
<m-radio value="s">Small</m-radio>
<m-radio value="m">Medium</m-radio>
<m-radio value="l">Large</m-radio>
</m-radios>
<script type="module">
const radios = document.querySelector('m-radios');
radios.addEventListener('change', (event) => setSize(event.detail.value));
</script>
import { useState } from 'react';
import { Radios, Radio } from 'mtrl/react';
import { setSize } from './app';
export function Example() {
const [value, setValue] = useState("m");
return (
<Radios value={value} onChange={(event) => { setValue(event.detail.value); setSize(event.detail.value); }} name="size">
<Radio value="s">Small</Radio>
<Radio value="m">Medium</Radio>
<Radio value="l">Large</Radio>
</Radios>
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { MRadios, MRadio } from 'mtrl/vue';
import { setSize } from './app';
const value = ref("m");
</script>
<template>
<MRadios v-model="value" @change="setSize($event.detail.value)" name="size">
<MRadio value="s">Small</MRadio>
<MRadio value="m">Medium</MRadio>
<MRadio value="l">Large</MRadio>
</MRadios>
</template>
<script lang="ts">
import { Radios, Radio } from 'mtrl/svelte';
import { setSize } from './app';
let value = $state("m");
</script>
<Radios bind:value onchange={(event) => setSize(event.detail.value)} name="size">
<Radio value="s">Small</Radio>
<Radio value="m">Medium</Radio>
<Radio value="l">Large</Radio>
</Radios>
import { createSignal } from 'solid-js';
import { Radios, Radio } from 'mtrl/solid';
import { setSize } from './app';
export function Example() {
const [value, setValue] = createSignal("m");
return (
<Radios value={value()} onChange={(event) => { setValue(event.detail.value); setSize(event.detail.value); }} name="size">
<Radio value="s">Small</Radio>
<Radio value="m">Medium</Radio>
<Radio value="l">Large</Radio>
</Radios>
);
}
Examples
In a row, one option disabled
direction: 'horizontal' lays the options out in a row. An option's own disabled leaves the
rest of the group usable.
import { createRadios } from 'mtrl';
const radios = createRadios({
name: 'delivery',
value: 'standard',
direction: 'horizontal',
options: [
{ value: 'standard', label: 'Standard' },
{ value: 'express', label: 'Express', disabled: true },
{ value: 'pickup', label: 'Pick up' },
],
});
document.body.append(radios.element);
<m-radios value="standard" name="delivery" direction="horizontal">
<m-radio value="standard">Standard</m-radio>
<m-radio value="express" disabled>Express</m-radio>
<m-radio value="pickup">Pick up</m-radio>
</m-radios>
import { useState } from 'react';
import { Radios, Radio } from 'mtrl/react';
export function Example() {
const [value, setValue] = useState("standard");
return (
<Radios value={value} onChange={(event) => setValue(event.detail.value)} name="delivery" direction="horizontal">
<Radio value="standard">Standard</Radio>
<Radio value="express" disabled>Express</Radio>
<Radio value="pickup">Pick up</Radio>
</Radios>
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { MRadios, MRadio } from 'mtrl/vue';
const value = ref("standard");
</script>
<template>
<MRadios v-model="value" name="delivery" direction="horizontal">
<MRadio value="standard">Standard</MRadio>
<MRadio value="express" disabled>Express</MRadio>
<MRadio value="pickup">Pick up</MRadio>
</MRadios>
</template>
<script lang="ts">
import { Radios, Radio } from 'mtrl/svelte';
let value = $state("standard");
</script>
<Radios bind:value name="delivery" direction="horizontal">
<Radio value="standard">Standard</Radio>
<Radio value="express" disabled>Express</Radio>
<Radio value="pickup">Pick up</Radio>
</Radios>
import { createSignal } from 'solid-js';
import { Radios, Radio } from 'mtrl/solid';
export function Example() {
const [value, setValue] = createSignal("standard");
return (
<Radios value={value()} onChange={(event) => setValue(event.detail.value)} name="delivery" direction="horizontal">
<Radio value="standard">Standard</Radio>
<Radio value="express" disabled>Express</Radio>
<Radio value="pickup">Pick up</Radio>
</Radios>
);
}
Setting the value
An action selects an option by its value. An unknown value clears the selection and emits
change, with value '' and option null.
import { createRadios } from 'mtrl';
const radios = createRadios({
name: 'size',
value: 'm',
options: [
{ value: 's', label: 'Small' },
{ value: 'm', label: 'Medium' },
{ value: 'l', label: 'Large' },
],
});
document.body.append(radios.element);
function reset() {
radios.setValue('s');
}
<m-radios value="m" name="size">
<m-radio value="s">Small</m-radio>
<m-radio value="m">Medium</m-radio>
<m-radio value="l">Large</m-radio>
</m-radios>
<script type="module">
const radios = document.querySelector('m-radios');
function reset() {
radios.value = "s";
}
</script>
import { useState } from 'react';
import { Radios, Radio } from 'mtrl/react';
export function Example() {
const [value, setValue] = useState("m");
const reset = () => setValue("s");
return (
<Radios value={value} onChange={(event) => setValue(event.detail.value)} name="size">
<Radio value="s">Small</Radio>
<Radio value="m">Medium</Radio>
<Radio value="l">Large</Radio>
</Radios>
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { MRadios, MRadio } from 'mtrl/vue';
const value = ref("m");
function reset() {
value.value = "s";
}
</script>
<template>
<MRadios v-model="value" name="size">
<MRadio value="s">Small</MRadio>
<MRadio value="m">Medium</MRadio>
<MRadio value="l">Large</MRadio>
</MRadios>
</template>
<script lang="ts">
import { Radios, Radio } from 'mtrl/svelte';
let value = $state("m");
function reset() {
value = "s";
}
</script>
<Radios bind:value name="size">
<Radio value="s">Small</Radio>
<Radio value="m">Medium</Radio>
<Radio value="l">Large</Radio>
</Radios>
import { createSignal } from 'solid-js';
import { Radios, Radio } from 'mtrl/solid';
export function Example() {
const [value, setValue] = createSignal("m");
const reset = () => setValue("s");
return (
<Radios value={value()} onChange={(event) => setValue(event.detail.value)} name="size">
<Radio value="s">Small</Radio>
<Radio value="m">Medium</Radio>
<Radio value="l">Large</Radio>
</Radios>
);
}
An option's labelBefore puts its label before the radio, in the reading direction.
API
Options
| Option | Type | Default | Description |
|---|---|---|---|
name |
string |
required | The inputs' shared name, which makes them one group for the keyboard and forms; an empty name is replaced by a generated one |
options |
RadioOptionConfig[] |
[] |
The options, in order |
value |
string |
undefined |
The selected value |
direction |
'vertical' | 'horizontal' |
'vertical' |
How the options are laid out |
disabled |
boolean |
false |
Whether the whole group starts disabled |
ripple |
boolean |
true |
Whether each radio has its state layer |
class |
string |
undefined |
Additional CSS classes |
prefix |
string |
'mtrl' |
Prefix for CSS class names |
rippleConfig |
object |
undefined |
Deprecated, no effect |
Option
| Option | Type | Default | Description |
|---|---|---|---|
value |
string |
required | The option's value |
label |
string |
required | Its label; a click on it selects the option |
disabled |
boolean |
false |
Whether the option starts disabled |
labelBefore |
boolean |
false |
Places the label before the radio, in the reading direction |
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
getValue() |
none | string |
The selected value, or '' |
setValue(value) |
value: string |
RadiosComponent |
Selects an option; an unknown value clears the selection and emits change |
getSelected() |
none | RadioOptionConfig | null |
The selected option |
addOption(option) |
option: RadioOptionConfig |
RadiosComponent |
Adds an option |
removeOption(value) |
value: string |
RadiosComponent |
Removes an option |
enable() / disable() |
none | RadiosComponent |
The whole group |
enableOption(value) / disableOption(value) |
value: string |
RadiosComponent |
One option |
on(event, handler) / off(event, handler) |
event: 'change', handler: Function |
RadiosComponent |
Adds or removes a listener |
destroy() |
none | void |
Removes the group |
Events
| Event | Description | Data |
|---|---|---|
change |
The user selected an option, or an unknown value cleared the selection | { value, option, originalEvent } |
originalEvent is the DOM event of a user's selection, undefined otherwise. The web
component's change carries { value }.
Accessibility
- A
radiogroupof native radio inputs, each named by its label. Name the group: the factory's element takesaria-labeloraria-labelledby, the web component anaria-labelattribute. Tablands on the selected radio, or the first; the arrow keys move and select, wrapping, and follow the reading direction in right-to-left layouts;Spaceselects the focused radio. This is the native behavior of radios sharing aname.- Keyboard focus draws a 0.10 state layer and a 3dp focus ring; a pointer shows no ring.
Styling
.mtrl-radios { } /* the group */
.mtrl-radios--vertical, .mtrl-radios--horizontal { }
.mtrl-radios__item { }
.mtrl-radios__input { } /* the native radio */
.mtrl-radios__label, .mtrl-radios__label--before { }
.mtrl-radios__control { } /* the 40dp state layer area */
.mtrl-radios__ripple { } /* the state layer */
.mtrl-radios__circle { } /* the ring; ::after is the dot */
.mtrl-radios__text { }
Measurements
From the m3.material.io radio button specs, then Compose's RadioButtonTokens and
material-web.
| Attribute | Value |
|---|---|
| Icon | 20dp, a 2dp ring |
| Unselected | on-surface-variant ring (on-surface on hover, focus and press) |
| Selected | primary ring and a 10dp primary dot |
| Disabled | on-surface 38% |
| State layer | 40dp circle: on-surface when unselected, primary when selected; a press takes the color of the state it leads to |
| Item | 48dp tall |
| Label | Body Medium, on-surface, 8dp from the control |
| Motion | The dot springs in on the fast spatial spring |