md-radio spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
Exactly one option from a small, visible set. Radios sharing a name form
an exclusive group with a roving tabstop and arrow-key navigation, and
participate in form submission and validation via ElementInternals.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div role="radiogroup" aria-labelledby="speed-label" style="display:flex; flex-direction:column; gap:8px;">
<span id="speed-label" style="font-weight:600;">Delivery speed</span>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="std" checked></md-radio> Standard</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="exp"></md-radio> Express</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="ovn"></md-radio> Overnight</label>
</div>import { MdRadio } from '@awc-ui/react';
export function Demo() {
return (
<>
<div role="radiogroup" aria-labelledby="speed-label" style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<span id="speed-label" style={{ fontWeight: '600' }}>Delivery speed</span>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="speed" value="std" checked></MdRadio> Standard</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="speed" value="exp"></MdRadio> Express</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="speed" value="ovn"></MdRadio> Overnight</label>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div role="radiogroup" aria-labelledby="speed-label" style="display:flex; flex-direction:column; gap:8px;">
<span id="speed-label" style="font-weight:600;">Delivery speed</span>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="std" checked></md-radio> Standard</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="exp"></md-radio> Express</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="ovn"></md-radio> Overnight</label>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div role="radiogroup" aria-labelledby="speed-label" style="display:flex; flex-direction:column; gap:8px;">
<span id="speed-label" style="font-weight:600;">Delivery speed</span>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="std" checked></md-radio> Standard</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="exp"></md-radio> Express</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="ovn"></md-radio> Overnight</label>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div role="radiogroup" aria-labelledby="speed-label" style="display:flex; flex-direction:column; gap:8px;">
<span id="speed-label" style="font-weight:600;">Delivery speed</span>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="std" checked></md-radio> Standard</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="exp"></md-radio> Express</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="speed" value="ovn"></md-radio> Overnight</label>
</div>
Already installed? See the
Installation guide for one-time package setup
(core + tokens, fonts). Each tab below shows two patterns for using
md-radio in your project: Option A registers every AWC UI
component at once (simplest), Option B imports only this component for
tree-shake-friendly bundles.
<!-- ─── Option A: global registration (all components) ─── -->
<script type="module">
import '@awc-ui/core/define';
</script>
<!-- ─── Option B: single import (tree-shake only md-radio) ─── -->
<script type="module">
import '@awc-ui/core/components/md-radio';
</script>
<md-radio></md-radio>// ─── Option A: typed React wrapper (registers all components) ───
// Importing from '@awc-ui/react' calls defineCustomElements() as a
// side effect, so every md-* element becomes available in the browser.
import { MdRadio } from '@awc-ui/react';
export function Example() {
return <MdRadio></MdRadio>;
}
// ─── Option B: single import (tree-shake to only md-radio) ───
// Skip the wrapper and use the raw custom element. Smallest bundle,
// but you lose typed props/events on JSX.
import '@awc-ui/core/components/md-radio';
export function ExampleTreeShaken() {
return <md-radio></md-radio>;
}// ─── Option A: schema module (any md-* element accepted) ───
// Pair with `defineCustomElements(window)` in main.ts.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<md-radio></md-radio>`,
})
export class ExampleComponent {}
// ─── Option B: typed directive (tree-shake friendly) ───
// Pair with `import '@awc-ui/core/components/md-radio'` in main.ts.
import { Component } from '@angular/core';
import { MdRadio } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [MdRadio],
template: `<md-radio></md-radio>`,
})
export class ExampleTreeShakenComponent {}<!-- ─── Option A: typed Vue wrapper (registers all components) ─── -->
<script setup lang="ts">
import { MdRadio } from '@awc-ui/vue';
</script>
<template>
<MdRadio></MdRadio>
</template>
<!-- ─── Option B: single import (tree-shake to only md-radio) ─── -->
<script setup lang="ts">
import '@awc-ui/core/components/md-radio';
</script>
<template>
<md-radio></md-radio>
</template><!-- ─── Option A: global registration (done once in main entry) ─── -->
<!-- main.ts: -->
<!-- import { defineCustomElements } from '@awc-ui/svelte'; -->
<!-- defineCustomElements(window); -->
<md-radio></md-radio>
<!-- ─── Option B: single import (tree-shake to only md-radio) ─── -->
<script lang="ts">
import '@awc-ui/core/components/md-radio';
</script>
<md-radio></md-radio>| Situation | Use instead |
|---|---|
| More than one option may be chosen | md-checkbox |
| More than five options, or constrained space | md-select |
| A view/mode switch that stays visible | md-segmented-button |
| An immediate on/off setting | md-switch |
| Filtering | md-chip |
| Choosing from a long or remote list | md-select / md-autocomplete |
| Running an action | md-button |
| Need | Setting |
|---|---|
| One choice from a visible set | This component |
| Group membership | A shared name |
| Preselect | checked on one member |
| Unavailable but discoverable | soft-disabled |
| Blocks submission when nothing is picked | required on a member |
| Smaller circles | density="-1…-4" |
name is the group. Grouping is by matching name, not by DOM nesting — a
<fieldset> alone does not scope anything. Two logically separate groups on one
page must use different names.
The group gets a roving tabstop: Tab enters the group once, arrow keys
move between options. That is the standard radiogroup pattern and it is
implemented for you.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<fieldset style="display:flex; flex-direction:column; gap:8px; border:1px solid var(--md-sys-color-outline-variant); border-radius:12px; padding:16px;">
<legend style="padding-inline:6px;">Plan</legend>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="free" checked></md-radio> Free</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="pro"></md-radio> Pro</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="team"></md-radio> Team</label>
</fieldset>
<fieldset style="display:flex; flex-direction:column; gap:8px; border:1px solid var(--md-sys-color-outline-variant); border-radius:12px; padding:16px;">
<legend style="padding-inline:6px;">Billing</legend>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="billing" value="monthly" checked></md-radio> Monthly</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="billing" value="yearly"></md-radio> Yearly</label>
</fieldset>import { MdRadio } from '@awc-ui/react';
export function Demo() {
return (
<>
<fieldset style={{ display: 'flex', flexDirection: 'column', gap: '8px', border: '1px solid var(--md-sys-color-outline-variant)', borderRadius: '12px', padding: '16px' }}>
<legend style={{ paddingInline: '6px' }}>Plan</legend>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="plan" value="free" checked></MdRadio> Free</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="plan" value="pro"></MdRadio> Pro</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="plan" value="team"></MdRadio> Team</label>
</fieldset>
<fieldset style={{ display: 'flex', flexDirection: 'column', gap: '8px', border: '1px solid var(--md-sys-color-outline-variant)', borderRadius: '12px', padding: '16px' }}>
<legend style={{ paddingInline: '6px' }}>Billing</legend>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="billing" value="monthly" checked></MdRadio> Monthly</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="billing" value="yearly"></MdRadio> Yearly</label>
</fieldset>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<fieldset style="display:flex; flex-direction:column; gap:8px; border:1px solid var(--md-sys-color-outline-variant); border-radius:12px; padding:16px;">
<legend style="padding-inline:6px;">Plan</legend>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="free" checked></md-radio> Free</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="pro"></md-radio> Pro</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="team"></md-radio> Team</label>
</fieldset>
<fieldset style="display:flex; flex-direction:column; gap:8px; border:1px solid var(--md-sys-color-outline-variant); border-radius:12px; padding:16px;">
<legend style="padding-inline:6px;">Billing</legend>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="billing" value="monthly" checked></md-radio> Monthly</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="billing" value="yearly"></md-radio> Yearly</label>
</fieldset><script setup>
import '@awc-ui/core/define';
</script>
<template>
<fieldset style="display:flex; flex-direction:column; gap:8px; border:1px solid var(--md-sys-color-outline-variant); border-radius:12px; padding:16px;">
<legend style="padding-inline:6px;">Plan</legend>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="free" checked></md-radio> Free</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="pro"></md-radio> Pro</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="team"></md-radio> Team</label>
</fieldset>
<fieldset style="display:flex; flex-direction:column; gap:8px; border:1px solid var(--md-sys-color-outline-variant); border-radius:12px; padding:16px;">
<legend style="padding-inline:6px;">Billing</legend>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="billing" value="monthly" checked></md-radio> Monthly</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="billing" value="yearly"></md-radio> Yearly</label>
</fieldset>
</template><script>
import '@awc-ui/core/define';
</script>
<fieldset style="display:flex; flex-direction:column; gap:8px; border:1px solid var(--md-sys-color-outline-variant); border-radius:12px; padding:16px;">
<legend style="padding-inline:6px;">Plan</legend>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="free" checked></md-radio> Free</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="pro"></md-radio> Pro</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="plan" value="team"></md-radio> Team</label>
</fieldset>
<fieldset style="display:flex; flex-direction:column; gap:8px; border:1px solid var(--md-sys-color-outline-variant); border-radius:12px; padding:16px;">
<legend style="padding-inline:6px;">Billing</legend>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="billing" value="monthly" checked></md-radio> Monthly</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="billing" value="yearly"></md-radio> Yearly</label>
</fieldset>| State | Focusable | Use when |
|---|---|---|
disabled | No | The option is unavailable and not worth discovering |
soft-disabled | Yes | Contextually unavailable but worth discovering |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:flex; flex-direction:column; gap:8px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st" value="a"></md-radio> Unselected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st" value="b" checked></md-radio> Selected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st2" value="c" disabled></md-radio> Disabled</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st3" value="d" checked disabled></md-radio> Disabled + selected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st4" value="e" soft-disabled></md-radio> Soft-disabled (still focusable)</label>
</div>import { MdRadio } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="st" value="a"></MdRadio> Unselected</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="st" value="b" checked></MdRadio> Selected</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="st2" value="c" disabled></MdRadio> Disabled</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="st3" value="d" checked disabled></MdRadio> Disabled + selected</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="st4" value="e" soft-disabled></MdRadio> Soft-disabled (still focusable)</label>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:flex; flex-direction:column; gap:8px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st" value="a"></md-radio> Unselected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st" value="b" checked></md-radio> Selected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st2" value="c" disabled></md-radio> Disabled</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st3" value="d" checked disabled></md-radio> Disabled + selected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st4" value="e" soft-disabled></md-radio> Soft-disabled (still focusable)</label>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display:flex; flex-direction:column; gap:8px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st" value="a"></md-radio> Unselected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st" value="b" checked></md-radio> Selected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st2" value="c" disabled></md-radio> Disabled</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st3" value="d" checked disabled></md-radio> Disabled + selected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st4" value="e" soft-disabled></md-radio> Soft-disabled (still focusable)</label>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display:flex; flex-direction:column; gap:8px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st" value="a"></md-radio> Unselected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st" value="b" checked></md-radio> Selected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st2" value="c" disabled></md-radio> Disabled</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st3" value="d" checked disabled></md-radio> Disabled + selected</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="st4" value="e" soft-disabled></md-radio> Soft-disabled (still focusable)</label>
</div>M3 advises vertical lists. A horizontal row is harder to scan and pairs labels with the wrong control at narrow widths — reserve it for two very short options.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:flex; flex-direction:column; gap:16px;">
<div role="radiogroup" aria-label="Vertical example" style="display:flex; flex-direction:column; gap:8px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="1" checked></md-radio> Vertical, preferred</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="2"></md-radio> Easy to scan</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="3"></md-radio> Labels never ambiguous</label>
</div>
<div role="radiogroup" aria-label="Horizontal example" style="display:flex; gap:20px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="h" value="yes" checked></md-radio> Yes</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="h" value="no"></md-radio> No</label>
</div>
</div>import { MdRadio } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
<div role="radiogroup" aria-label="Vertical example" style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="v" value="1" checked></MdRadio> Vertical, preferred</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="v" value="2"></MdRadio> Easy to scan</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="v" value="3"></MdRadio> Labels never ambiguous</label>
</div>
<div role="radiogroup" aria-label="Horizontal example" style={{ display: 'flex', gap: '20px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="h" value="yes" checked></MdRadio> Yes</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="h" value="no"></MdRadio> No</label>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:flex; flex-direction:column; gap:16px;">
<div role="radiogroup" aria-label="Vertical example" style="display:flex; flex-direction:column; gap:8px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="1" checked></md-radio> Vertical, preferred</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="2"></md-radio> Easy to scan</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="3"></md-radio> Labels never ambiguous</label>
</div>
<div role="radiogroup" aria-label="Horizontal example" style="display:flex; gap:20px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="h" value="yes" checked></md-radio> Yes</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="h" value="no"></md-radio> No</label>
</div>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display:flex; flex-direction:column; gap:16px;">
<div role="radiogroup" aria-label="Vertical example" style="display:flex; flex-direction:column; gap:8px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="1" checked></md-radio> Vertical, preferred</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="2"></md-radio> Easy to scan</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="3"></md-radio> Labels never ambiguous</label>
</div>
<div role="radiogroup" aria-label="Horizontal example" style="display:flex; gap:20px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="h" value="yes" checked></md-radio> Yes</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="h" value="no"></md-radio> No</label>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display:flex; flex-direction:column; gap:16px;">
<div role="radiogroup" aria-label="Vertical example" style="display:flex; flex-direction:column; gap:8px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="1" checked></md-radio> Vertical, preferred</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="2"></md-radio> Easy to scan</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="v" value="3"></md-radio> Labels never ambiguous</label>
</div>
<div role="radiogroup" aria-label="Horizontal example" style="display:flex; gap:20px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="h" value="yes" checked></md-radio> Yes</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="h" value="no"></md-radio> No</label>
</div>
</div>density="-1…-4" shrinks the circles and the state layer, and overrides the
inherited data-density rung. Keep the hit target at 48px on touch.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:flex; flex-direction:column; gap:6px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="0" density="0" checked></md-radio> density 0</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-1" density="-1"></md-radio> density -1</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-2" density="-2"></md-radio> density -2</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-3" density="-3"></md-radio> density -3</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-4" density="-4"></md-radio> density -4</label>
</div>import { MdRadio } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="d" value="0" density="0" checked></MdRadio> density 0</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="d" value="-1" density="-1"></MdRadio> density -1</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="d" value="-2" density="-2"></MdRadio> density -2</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="d" value="-3" density="-3"></MdRadio> density -3</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="d" value="-4" density="-4"></MdRadio> density -4</label>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:flex; flex-direction:column; gap:6px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="0" density="0" checked></md-radio> density 0</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-1" density="-1"></md-radio> density -1</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-2" density="-2"></md-radio> density -2</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-3" density="-3"></md-radio> density -3</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-4" density="-4"></md-radio> density -4</label>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display:flex; flex-direction:column; gap:6px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="0" density="0" checked></md-radio> density 0</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-1" density="-1"></md-radio> density -1</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-2" density="-2"></md-radio> density -2</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-3" density="-3"></md-radio> density -3</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-4" density="-4"></md-radio> density -4</label>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display:flex; flex-direction:column; gap:6px;">
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="0" density="0" checked></md-radio> density 0</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-1" density="-1"></md-radio> density -1</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-2" density="-2"></md-radio> density -2</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-3" density="-3"></md-radio> density -3</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="d" value="-4" density="-4"></md-radio> density -4</label>
</div>md-radio is form-associated, so name/value submit without any hidden
<input>. Put required on the group’s members and constraint validation
blocks submit until one is chosen, with value-missing-label as the message.
Two ways to surface that message, and the choice is yours:
novalidate and render it yourself from getValidity(), as the demo
below does. The message then lives on the group, which is where it belongs:
a radio is one member of a set, so unlike md-checkbox
— a single control that owns its own error-text line — there is no one member
for the message to hang off. The group markup is yours, so the message is too.<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<form id="checkout" novalidate>
<div role="radiogroup" aria-labelledby="pay-label" aria-describedby="pay-error">
<span id="pay-label">Payment method</span>
<label>
<md-radio name="pay" value="card" required
value-missing-label="Please choose a payment method."></md-radio>
Card
</label>
<label>
<md-radio name="pay" value="bank" required
value-missing-label="Please choose a payment method."></md-radio>
Bank transfer
</label>
<span id="pay-error" role="alert"></span>
</div>
<md-button variant="filled" type="submit">Pay</md-button>
</form>
<script type="module">
const form = document.getElementById('checkout');
const error = document.getElementById('pay-error');
const radios = document.querySelectorAll('md-radio[name="pay"]');
// novalidate suppresses the browser bubble; the group owns the message.
form.addEventListener('submit', async (e) => {
e.preventDefault();
const { valid, validationMessage } = await radios[0].getValidity();
error.textContent = valid ? '' : validationMessage;
if (valid) save(new FormData(form).get('pay')); // 'card' | 'bank'
});
radios.forEach((r) =>
r.addEventListener('mdChange', () => { error.textContent = ''; }),
);
</script>import { useEffect, useRef } from 'react';
import { MdButton, MdRadio } from '@awc-ui/react';
export function Demo() {
const formRef = useRef(null);
const errorRef = useRef(null);
useEffect(() => {
const form = formRef.current;
const error = errorRef.current;
const radios = document.querySelectorAll('md-radio[name="pay"]');
// novalidate suppresses the browser bubble; the group owns the message.
form.addEventListener('submit', async (e) => {
e.preventDefault();
const { valid, validationMessage } = await radios[0].getValidity();
error.textContent = valid ? '' : validationMessage;
if (valid) save(new FormData(form).get('pay')); // 'card' | 'bank'
});
radios.forEach((r) =>
r.addEventListener('mdChange', () => { error.textContent = ''; }),
);
}, []);
return (
<>
<form id="checkout" ref={formRef} novalidate>
<div role="radiogroup" aria-labelledby="pay-label" aria-describedby="pay-error">
<span id="pay-label">Payment method</span>
<label>
<MdRadio name="pay" value="card" required
valueMissingLabel="Please choose a payment method."></MdRadio>
Card
</label>
<label>
<MdRadio name="pay" value="bank" required
valueMissingLabel="Please choose a payment method."></MdRadio>
Bank transfer
</label>
<span id="pay-error" ref={errorRef} role="alert"></span>
</div>
<MdButton variant="filled" type="submit">Pay</MdButton>
</form>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
// app.component.ts
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-demo',
templateUrl: './app.component.html',
})
export class DemoComponent implements AfterViewInit {
@ViewChild('form') formRef!: ElementRef;
@ViewChild('error') errorRef!: ElementRef;
ngAfterViewInit() {
const form = this.formRef.nativeElement;
const error = this.errorRef.nativeElement;
const radios = document.querySelectorAll('md-radio[name="pay"]');
// novalidate suppresses the browser bubble; the group owns the message.
form.addEventListener('submit', async (e) => {
e.preventDefault();
const { valid, validationMessage } = await radios[0].getValidity();
error.textContent = valid ? '' : validationMessage;
if (valid) save(new FormData(form).get('pay')); // 'card' | 'bank'
});
radios.forEach((r) =>
r.addEventListener('mdChange', () => { error.textContent = ''; }),
);
}
}
<!-- app.component.html -->
<form id="checkout" #form novalidate>
<div role="radiogroup" aria-labelledby="pay-label" aria-describedby="pay-error">
<span id="pay-label">Payment method</span>
<label>
<md-radio name="pay" value="card" required
value-missing-label="Please choose a payment method."></md-radio>
Card
</label>
<label>
<md-radio name="pay" value="bank" required
value-missing-label="Please choose a payment method."></md-radio>
Bank transfer
</label>
<span id="pay-error" #error role="alert"></span>
</div>
<md-button variant="filled" type="submit">Pay</md-button>
</form><script setup>
import { onMounted, ref } from 'vue';
import '@awc-ui/core/define';
const formRef = ref(null);
const errorRef = ref(null);
onMounted(() => {
const form = formRef.value;
const error = errorRef.value;
const radios = document.querySelectorAll('md-radio[name="pay"]');
// novalidate suppresses the browser bubble; the group owns the message.
form.addEventListener('submit', async (e) => {
e.preventDefault();
const { valid, validationMessage } = await radios[0].getValidity();
error.textContent = valid ? '' : validationMessage;
if (valid) save(new FormData(form).get('pay')); // 'card' | 'bank'
});
radios.forEach((r) =>
r.addEventListener('mdChange', () => { error.textContent = ''; }),
);
});
</script>
<template>
<form id="checkout" ref="formRef" novalidate>
<div role="radiogroup" aria-labelledby="pay-label" aria-describedby="pay-error">
<span id="pay-label">Payment method</span>
<label>
<md-radio name="pay" value="card" required
value-missing-label="Please choose a payment method."></md-radio>
Card
</label>
<label>
<md-radio name="pay" value="bank" required
value-missing-label="Please choose a payment method."></md-radio>
Bank transfer
</label>
<span id="pay-error" ref="errorRef" role="alert"></span>
</div>
<md-button variant="filled" type="submit">Pay</md-button>
</form>
</template><script>
import { onMount } from 'svelte';
import '@awc-ui/core/define';
let formRef;
let errorRef;
onMount(() => {
const form = formRef;
const error = errorRef;
const radios = document.querySelectorAll('md-radio[name="pay"]');
// novalidate suppresses the browser bubble; the group owns the message.
form.addEventListener('submit', async (e) => {
e.preventDefault();
const { valid, validationMessage } = await radios[0].getValidity();
error.textContent = valid ? '' : validationMessage;
if (valid) save(new FormData(form).get('pay')); // 'card' | 'bank'
});
radios.forEach((r) =>
r.addEventListener('mdChange', () => { error.textContent = ''; }),
);
});
</script>
<form id="checkout" bind:this={formRef} novalidate>
<div role="radiogroup" aria-labelledby="pay-label" aria-describedby="pay-error">
<span id="pay-label">Payment method</span>
<label>
<md-radio name="pay" value="card" required
value-missing-label="Please choose a payment method."></md-radio>
Card
</label>
<label>
<md-radio name="pay" value="bank" required
value-missing-label="Please choose a payment method."></md-radio>
Bank transfer
</label>
<span id="pay-error" bind:this={errorRef} role="alert"></span>
</div>
<md-button variant="filled" type="submit">Pay</md-button>
</form>| Event | Cancelable | Detail | Fires |
|---|---|---|---|
mdChange | no | { checked, value } | The radio becomes checked |
mdFocus / mdBlur | no | void | Focus enters / leaves the control |
mdValidityChange | no | { valid, validationMessage, flags } | Validity is recomputed |
select() checks this radio, unchecks its siblings, and emits mdChange as if
a user had done it.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div role="radiogroup" aria-label="Shipping">
<label><md-radio name="ship" value="std"></md-radio> Standard</label>
<label><md-radio name="ship" value="exp"></md-radio> Express</label>
</div>
<script type="module">
const express = document.querySelector('md-radio[value="exp"]');
// Checks it, unchecks its siblings, and emits mdChange as a user click would.
express.select();
</script>import { useEffect } from 'react';
import { MdRadio } from '@awc-ui/react';
export function Demo() {
useEffect(() => {
const express = document.querySelector('md-radio[value="exp"]');
// Checks it, unchecks its siblings, and emits mdChange as a user click would.
express.select();
}, []);
return (
<>
<div role="radiogroup" aria-label="Shipping">
<label><MdRadio name="ship" value="std"></MdRadio> Standard</label>
<label><MdRadio name="ship" value="exp"></MdRadio> Express</label>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
// app.component.ts
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-demo',
templateUrl: './app.component.html',
})
export class DemoComponent implements AfterViewInit {
ngAfterViewInit() {
const express = document.querySelector('md-radio[value="exp"]');
// Checks it, unchecks its siblings, and emits mdChange as a user click would.
express.select();
}
}
<!-- app.component.html -->
<div role="radiogroup" aria-label="Shipping">
<label><md-radio name="ship" value="std"></md-radio> Standard</label>
<label><md-radio name="ship" value="exp"></md-radio> Express</label>
</div><script setup>
import { onMounted } from 'vue';
import '@awc-ui/core/define';
onMounted(() => {
const express = document.querySelector('md-radio[value="exp"]');
// Checks it, unchecks its siblings, and emits mdChange as a user click would.
express.select();
});
</script>
<template>
<div role="radiogroup" aria-label="Shipping">
<label><md-radio name="ship" value="std"></md-radio> Standard</label>
<label><md-radio name="ship" value="exp"></md-radio> Express</label>
</div>
</template><script>
import { onMount } from 'svelte';
import '@awc-ui/core/define';
onMount(() => {
const express = document.querySelector('md-radio[value="exp"]');
// Checks it, unchecks its siblings, and emits mdChange as a user click would.
express.select();
});
</script>
<div role="radiogroup" aria-label="Shipping">
<label><md-radio name="ship" value="std"></md-radio> Standard</label>
<label><md-radio name="ship" value="exp"></md-radio> Express</label>
</div>syncValidityFromGroup() exists so siblings can agree on validity — it is a
coordination hook between radios, not something you normally call.
| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
valueMissingLabel | value-missing-label | string | 'Please select one of these options.' | — |
checked | checked | boolean | false | Yes |
disabled | disabled | boolean | false | Yes |
softDisabled | soft-disabled | boolean | false | Yes |
required | required | boolean | false | Yes |
name | name | string | '' | Yes |
value | value | string | '' | Yes |
density | density | 0 | -1 | -2 | -3 | -4 | 0 | Yes |
| Method | Parameters |
|---|---|
syncValidityFromGroup() | none |
getValidity() | none |
checkValidity() | none |
reportValidity() | none |
setCustomValidity() | message: string |
setFocus() | none |
setBlur() | none |
select() | none |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-radio-icon-color | Unchecked outer circle color |
--md-radio-selected-icon-color | Checked outer circle + inner dot color |
--md-radio-icon-size | Outer circle size |
--md-radio-inner-circle-size | Inner dot size |
--md-radio-state-layer-size | State-layer diameter |
--md-radio-state-layer-color | State-layer color (unchecked) |
--md-radio-selected-state-layer-color | — |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
state-layer | Hover / focus / press overlay |
container | Wrapper around outer + inner circles |
outer-circle | The 20px circle border |
inner-circle | The 10px filled dot |
<label> for its accessible name and a bigger hit
target. There is no default slot, so unwrapped text is dropped.role="radiogroup" + aria-labelledby, or a
<fieldset>/<legend>. The component cannot do this for you.Tab stop per group, arrows to move — implemented for
you.disabled leaves the tab order; soft-disabled stays focusable so an
unavailable option is still discoverable.| Key | Action |
|---|---|
Tab | One stop per group — it lands on the checked radio, or the first enabled one |
↑ / ← | Previous option, wrapping (mirrored under dir="rtl") |
↓ / → | Next option, wrapping |
Space | Select the focused option |
Arrow keys select as they move, which is the ARIA radiogroup pattern — there is no “preview without committing”.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div role="radiogroup" aria-label="Delivery speed" style="display:flex;flex-direction:column;gap:8px;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="std" checked></md-radio> Standard</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="exp"></md-radio> Express</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="ovn" soft-disabled></md-radio> Overnight — unavailable to your address</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="drn" disabled></md-radio> Drone — skipped by Tab</label>
</div>import { MdRadio } from '@awc-ui/react';
export function Demo() {
return (
<>
<div role="radiogroup" aria-label="Delivery speed" style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="a11y-speed" value="std" checked></MdRadio> Standard</label>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="a11y-speed" value="exp"></MdRadio> Express</label>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="a11y-speed" value="ovn" soft-disabled></MdRadio> Overnight — unavailable to your address</label>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="a11y-speed" value="drn" disabled></MdRadio> Drone — skipped by Tab</label>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div role="radiogroup" aria-label="Delivery speed" style="display:flex;flex-direction:column;gap:8px;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="std" checked></md-radio> Standard</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="exp"></md-radio> Express</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="ovn" soft-disabled></md-radio> Overnight — unavailable to your address</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="drn" disabled></md-radio> Drone — skipped by Tab</label>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div role="radiogroup" aria-label="Delivery speed" style="display:flex;flex-direction:column;gap:8px;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="std" checked></md-radio> Standard</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="exp"></md-radio> Express</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="ovn" soft-disabled></md-radio> Overnight — unavailable to your address</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="drn" disabled></md-radio> Drone — skipped by Tab</label>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div role="radiogroup" aria-label="Delivery speed" style="display:flex;flex-direction:column;gap:8px;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="std" checked></md-radio> Standard</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="exp"></md-radio> Express</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="ovn" soft-disabled></md-radio> Overnight — unavailable to your address</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="a11y-speed" value="drn" disabled></md-radio> Drone — skipped by Tab</label>
</div>RTL — the control and its label side mirror automatically, and arrow-key direction follows reading order. See RTL.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:16px;align-items:center;">
<span style="inline-size:2.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr" role="radiogroup" aria-label="Speed" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-a" value="std" checked></md-radio> Standard</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-a" value="exp"></md-radio> Express</label>
</div>
<span style="inline-size:2.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl" role="radiogroup" aria-label="السرعة" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-b" value="std" checked></md-radio> قياسي</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-b" value="exp"></md-radio> سريع</label>
</div>
</div>import { MdRadio } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '16px', alignItems: 'center' }}>
<span style={{ inlineSize: '2.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>ltr</span>
<div dir="ltr" role="radiogroup" aria-label="Speed" style={{ display: 'flex', gap: '20px', flexWrap: 'wrap' }}>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="rtl-a" value="std" checked></MdRadio> Standard</label>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="rtl-a" value="exp"></MdRadio> Express</label>
</div>
<span style={{ inlineSize: '2.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>rtl</span>
<div dir="rtl" role="radiogroup" aria-label="السرعة" style={{ display: 'flex', gap: '20px', flexWrap: 'wrap' }}>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="rtl-b" value="std" checked></MdRadio> قياسي</label>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="rtl-b" value="exp"></MdRadio> سريع</label>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:grid;grid-template-columns:auto 1fr;gap:16px;align-items:center;">
<span style="inline-size:2.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr" role="radiogroup" aria-label="Speed" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-a" value="std" checked></md-radio> Standard</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-a" value="exp"></md-radio> Express</label>
</div>
<span style="inline-size:2.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl" role="radiogroup" aria-label="السرعة" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-b" value="std" checked></md-radio> قياسي</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-b" value="exp"></md-radio> سريع</label>
</div>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display:grid;grid-template-columns:auto 1fr;gap:16px;align-items:center;">
<span style="inline-size:2.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr" role="radiogroup" aria-label="Speed" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-a" value="std" checked></md-radio> Standard</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-a" value="exp"></md-radio> Express</label>
</div>
<span style="inline-size:2.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl" role="radiogroup" aria-label="السرعة" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-b" value="std" checked></md-radio> قياسي</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-b" value="exp"></md-radio> سريع</label>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:16px;align-items:center;">
<span style="inline-size:2.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr" role="radiogroup" aria-label="Speed" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-a" value="std" checked></md-radio> Standard</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-a" value="exp"></md-radio> Express</label>
</div>
<span style="inline-size:2.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl" role="radiogroup" aria-label="السرعة" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-b" value="std" checked></md-radio> قياسي</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="rtl-b" value="exp"></md-radio> سريع</label>
</div>
</div>Density — density="-1…-4" shrinks the circles and state layer. See
Density.
i18n — translate the label text you wrap around the radio and the
value-missing-label validation message.
| Custom property | Purpose |
|---|---|
--md-radio-icon-color | Circle color when unselected |
--md-radio-selected-icon-color | Circle color when selected |
--md-radio-icon-size | Outer circle size |
--md-radio-inner-circle-size | Selected dot size |
--md-radio-state-layer-size | Hover/press overlay size |
--md-radio-state-layer-color / --md-radio-selected-state-layer-color | Overlay tint |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="theme" value="a" checked style="--md-radio-selected-icon-color: var(--md-sys-color-tertiary); --md-radio-selected-state-layer-color: var(--md-sys-color-tertiary);"></md-radio> Tertiary accent</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="theme" value="b" style="--md-radio-icon-size: 28px; --md-radio-inner-circle-size: 14px;"></md-radio> Larger circle</label>import { MdRadio } from '@awc-ui/react';
export function Demo() {
return (
<>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="theme" value="a" checked style={{ '--md-radio-selected-icon-color': 'var(--md-sys-color-tertiary)', '--md-radio-selected-state-layer-color': 'var(--md-sys-color-tertiary)' }}></MdRadio> Tertiary accent</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><MdRadio name="theme" value="b" style={{ '--md-radio-icon-size': '28px', '--md-radio-inner-circle-size': '14px' }}></MdRadio> Larger circle</label>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="theme" value="a" checked style="--md-radio-selected-icon-color: var(--md-sys-color-tertiary); --md-radio-selected-state-layer-color: var(--md-sys-color-tertiary);"></md-radio> Tertiary accent</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="theme" value="b" style="--md-radio-icon-size: 28px; --md-radio-inner-circle-size: 14px;"></md-radio> Larger circle</label><script setup>
import '@awc-ui/core/define';
</script>
<template>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="theme" value="a" checked style="--md-radio-selected-icon-color: var(--md-sys-color-tertiary); --md-radio-selected-state-layer-color: var(--md-sys-color-tertiary);"></md-radio> Tertiary accent</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="theme" value="b" style="--md-radio-icon-size: 28px; --md-radio-inner-circle-size: 14px;"></md-radio> Larger circle</label>
</template><script>
import '@awc-ui/core/define';
</script>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="theme" value="a" checked style="--md-radio-selected-icon-color: var(--md-sys-color-tertiary); --md-radio-selected-state-layer-color: var(--md-sys-color-tertiary);"></md-radio> Tertiary accent</label>
<label style="display:flex; align-items:center; gap:8px;"><md-radio name="theme" value="b" style="--md-radio-icon-size: 28px; --md-radio-inner-circle-size: 14px;"></md-radio> Larger circle</label>Every default resolves through an md-sys-color role, so a radio that sets no
custom properties follows the theme on its own:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div role="radiogroup" aria-label="Untouched defaults" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="a" checked></md-radio> Selected</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="b"></md-radio> Unselected</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="c" soft-disabled></md-radio> Soft-disabled</label>
</div>import { MdRadio } from '@awc-ui/react';
export function Demo() {
return (
<>
<div role="radiogroup" aria-label="Untouched defaults" style={{ display: 'flex', gap: '20px', flexWrap: 'wrap' }}>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="dk" value="a" checked></MdRadio> Selected</label>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="dk" value="b"></MdRadio> Unselected</label>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio name="dk" value="c" soft-disabled></MdRadio> Soft-disabled</label>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div role="radiogroup" aria-label="Untouched defaults" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="a" checked></md-radio> Selected</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="b"></md-radio> Unselected</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="c" soft-disabled></md-radio> Soft-disabled</label>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div role="radiogroup" aria-label="Untouched defaults" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="a" checked></md-radio> Selected</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="b"></md-radio> Unselected</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="c" soft-disabled></md-radio> Soft-disabled</label>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div role="radiogroup" aria-label="Untouched defaults" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="a" checked></md-radio> Selected</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="b"></md-radio> Unselected</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio name="dk" value="c" soft-disabled></md-radio> Soft-disabled</label>
</div>| Part | Element |
|---|---|
container | The control box |
outer-circle | The ring |
inner-circle | The selected dot |
state-layer | Hover / focus / press overlay |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<style>
.parts-radio::part(outer-circle) { border-width: 3px; }
.parts-radio::part(inner-circle) { border-radius: 3px; }
.parts-radio::part(state-layer) { background: var(--md-sys-color-tertiary); }
</style>
<div role="radiogroup" aria-label="Styled parts" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio class="parts-radio" name="parts" value="a" checked></md-radio> Square dot</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio class="parts-radio" name="parts" value="b"></md-radio> Thick ring</label>
</div>import { MdRadio } from '@awc-ui/react';
export function Demo() {
return (
<>
<style>
.parts-radio::part(outer-circle) { border-width: 3px; }
.parts-radio::part(inner-circle) { border-radius: 3px; }
.parts-radio::part(state-layer) { background: var(--md-sys-color-tertiary); }
</style>
<div role="radiogroup" aria-label="Styled parts" style={{ display: 'flex', gap: '20px', flexWrap: 'wrap' }}>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio className="parts-radio" name="parts" value="a" checked></MdRadio> Square dot</label>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><MdRadio className="parts-radio" name="parts" value="b"></MdRadio> Thick ring</label>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<style>
.parts-radio::part(outer-circle) { border-width: 3px; }
.parts-radio::part(inner-circle) { border-radius: 3px; }
.parts-radio::part(state-layer) { background: var(--md-sys-color-tertiary); }
</style>
<div role="radiogroup" aria-label="Styled parts" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio class="parts-radio" name="parts" value="a" checked></md-radio> Square dot</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio class="parts-radio" name="parts" value="b"></md-radio> Thick ring</label>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<style>
.parts-radio::part(outer-circle) { border-width: 3px; }
.parts-radio::part(inner-circle) { border-radius: 3px; }
.parts-radio::part(state-layer) { background: var(--md-sys-color-tertiary); }
</style>
<div role="radiogroup" aria-label="Styled parts" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio class="parts-radio" name="parts" value="a" checked></md-radio> Square dot</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio class="parts-radio" name="parts" value="b"></md-radio> Thick ring</label>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<style>
.parts-radio::part(outer-circle) { border-width: 3px; }
.parts-radio::part(inner-circle) { border-radius: 3px; }
.parts-radio::part(state-layer) { background: var(--md-sys-color-tertiary); }
</style>
<div role="radiogroup" aria-label="Styled parts" style="display:flex;gap:20px;flex-wrap:wrap;">
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio class="parts-radio" name="parts" value="a" checked></md-radio> Square dot</label>
<label style="display:inline-flex;align-items:center;gap:8px;"><md-radio class="parts-radio" name="parts" value="b"></md-radio> Thick ring</label>
</div>md-checkbox ·
md-switch ·
md-select ·
md-segmented-button ·
md-chip ·
md-ripple
md-radioTwo artefacts to give your AI agent so it generates correct UI with this component. The per-component spec answers "how do I use this exact tag?". The main-llm spec answers "which tag should I pick in the first place?".
md-radio spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
System-prompt preamble · decision matrix · token reference · page recipes · anti-patterns. Paste into the system prompt at the start of a piece of work.