Skip to content

HyRadio

Usage examples on this page are written for Lit / plain HTML (<hy-radio>). The same component ships as HyRadio in @whitespaceux/harmony-react (native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.

A form-associated radio button component that provides single-choice selection within a group. Supports comprehensive form integration via the ElementInternals API, accessibility features, and customizable styling. Automatically manages mutual exclusivity when radio buttons share the same name attribute.

Key Behavioral Model

Radio buttons implement mutually exclusive selection within groups:

  • Single selection: Checking one radio unchecks others in the same group

  • Click to select: Once selected, clicking again has no effect

  • Keyboard navigation: Space/Enter to select, Arrow keys to move between group members (when inside hy-radio-group)

  • No deselection: Users cannot uncheck a radio button directly (use setChecked(false) programmatically)

  • setChecked(): Programmatically select a radio and uncheck others in group

  • Group management: Automatic handling of name-based grouping

  • Form integration: Works seamlessly with native form submission via ElementInternals

Form Integration

This component implements the Form-Associated Custom Elements API. When used standalone (not inside hy-radio-group), each radio participates in form submission via ElementInternals. When used inside hy-radio-group, the group component handles form submission and individual radios become presentational.

Styling and Theming

The component uses design tokens from your design system:

  • small: 16px radio button, 8px inner dot

  • medium: 20px radio button, 12px inner dot (default)

  • large: 24px radio button, 16px inner dot

  • --hy-background-surface-base: Background color when unchecked

  • --hy-foreground-default-rest: Color of inner dot

  • --hy-foreground-default-rest: Label text color

  • --hy-foreground-subtle-rest: Top label and help text color

  • --hy-foreground-danger-rest: Error variant text color

  • --hy-foreground-default-disabled: Disabled state color

  • Small: Uses --hy-body-sm-* tokens

  • Medium: Uses --hy-body-md-* tokens

  • Large: Uses --hy-body-lg-* tokens

Common Use Cases

Examples

Simple Single Selection (Standalone)

html
<form>
  <hy-radio name="size" value="small" label="Small"></hy-radio>
  <hy-radio name="size" value="medium" label="Medium" state="on"></hy-radio>
  <hy-radio name="size" value="large" label="Large"></hy-radio>
  <button type="submit">Submit</button>
</form>
html
<form>
  <hy-radio-group name="size" label="Select Size" value="medium">
    <hy-radio value="small" label="Small"></hy-radio>
    <hy-radio value="medium" label="Medium"></hy-radio>
    <hy-radio value="large" label="Large"></hy-radio>
  </hy-radio-group>
  <button type="submit">Submit</button>
</form>

Radio with Different Sizes

html
<hy-radio name="size-demo" value="small" label="Small Radio" size="small"> </hy-radio>
<hy-radio name="size-demo" value="medium" label="Medium Radio" size="medium"> </hy-radio>
<hy-radio name="size-demo" value="large" label="Large Radio" size="large"> </hy-radio>

Error State for Validation Feedback

html
<hy-radio
  name="terms"
  value="agree"
  label="I agree to the terms and conditions"
  variant="error"
  help-text="You must agree to continue"
  required
>
</hy-radio>

Using Slots for Rich Content

html
<hy-radio name="plan" value="premium">
  <span slot="top-label">Select Plan</span>
  <span slot="help-text"> <strong>Premium</strong> includes all features </span>
</hy-radio>

Event Handling

Programmatic Methods

Accessibility Features

  • Full keyboard navigation support (Space/Enter to check, Arrow keys via hy-radio-group)
  • Proper ARIA attributes (role, aria-checked, aria-describedby)
  • Screen reader announcements for state changes
  • High contrast mode support with system color keywords
  • Minimum 44x44px touch targets on mobile devices
  • Semantic form integration with native validation via ElementInternals
  • Focus visible indicators (outline only on keyboard focus)

Design Token Requirements

This component requires the following design tokens to be defined:

Size tokens:

  • --hy-radio-size-sm, --hy-radio-size-md, --hy-radio-size-lg
  • --hy-radio-padding-size-sm, --hy-radio-padding-size-md, --hy-radio-padding-size-lg

Color tokens:

  • --hy-background-surface-base
  • --hy-foreground-default-rest, --hy-foreground-subtle-rest
  • --hy-foreground-danger-rest, --hy-foreground-default-disabled

Typography tokens (per size):

  • --hy-body-sm-*, --hy-body-md-*, --hy-body-lg-*

## API

### Properties

| Property | Attribute | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `state` | `state` | `'off' \| 'on'` | `'off'` | The current state of the radio button: 'off' or 'on'. |
| `label` | `label` | `string` | `''` | The text label displayed next to the radio button. |
| `helpText` | `help-text` | `string` | `''` | Help text displayed below the radio button to provide additional context. |
| `topLabel` | `top-label` | `string` | `''` | Label displayed above the radio button, typically used for grouping. |
| `disabled` | `disabled` | `boolean` | `false` | Disables the radio button, preventing user interaction. |
| `required` | `required` | `boolean` | `false` | Marks the radio button as required for form validation. |
| `name` | `name` | `string` | `''` | The name attribute for form submission and grouping radio buttons. |
| `value` | `value` | `string` | `''` | The value attribute for form submission. |
| `variant` | `variant` | `'default' \| 'error'` | `'default'` | The visual variant of the radio button: 'default' or 'error'. |
| `size` | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | The size of the radio button: 'small', 'medium' or 'large'. |
| `ariaDescribedby` | `aria-describedby` | `string` | `''` | Additional element IDs that describe the radio button for accessibility. |
| `autofocus` | `autofocus` | `boolean` | `false` | Marks this radio as the auto-focus target. Read by `hy-dialog` (and other overlay containers) on `show` to choose initial focus. Also focuses on first connect when used outside an overlay, mirroring native HTML `autofocus` but routed through JS so SSR'd HTML doesn't pull focus during hydration. |
| `radioId` | `radio-id` | `string` | `''` | ID for the radio input element. Defaults to auto-generated ID. |
| `helperTextId` | `helper-text-id` | `string` | `''` | ID for the helper text element. Defaults to auto-generated ID. |
| `topLabelId` | `top-label-id` | `string` | `''` | ID for the top label element. Defaults to auto-generated ID. |
| `checked` | `checked` | `boolean` | — | Whether the radio button is in the checked state (convenience property). Setting this uses setChecked() internally to properly manage form state. |
| `form` | — | `HTMLFormElement \| null` | — | Returns the form element this radio button is associated with via ElementInternals. Only applicable when radio is standalone (not in radio-group). |
| `validity` | — | `ValidityState` | — | Returns the ValidityState object for this radio button via ElementInternals. Only applicable when radio is standalone (not in radio-group). |
| `validationMessage` | — | `string` | — | Returns the validation message for this radio button via ElementInternals. Only applicable when radio is standalone (not in radio-group). |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | — | Fired when state changes. Detail: { state: RadioState, checked: boolean, value: string, name: string, reason: 'user' } |
| `input` | — | Standard form input event for compatibility |

### Slots

| Slot | Description |
| --- | --- |
| `top-label` | Content to display above the radio button as a group label |
| `help-text` | Content to display below the radio button as help or error text |

### CSS Parts

| Part | Description |
| --- | --- |
| `base` | The radio button's base wrapper element |

### CSS Custom Properties

| Property | Description |
| --- | --- |
| `--hy-radio-text-transform` | Text transform for radio label |
| `--hy-radio-font-family` | Font family override for radio label |
| `--hy-radio-focus-ring-width` | Focus ring width override |
| `--hy-radio-border-rest` | Ring color of the unchecked radio. Default: `var(--hy-border-default-rest)` (rest stays own-role). |
| `--hy-radio-border-checked` | Ring color of the checked radio. Default: `var(--hy-border-brand-rest)` (checked escalates to brand). |
| `--hy-radio-foreground-rest` | Fill of the checked inner dot. Default: `var(--hy-background-brand-rest)`. |
| `--hy-radio-height-sm` | Outer container min-height for small size variant. Per-component override in the form-control height contract (see `.claude/rules/control-heights.md`). The indicator stays at indicator-size inside; the host floors to form-row baseline (Position A) so radios align with selects/inputs in inline form rows. **Default:** `var(--hy-control-height-sm)` |
| `--hy-radio-height-md` | Outer container min-height for medium size variant. **Default:** `var(--hy-control-height-md)` |
| `--hy-radio-height-lg` | Outer container min-height for large size variant. **Default:** `var(--hy-control-height-lg)` |
| `--hy-radio-hover-duration` | Transition duration for hover and focus state feedback |
| `--hy-radio-hover-easing` | Transition easing for hover and focus state feedback |

## Methods

### checkValidity()

Checks the validity of the radio button against any validation constraints.
Integrates with native HTML form validation via ElementInternals.
Only applicable when radio is standalone (not in radio-group).

```javascript
const radio = document.querySelector('hy-radio[required]');
if (!radio.checkValidity()) {
console.log('Required radio button is not selected');
}

reportValidity()

Checks the validity of the radio button and shows validation messages if invalid. Triggers the browser's built-in validation UI via ElementInternals. Only applicable when radio is standalone (not in radio-group).

javascript
const radio = document.querySelector('hy-radio[required]');
radio.reportValidity(); // Shows native validation message if invalid

focus()

Sets focus on the radio button input element.

javascript
const radio = document.querySelector('hy-radio');
radio.focus(); // Focuses the radio button for keyboard interaction

blur()

Removes focus from the radio button input element.

javascript
const radio = document.querySelector('hy-radio');
radio.blur(); // Removes focus from the radio button

setChecked()

Programmatically sets the checked state of the radio button. When setting to true and radio is standalone (not in radio-group), automatically unchecks other radio buttons in the same group and updates the form value via ElementInternals. When setting to false, clears the form value.

Parameters:

  • checked - Whether the radio button should be checked
javascript
const radio = document.querySelector('hy-radio');
radio.setChecked(true); // Check this radio and uncheck others in group
radio.setChecked(false); // Uncheck this radio

Built with Lit. Documented with VitePress.