Skip to content

Radio ​

An enhanced native <input type="radio"> supporting group keyboard navigation (radiogroup pattern), data-driven options, and a card variant.

Basic Usage ​

Basic usage

Option oneOption two

Radios sharing a name are mutually exclusive (the component syncs across Shadow DOM boundaries); without a name each stays independent — use a shared name or oas-radio-group for exclusivity.

Sizes ​

Three sizes (size)

small (14px)medium (default, 16px)large (18px)

size controls both the dot size and the font size; set on the group it cascades to all items, while an explicit item size wins.

Radio Group (Keyboard Accessible) ​

Radio group

Payment methodWeChat PayAlipayBank card

The group is controlled via value; each item's value acts as the option identifier, and the group manages exclusivity.

The group follows the WAI-ARIA radiogroup keyboard pattern: Tab enters the group once (stopping at the checked item, or the first non-disabled / non-readonly item when none is checked), ↑ ↓ ← → move and select immediately (wrapping around, skipping disabled items), Home / End jump to the first/last enabled item. Native inputs each live inside their own Shadow DOM, so the browser's same-name arrow grouping breaks across shadow boundaries — the component fills the gap with a host-level roving tabindex and arrow key handling.

Single Disabled ​

Single disabled

SelectableDisabledDisabled & checked

A single item's disabled only disables that item: not clickable, not focusable (native disabled semantics); the rest of the group is unaffected. Disable the whole group via oas-radio-group's disabled (below).

Disabled ​

Disabled

Checked & disabledDisabledDisabled

Readonly ​

Readonly

Readonly group (focusable, arrows do not switch)Option AOption BOption C

readonly and disabled carry distinct form semantics: focusable, reachable via Tab, value still submits, but clicks (and Space) plus arrow keys never switch. Can also be set per item.

Validation Status ​

Validation status (status, per-item supported)

Choose a delivery method (required)Same-day courierStandard expressItem successItem warningItem error

A group-level status cascades to all items; an explicit item status wins over the group value. status="error" automatically sets aria-invalid="true"; in the card variant the status color lands on the border.

Description & Label Position ​

Description (description) and label position (label-position)

Same-day courierStandard expressStore pickupLabel on the start side (label-position="start")

The description attribute renders as secondary text below the label (per-option subtext); a <span slot="description"> can distribute rich content instead (takes priority over the attribute). label-position="start" moves the text to the start side of the dot (mirrors automatically in RTL).

Custom Indicator ​

Custom indicator (checked-icon slot)

Like Native (comparison)

Slot content takes over the checked-state icon (template[slot] or a direct element both work); the unchecked state falls back to a default hollow circle, overridable via ::part(box) (see the next example: both states customized).

Both states customized (checked-icon slot + ::part(box))

Starred (primary filled star) Not starred (muted dashed ring)

The custom indicator is a per-item switch: only items carrying the checked-icon slot enter custom mode (native dot hidden, .box becomes the unchecked carrier) — that is why both items above carry the slot. For a uniform look across a group, every item needs it.

Horizontal Group ​

Horizontal direction (direction)

GenderMaleFemalePrefer not to say

Defaults to direction="vertical" (stacked); horizontal lays items out in a row and wraps automatically.

Data-Driven ​

options data channel

Delivery method

The options JSON attribute and declarative child elements form dual channels: when options is explicit, data-driven rendering wins (rendered internally; declarative children step aside). Fields: label / value / disabled / description. Data-driven items participate in keyboard navigation as usual.

Card Variant ​

Card variant (variant=card)

Payment methodWalletBank cardCash on delivery

variant="card" switches to a card skin: the whole block is clickable, the selected state tints the border, hover gives feedback; put titles in the default slot and use description for subtext. Card items can join a group (driven by the group value, keyboard navigation intact) or stand alone.

Events ​

Events (oas-change / oas-focus / oas-blur)

Option AOption BOption C

An item dispatches oas-change (detail: { checked, value }) plus oas-focus / oas-blur; the group dispatches oas-change (detail: { value }, fired by both clicks and arrow-key selection) and group-level oas-focus / oas-blur (moves between items do not misfire).

required ​

required (native validation chain)

Plan A

required drives the native validation chain (form-associated): when unfilled, checkValidity() returns false (valueMissing) and native form submission is blocked; it recovers to :valid once filled.

required also works at the group level: oas-radio-group required uses the native form-associated validation chain — when no option is selected, valueMissing blocks native form submission (FormData submits the selected value under the group's name; nothing is submitted when empty).

Group-level required (radio-group)

BasicProTeamSubmit (blocked by the browser when nothing is selected)

Click submit without selecting: the browser shows the valueMissing validation bubble, the form is not submitted, and the page asks you to pick a plan first; select an option and submit again to see the submitted value.

The current value is available through the public value property for read/write (mirroring the native radio.value): submission-value semantics — falls back to 'on' when the value attribute is absent, independent of the checked state (whether it is submitted is decided by checked); el.value = 'x' writes the controlled value attribute and refreshes the FormData submission value. No events are dispatched.

API ​

oas-radio ​

Attributes ​

AttributeDescriptionTypeDefault
checkedWhether checkedboolean—
descriptionSecondary text rendered below the label; a slot="description" distribution takes prioritystring—
disabledDisabledboolean—
label-positionLabel position: end (default, dot left / text right) / start (text left; mirrors automatically in RTL)——
nameNative grouping name (same-name exclusivity across Shadow DOM; a group assigns a unique name automatically)string—
readonlyReadonly: focusable and Tab-reachable, value still submits, but clicks (and Space) plus arrow keys never switchboolean—
requiredRequired marker (per-element semantics: this item unchecked means valueMissing; group-level any-checked semantics are a known boundary)boolean—
sizeSize: small (14px) / medium (default 16px) / large (18px), dot and font scale together; a group-level value cascades to items while an explicit item value winsstringmedium
statusValidation status: error / warning / success (tints the dot; error also sets host aria-invalid)string—
valueOption identifierstring | null—
variantVariant: default / card (whole block clickable, selected border tint, hover feedback)string—

Property (JS property; see each row for attribute reflection) ​

PropertyDescriptionTypeDefault
valueThe submitted value (attribute default on; separate from checked, matching native radio.value)string | null—

Events ​

EventDescription
oas-blurThe radio lost focus
oas-changeSelection change, detail: { checked, value }
oas-focusThe radio gained focus

Slots ​

NameDescription
defaultLabel content (rich text/links allowed)
checked-iconCustom checked indicator (template[slot] or a direct element; unchecked falls back to the default hollow circle)
descriptionDescription distribution channel (takes priority over the description attribute)

oas-radio-group ​

Attributes ​

AttributeDescriptionTypeDefault
directionLayout direction: vertical (default) / horizontal (row layout with wrapping)stringvertical
disabledDisable the whole group (cascades to items without overriding their own explicit disabled)boolean—
optionsData channel: JSON [{ label, value, disabled?, description? }]; when explicit, data-driven rendering wins over declarative childrenRadioOption[] | string—
readonlyReadonly (cascades to items): focusable, arrow keys do not switchboolean—
requiredGroup-level required validation via the form-associated chain (valueMissing); reset restores the baselineboolean—
sizeSize (cascades to items): small / medium (default) / largestring—
statusValidation status (cascades to items): error / warning / success; an explicit item status winsstring—
valueGroup value (the selected item value)string—

Events ​

EventDescription
oas-blurFired when focus leaves the group
oas-changeGroup value change, detail: { value } (fired by both clicks and arrow-key selection)
oas-focusAny item inside the group gained focus (moves between items do not misfire)

Slots ​

NameDescription
defaultDeclarative item channel (<oas-radio> children; steps aside when options is explicit)
labelGroup title (rendered into the legend)

Released under the MIT OR Apache-2.0 License.