Radio group
<gui-radiogroup> is a group of radio buttons to pick one option out of a few. It renders native
radio buttons inside an element with the radiogroup role, named by the label.
import { GuiRadiogroup } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const options = ['Standard', 'Express', 'Overnight'];
export function RadioGroupBasic() { return <GuiRadiogroup label="Delivery" options={options} />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/radiogroup';
@Component({ selector: 'app-radio-group-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-radiogroup label="Delivery" [options]="deliveries"></gui-radiogroup> `,})export class RadioGroupBasicComponent { deliveries = ['Standard', 'Express', 'Overnight'];}<script setup lang="ts">import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const options = ['Standard', 'Express', 'Overnight'];</script>
<template> <gui-radiogroup label="Delivery" :options.prop="options"></gui-radiogroup></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const deliveries = ['Standard', 'Express', 'Overnight'];
@customElement('radio-group-basic')export class RadioGroupBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-radiogroup label="Delivery" .options=${deliveries}></gui-radiogroup> `; }}<gui-radiogroup label="Delivery" options='["Standard", "Express", "Overnight"]'></gui-radiogroup>The element’s entry point is @golemui/gui-components/radiogroup, which registers
<gui-radiogroup> and nothing else. To install the package and load the stylesheet, see
Installation.
The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.
import { useState } from 'react';import { GuiRadiogroup } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const speeds = ['Standard', 'Express', 'Overnight'];
export function DeliveryField() { const [delivery, setDelivery] = useState('');
return ( <GuiRadiogroup label="Delivery" name="delivery" options={speeds} value={delivery} onGuiChange={(event) => setDelivery(event.detail.value ?? '')} /> );}Add CUSTOM_ELEMENTS_SCHEMA to the component that uses the tags, and the stylesheet to styles in angular.json.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import type { GuiEventMap, GuiRadiogroupEvents } from '@golemui/gui-components';import '@golemui/gui-components/radiogroup';
type ChangeEvent = GuiEventMap<typeof GuiRadiogroupEvents>['gui-change'];
@Component({ selector: 'app-delivery-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-radiogroup label="Delivery" name="delivery" [options]="speeds" [value]="delivery" (gui-change)="onChange($event)" ></gui-radiogroup> `,})export class DeliveryFieldComponent { speeds = ['Standard', 'Express', 'Overnight'];
delivery = '';
onChange(event: Event) { this.delivery = (event as ChangeEvent).detail.value ?? ''; }}"styles": ["node_modules/@golemui/gui-components/lib/styles/index.css"]Tell Vue's compiler that gui-* tags are custom elements, and add the typings once for typed props and @gui-* handlers.
<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const speeds = ['Standard', 'Express', 'Overnight'];
const delivery = ref('');</script>
<template> <gui-radiogroup label="Delivery" name="delivery" :options.prop="speeds" :value="delivery" @gui-change="delivery = $event.detail.value ?? ''" /></template>vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('gui-') } },});import type {} from '@golemui/gui-components/vue';The page's stylesheet styles the gui-* elements, so it only reaches them outside a shadow root. Render the host component into the light DOM too, as below, or load the stylesheet into its shadow root.
import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const speeds = ['Standard', 'Express', 'Overnight'];
@customElement('delivery-field')export class DeliveryField extends LitElement { @state() private delivery = '';
protected override createRenderRoot() { return this; }
override render() { return html` <gui-radiogroup label="Delivery" name="delivery" .options=${speeds} .value=${this.delivery} @gui-change=${(event: CustomEvent) => (this.delivery = event.detail.value)} ></gui-radiogroup> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-radiogroup label="Delivery" name="delivery"></gui-radiogroup>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const element = document.querySelector('gui-radiogroup');element.options = ['Standard', 'Express', 'Overnight'];
element.addEventListener('gui-change', (event) => { console.log(event.detail.value);});In HTML, options can also be a JSON attribute, as in the examples below.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
localeId
attribute locale-id | string | 'en' | BCP 47 locale for formatting and parsing, such as en-US or es. |
value | OptionValue | – | The value of the selected option. |
options | OptionInput[] | [] | The options: values, which are both the text and the value, { label, value } objects, or any objects read through label-field and value-field. |
labelField
attribute label-field | string | – | For options given as objects, the key of the text to show. |
valueField
attribute value-field | string | – | For options given as objects, the key of the value. |
direction | 'row' | 'column' | 'column' | Lays the options out in a row or a column. |
uid | string | – | The id of the element's control. The ids of its label, hint and errors derive from it. Optional: when it is not set, the element generates one from its tag name and a page-wide counter, e.g. gui-textinput-3. Generated ids depend on creation order, so a server-rendered element must set uid or its ids will not match after hydration. |
Form control properties
Every field has these, from the base class of the form controls.
| Property | Type | Default | Description |
|---|---|---|---|
name | string | – | The name the value is submitted under. Without it the element stays out of its form. Reflected, like a native control's: the form reads a single value's name from the attribute. |
label | string | – | The visible label, which also names the control for assistive technology. Without one the control has no accessible name; the date, time, range, tags and file fields warn about it in development builds. |
hint | string | – | Help text shown under the label and announced as the control's description. |
errors | string[] | [] | Error messages to show, typically from your own validation. They show as soon as they are set, unless touched is false. |
touched | boolean | – | Whether the user has interacted with the control. Leave it unset unless you validate on interaction: false holds errors back until it becomes true. |
required | boolean | false | The control needs a value: it is marked as required and, with a name, blocks submission. |
readOnly
attribute readonly | boolean | – | The value can be read and focused but not changed. |
disabled | boolean | – | Disabled by its own attribute or by a disabled <fieldset> around it. Setting the property sets the attribute, like a native control's. |
form read-only, no attribute | HTMLFormElement | null | – | The <form> the element belongs to, if any. |
validity read-only, no attribute | ValidityState | – | The element's validity, like a native control's. |
validationMessage read-only, no attribute | string | – | The message the browser shows for an invalid value. |
Methods
| Method | Description |
|---|---|
checkValidity(): boolean | – |
reportValidity(): boolean | – |
Events
Every event bubbles and is composed, so you can listen on any ancestor.
| Event | Description |
|---|---|
gui-input | The user changed the value. detail.value is the new value. |
gui-change | The user committed the value. detail.value is the committed value. |
gui-blur | Focus left the control. |
Examples
Section titled “Examples”Options
Section titled “Options”options takes the list of options. As an attribute it is a JSON array; from JavaScript, set the
options property to an array. Each option is a string or a number, which is both its text and
its value, as above, or a { label, value } object, to show a text other than the value.
value is the value of the selected option. Leave it unset to start with none selected.
import { GuiRadiogroup } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const plans = [ { label: 'Free, for one person', value: 'free' }, { label: 'Team, up to 20 people', value: 'team' }, { label: 'Enterprise, with single sign-on', value: 'enterprise' },];
export function RadioGroupObjects() { return <GuiRadiogroup label="Plan" value="team" options={plans} />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/radiogroup';
@Component({ selector: 'app-radio-group-objects', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-radiogroup label="Plan" value="team" [options]="plans" ></gui-radiogroup> `,})export class RadioGroupObjectsComponent { plans = [ { label: 'Free, for one person', value: 'free' }, { label: 'Team, up to 20 people', value: 'team' }, { label: 'Enterprise, with single sign-on', value: 'enterprise' }, ];}<script setup lang="ts">import type { Option } from '@golemui/gui-components';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const plans: Option[] = [ { label: 'Free, for one person', value: 'free' }, { label: 'Team, up to 20 people', value: 'team' }, { label: 'Enterprise, with single sign-on', value: 'enterprise' },];</script>
<template> <gui-radiogroup label="Plan" value="team" :options.prop="plans" ></gui-radiogroup></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const plans = [ { label: 'Free, for one person', value: 'free' }, { label: 'Team, up to 20 people', value: 'team' }, { label: 'Enterprise, with single sign-on', value: 'enterprise' },];
@customElement('radio-group-objects')export class RadioGroupObjects extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-radiogroup label="Plan" value="team" .options=${plans} ></gui-radiogroup> `; }}<gui-radiogroup label="Plan" value="team" options='[ { "label": "Free, for one person", "value": "free" }, { "label": "Team, up to 20 people", "value": "team" }, { "label": "Enterprise, with single sign-on", "value": "enterprise" } ]'></gui-radiogroup>Options from your data
Section titled “Options from your data”When your options are objects with other keys, such as records from an API, label-field names
the key of the text and value-field the key of the value. You don’t need to map them first.
import { GuiRadiogroup } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const airports = [ { city: 'Lisbon', code: 'LIS' }, { city: 'Porto', code: 'OPO' }, { city: 'Faro', code: 'FAO' },];
export function RadioGroupFields() { return ( <GuiRadiogroup label="Departure airport" labelField="city" valueField="code" options={airports} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/radiogroup';
@Component({ selector: 'app-radio-group-fields', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-radiogroup label="Departure airport" label-field="city" value-field="code" [options]="airports" ></gui-radiogroup> `,})export class RadioGroupFieldsComponent { airports = [ { city: 'Lisbon', code: 'LIS' }, { city: 'Porto', code: 'OPO' }, { city: 'Faro', code: 'FAO' }, ];}<script setup lang="ts">import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const airports = [ { city: 'Lisbon', code: 'LIS' }, { city: 'Porto', code: 'OPO' }, { city: 'Faro', code: 'FAO' },];</script>
<template> <gui-radiogroup label="Departure airport" label-field="city" value-field="code" :options.prop="airports" ></gui-radiogroup></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const airports = [ { city: 'Lisbon', code: 'LIS' }, { city: 'Porto', code: 'OPO' }, { city: 'Faro', code: 'FAO' },];
@customElement('radio-group-fields')export class RadioGroupFields extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-radiogroup label="Departure airport" label-field="city" value-field="code" .options=${airports} ></gui-radiogroup> `; }}<gui-radiogroup label="Departure airport" label-field="city" value-field="code" options='[ { "city": "Lisbon", "code": "LIS" }, { "city": "Porto", "code": "OPO" }, { "city": "Faro", "code": "FAO" } ]'></gui-radiogroup>Number values
Section titled “Number values”Option values can be numbers. An attribute is always text, though, so value="2" selects no
number option: set the value property instead, as here. The value the element reports keeps the
option’s type.
import { GuiRadiogroup } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const seats = [1, 2, 4, 8];
export function RadioGroupNumbers() { return ( <GuiRadiogroup id="seats-radio" label="Seats" direction="row" options={seats} value={2} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/radiogroup';
@Component({ selector: 'app-radio-group-numbers', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-radiogroup id="seats-radio" label="Seats" direction="row" [options]="seatCounts" [value]="seats" ></gui-radiogroup> `,})export class RadioGroupNumbersComponent { seatCounts = [1, 2, 4, 8]; seats = 2;}<script setup lang="ts">import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const seats = [1, 2, 4, 8];</script>
<template> <gui-radiogroup id="seats-radio" label="Seats" direction="row" :options.prop="seats" :value="2" ></gui-radiogroup></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const seats = [1, 2, 4, 8];
@customElement('radio-group-numbers')export class RadioGroupNumbers extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-radiogroup id="seats-radio" label="Seats" direction="row" .options=${seats} .value=${2} ></gui-radiogroup> `; }}<gui-radiogroup id="seats-radio" label="Seats" direction="row"></gui-radiogroup>
<script type="module"> const seats = document.querySelector('#seats-radio');
seats.options = [1, 2, 4, 8]; seats.value = 2;</script>In a row
Section titled “In a row”The options stack in a column. direction="row" lays them out side by side, for a few short
options. Inside an element with the gui-container class, a row stacks back into a column below
480px wide: see Grid.
import { GuiRadiogroup } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const sizes = ['S', 'M', 'L', 'XL'];
export function RadioGroupRow() { return ( <GuiRadiogroup label="Size" direction="row" value="M" options={sizes} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/radiogroup';
@Component({ selector: 'app-radio-group-row', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-radiogroup label="Size" direction="row" value="M" [options]="sizes" ></gui-radiogroup> `,})export class RadioGroupRowComponent { sizes = ['S', 'M', 'L', 'XL'];}<script setup lang="ts">import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const sizes = ['S', 'M', 'L', 'XL'];</script>
<template> <gui-radiogroup label="Size" direction="row" value="M" :options.prop="sizes" ></gui-radiogroup></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const sizes = ['S', 'M', 'L', 'XL'];
@customElement('radio-group-row')export class RadioGroupRow extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-radiogroup label="Size" direction="row" value="M" .options=${sizes} ></gui-radiogroup> `; }}<gui-radiogroup label="Size" direction="row" value="M" options='["S", "M", "L", "XL"]'></gui-radiogroup>Disabled and read-only
Section titled “Disabled and read-only”A disabled group can’t be focused or changed, and a form doesn’t submit it. A readonly group
can be focused, but clicks and the arrow keys don’t change the selected option, and a form
submits its value.
import { GuiRadiogroup } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function RadioGroupDisabledReadonly() { return ( <div className="gui-grid gui-grid--row"> <div className="gui-grid__cell"> <GuiRadiogroup label="Billing" value="Yearly" options={['Monthly', 'Yearly']} disabled /> </div> <div className="gui-grid__cell"> <GuiRadiogroup label="Region" value="Europe" options={['Europe', 'Americas']} readOnly /> </div> </div> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/radiogroup';
@Component({ selector: 'app-radio-group-disabled-readonly', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-radiogroup label="Billing" value="Yearly" [options]="billings" disabled ></gui-radiogroup> </div> <div class="gui-grid__cell"> <gui-radiogroup label="Region" value="Europe" [options]="regions" readonly ></gui-radiogroup> </div> </div> `,})export class RadioGroupDisabledReadonlyComponent { billings = ['Monthly', 'Yearly']; regions = ['Europe', 'Americas'];}<script setup lang="ts">import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const billing = ['Monthly', 'Yearly'];const regions = ['Europe', 'Americas'];</script>
<template> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-radiogroup label="Billing" value="Yearly" :options.prop="billing" :disabled="true" ></gui-radiogroup> </div> <div class="gui-grid__cell"> <gui-radiogroup label="Region" value="Europe" :options.prop="regions" :readOnly="true" ></gui-radiogroup> </div> </div></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const billings = ['Monthly', 'Yearly'];const regions = ['Europe', 'Americas'];
@customElement('radio-group-disabled-readonly')export class RadioGroupDisabledReadonly extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-radiogroup label="Billing" value="Yearly" .options=${billings} disabled ></gui-radiogroup> </div> <div class="gui-grid__cell"> <gui-radiogroup label="Region" value="Europe" .options=${regions} readonly ></gui-radiogroup> </div> </div> `; }}<div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-radiogroup label="Billing" value="Yearly" options='["Monthly", "Yearly"]' disabled ></gui-radiogroup> </div> <div class="gui-grid__cell"> <gui-radiogroup label="Region" value="Europe" options='["Europe", "Americas"]' readonly ></gui-radiogroup> </div></div>In a form
Section titled “In a form”With a name, <gui-radiogroup> submits the value of the selected option under that name, and
nothing while none is selected. required blocks the submit until the user picks one. Submit the
form below, then pick an option and submit again.
import { useState, type SubmitEvent } from 'react';import { GuiButton, GuiRadiogroup } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const sources = [ { label: 'A friend', value: 'friend' }, { label: 'A search engine', value: 'search' }, { label: 'A conference talk', value: 'talk' },];
export function RadioGroupNativeForm() { const [result, setResult] = useState('');
const onSubmit = (event: SubmitEvent<HTMLFormElement>) => { event.preventDefault(); const data = Object.fromEntries(new FormData(event.currentTarget)); setResult(`Submitted ${JSON.stringify(data)}`); };
return ( <form id="survey" onSubmit={onSubmit}> <div className="gui-grid"> <div className="gui-grid__cell"> <GuiRadiogroup label="How did you hear about us?" name="source" required options={sources} /> </div> <div className="gui-grid__cell"> <GuiButton type="submit" label="Send" /> </div> </div> <p id="survey-result" role="status"> {result} </p> </form> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import '@golemui/gui-components/button';import '@golemui/gui-components/radiogroup';
@Component({ selector: 'app-radio-group-native-form', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <form id="survey" #form (submit)="onSubmit($event, form)"> <div class="gui-grid"> <div class="gui-grid__cell"> <gui-radiogroup label="How did you hear about us?" name="source" required [options]="sources" ></gui-radiogroup> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Send"></gui-button> </div> </div> <p id="survey-result" role="status">{{ result() }}</p> </form> `,})export class RadioGroupNativeFormComponent { sources = [ { label: 'A friend', value: 'friend' }, { label: 'A search engine', value: 'search' }, { label: 'A conference talk', value: 'talk' }, ];
result = signal('');
onSubmit(event: SubmitEvent, form: HTMLFormElement) { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); this.result.set(`Submitted ${JSON.stringify(data)}`); }}<script setup lang="ts">import { ref } from 'vue';import type { Option } from '@golemui/gui-components';import '@golemui/gui-components/button';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const sources: Option[] = [ { label: 'A friend', value: 'friend' }, { label: 'A search engine', value: 'search' }, { label: 'A conference talk', value: 'talk' },];
const result = ref('');
function onSubmit(event: SubmitEvent) { if (!(event.currentTarget instanceof HTMLFormElement)) return; const data = Object.fromEntries(new FormData(event.currentTarget)); result.value = `Submitted ${JSON.stringify(data)}`;}</script>
<template> <form id="survey" @submit.prevent="onSubmit"> <div class="gui-grid"> <div class="gui-grid__cell"> <gui-radiogroup label="How did you hear about us?" name="source" :required="true" :options.prop="sources" ></gui-radiogroup> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Send"></gui-button> </div> </div> <p id="survey-result" role="status">{{ result }}</p> </form></template>import { LitElement, html } from 'lit';import { customElement, query, state } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/radiogroup';import '@golemui/gui-components/index.css';
const sources = [ { label: 'A friend', value: 'friend' }, { label: 'A search engine', value: 'search' }, { label: 'A conference talk', value: 'talk' },];
@customElement('radio-group-native-form')export class RadioGroupNativeForm extends LitElement { @query('#survey') private form!: HTMLFormElement; @state() private result = '';
protected override createRenderRoot() { return this; }
private onSubmit(event: SubmitEvent) { event.preventDefault(); const data = Object.fromEntries(new FormData(this.form)); this.result = `Submitted ${JSON.stringify(data)}`; }
override render() { return html` <form id="survey" @submit=${this.onSubmit}> <div class="gui-grid"> <div class="gui-grid__cell"> <gui-radiogroup label="How did you hear about us?" name="source" required .options=${sources} ></gui-radiogroup> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Send"></gui-button> </div> </div> <p id="survey-result" role="status">${this.result}</p> </form> `; }}<form id="survey"> <div class="gui-grid"> <div class="gui-grid__cell"> <gui-radiogroup label="How did you hear about us?" name="source" required options='[ { "label": "A friend", "value": "friend" }, { "label": "A search engine", "value": "search" }, { "label": "A conference talk", "value": "talk" } ]' ></gui-radiogroup> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Send"></gui-button> </div> </div> <p id="survey-result" role="status"></p></form>
<script type="module"> const form = document.querySelector('#survey');
form.addEventListener('submit', (event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); document.querySelector('#survey-result').textContent = `Submitted ${JSON.stringify(data)}`; });</script>The radio buttons inside stay out of the form: the element submits the value itself.
How the elements submit, validate and reset is in Native forms.
Styling
Section titled “Styling”Anatomy
Section titled “Anatomy”<gui-radiogroup> renders into the light DOM, so you can style every part with plain CSS. These
are the parts and their public classes:
<gui-radiogroup class="gui-field"> <span class="gui-label" id="delivery_label"> <span class="gui-label__text"> Delivery<span aria-hidden="true"> *</span> </span> <div class="gui-widget-hint" id="delivery_hint" aria-hidden="true"> Express arrives in two days. </div> </span> <div role="radiogroup" class="gui-widget" id="delivery" aria-labelledby="delivery_label" aria-describedby="delivery_hint" aria-required="true" aria-invalid="true" aria-errormessage="delivery_errors" > <label for="delivery_0"> <input type="radio" tabindex="-1" id="delivery_0" name="delivery" form="delivery_none" value="Standard" aria-describedby="delivery_hint" aria-invalid="true" aria-errormessage="delivery_errors" aria-required="true" /> Standard </label> <label for="delivery_1"> <input type="radio" tabindex="0" id="delivery_1" name="delivery" form="delivery_none" value="Express" aria-describedby="delivery_hint" aria-invalid="true" aria-errormessage="delivery_errors" aria-required="true" /> Express </label> <!-- One label and radio per option. --> </div> <div class="gui-validator" id="delivery_errors" role="alert"> <div class="gui-validator__error">Pick a delivery option.</div> </div></gui-radiogroup>| Class | Part |
|---|---|
gui-field | The element itself. Lines its parts up with the fields next to it in a gui-grid row. |
gui-label | The group’s label, with the text and the hint. |
gui-label__text | The label text and the required marker. |
gui-widget-hint | The hint. |
gui-widget | The group, with a <label> and a radio button per option. |
gui-widget--horizontal | On the group with direction="row": lays the options out in a row. |
gui-validator | The errors container. It is always there, so the alert role announces new errors. |
gui-validator__error | One error message. |
The options have no class of their own: select them as gui-radiogroup label and
gui-radiogroup input. The ids derive from uid, or from a generated id such as
gui-radiogroup-3 when you don’t set one. Select by class or by tag, not by id.
Tokens
Section titled “Tokens”The group reads these design tokens. Set them on :root to change every group, or on any element
to change the groups inside it:
| Token | What it styles |
|---|---|
--gui-bg-surface | The radio buttons’ background. |
--gui-border-default | The radio buttons’ border. |
--gui-intent-primary | The dot and the border of the selected radio button. |
--gui-text-default | The label and the options’ text. |
--gui-font-family | The font. |
--gui-font-sm | The text size of the label and the options. |
--gui-font-xs | The text size of the hint and the errors. |
--gui-text-secondary | The hint. |
--gui-border-focus | The border of the focused radio button. |
--gui-shadow-focus | The focus ring. |
--gui-intent-error | The radio buttons’ border and the messages when there are errors. |
--gui-shadow-focus-error | The focus ring when there are errors. |
--gui-space-1 | The space between the label, hint and errors. |
/* More room between the options of every group. */gui-radiogroup .gui-widget { row-gap: 0.75rem;}To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.
Accessibility
Section titled “Accessibility”- Group. The options sit in an element with the
radiogrouprole, named by thelabelthrougharia-labelledby. Without a label the group has no accessible name. Each radio button is named by its option’s text. - Description. The
hintis the group’s description, througharia-describedby. - Errors. While errors show, the group and its radio buttons have
aria-invalid="true"and point at them witharia-errormessage, and screen readers announce new errors as they appear. - Required and read-only.
requiredsetsaria-required, andreadonlysetsaria-readonlyon the group, which radio buttons can’t carry themselves. - One tab stop. The group is a single stop in the tab order: Tab lands on the selected option, or on the first one when none is selected.
| Key | Action |
|---|---|
| Tab | Moves focus into the group, to the selected option, and out of it. |
| ↓ → | Selects the next option, and fires gui-change. |
| ↑ ← | Selects the previous option, and fires gui-change. |
| Space | Selects the focused option, when none is selected yet. |
In a read-only group the arrow keys and Space do nothing.
What every element shares, such as translating the browser messages, is in Accessibility.