<!-- GENERATED by scripts/build-llms.mjs from llms/forms.md — do not edit this file. -->

# `lr-radio-button`

- **Import** `import '@aceshooting/lyra-ui/components/lr-radio-button.js';` (stable tag alias; registers the tag)
- **Class** `LyraRadioButton`, also available unregistered from `@aceshooting/lyra-ui/components/forms/radio/radio-button.class.js`
- **Family** `components/forms/` — see `llms/index.md` for its siblings
- **Status** `stable` since `8.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
- **Deprecations** none
- **Optional peers** none
- **Themeable via** 13 parts, 23 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-radio-button`

The same single-choice control as `lr-radio`, rendered as a button instead of a circle. Mirrors
`sl-radio-button`.

An explicit live `checked` assignment marks the state dirty even if its boolean value is unchanged.
For example, assigning `checked = false` to an already-unchecked radio prevents a later
`defaultChecked = true` from selecting it until form reset. Pristine default propagation and
owning-group normalization retain their existing behavior.

Host `aria-describedby` references resolve onto the internal `role="radio"`, tracking unresolved
IDs, target replacement/removal/reinsertion, reconnect, and document adoption. The same behavior
applies to the button appearance of `lr-radio`.

Deliberately a **subclass of `LyraRadio`**: form association, validity, `form.reset()` restoration
and the whole `lr-radio-group` ownership/roving-focus contract are inherited rather than
reimplemented, so the two can never drift apart. Only the chrome differs. A `lr-radio-group` accepts
either tag and the two can be mixed in one group.

Inside a horizontal `lr-radio-group`, measured adjacent runs of `lr-radio-button` controls merge
their touching borders and retain rounded outer corners. Separated or mixed controls, vertical
layouts, wrapped rows and standalone buttons keep the corners appropriate to their actual layout;
label and hint slots do not determine the segment endpoints.

Standalone button chrome is allocation-safe too: unbroken labels wrap, and start/end (or retained
prefix/suffix) adornments are each capped and truncate rather than widening the containing panel.
The exact-320px story shows that behavior in LTR and RTL.

**Properties and methods:** exactly the same functional surface as `lr-radio`. Its writable fields
are `appearance`, `checked`, `defaultChecked`, `customError`, `disabled`, `name`, `required`, `value`,
`size`, and `pill`. Its effective form/validity state is also inherited: read-only
`effectiveDisabled`, `effectiveRequired`, `form`, `labels`, `validity`, `validationMessage`, and
`willValidate`. The delegated methods are `click()`, `focus()`, `blur()`, `getForm()`,
`checkValidity()`, `reportValidity()`, `setCustomValidity()`, and `resetValidity()`; form reset,
disabled-fieldset, and state-restoration callbacks remain the browser-owned FACE integration behind
those public operations. Its inherited strongly typed `addEventListener()` and
`removeEventListener()` overloads cover the radio event map listed below while retaining the
standard DOM string overloads. The inherited `appearance` remains `'default' | 'button'`; this tag already
renders button chrome in either state, so the property adds no second visual mode. `size` is where this
chrome differs most visibly: the shared ladder drives the button's height (floored at `1.5rem`),
inline padding and font size, so a `size="small"` radio button sits at the same height as a
`size="small"` `lr-button` beside it. `pill` is the one inherited property that does _more_ here
than on a plain `lr-radio` — see the radius note below.

The inherited derived reads `effectiveName` and `effectiveSize` expose the resolved group name and
size used by the button's form and chrome logic.

**Events:** identical to `lr-radio` — a standalone selection emits `input`, `lr-input`, `change`,
then `lr-change` (both aliases carry `{ checked, value }`); an owning `lr-radio-group` emits the
aggregate sequence instead. The internal control's `focus` / `blur` are re-emitted because they do
not cross the shadow boundary. `lr-invalid` (no detail) belongs to a standalone radio button; an aggregate group emits
its own alias.

**Slots:** default (label text), `start` (leading content, typically an icon), and `end` (trailing
content). Shoelace's `prefix` and `suffix` are retained as aliases for `start` and `end`,
respectively; either spelling can be used, and both spellings share one wrapper at each edge.
The leading, label, and trailing wrappers are hidden independently while empty, so missing regions
do not contribute dead flex gaps.
Host `aria-label` is forwarded to the internal radio by attribute presence, including
`aria-label=""`; it is not replaced by the visible default-slot text.

**CSS parts:** `base` / `button` / `control`, `start` / `prefix` (the same leading wrapper),
`label`, and `end` / `suffix` (the same trailing wrapper). The interactive node carries `checked`
and `button--checked` when selected, plus `disabled` under own, group, or fieldset disablement,
because an attribute selector
after `::part()` never matches.

**Themeable custom properties:** `--lr-radio-radius` is the one inherited knob this element really
uses. `lr-radio` gives it a private `--lr-radius-pill` default for its circular indicator; this
subclass changes that private default to `--lr-form-control-radius` — the active `size` tier's
shared corner radius — and `pill` changes it back to `--lr-radius-pill`. An inherited or direct
public value wins. Only the _outer_ corners of an actually contiguous run take it: an
owning horizontal group measures same-line adjacency after layout, then collapses shared borders.
The ordinary group gap, a plain-radio interruption, vertical layout, or a flex wrap starts a new
fully rounded run, and live add/remove/reorder plus LTR/RTL changes are reconciled. Standalone
siblings are never guessed into a run. `--lr-radio-button-gap` (default `var(--lr-space-xs)`) controls the
spacing between the start/prefix wrapper, label, and end/suffix wrapper in both `<lr-radio-button>`
and `<lr-radio appearance="button">` without changing the shared spacing token used elsewhere.
Button paint states can be rethemed without changing shared tokens:
`--lr-radio-button-hover-bg` / `--lr-radio-button-hover-border-color` and
`--lr-radio-button-active-bg` / `--lr-radio-button-active-border-color` control the unchecked
pointer states; `--lr-radio-button-checked-bg`, `--lr-radio-button-checked-border-color`, and
`--lr-radio-button-checked-color` control checked rest; and the corresponding
`--lr-radio-button-checked-hover-bg`, `--lr-radio-button-checked-hover-border-color`,
`--lr-radio-button-checked-active-bg`, and `--lr-radio-button-checked-active-border-color` hooks
control checked pointer states. The inherited `--lr-radio-hover-border-color`,
`--lr-radio-active-border-color`, and `--lr-radio-active-ring-color` remain visible in generated
metadata but apply only to the base radio's circular chrome. All fallbacks preserve the existing
brand, on-brand, quiet, and color-mix treatments.

Because this is a subclass, the manifest also lists `lr-radio`'s own `circle` and `dot` parts and
its `--lr-radio-circle-size`, `--lr-radio-dot-size`, `--lr-radio-label-indent`,
`--lr-radio-checked-border-color` and `--lr-radio-checked-dot-color` custom properties. **This
element renders none of those** — it draws a button, not a circle and dot — so styling them here has
no effect. The same is true of the inherited `checked-icon` and `control--checked` parts and the
`--checked-icon-color` / `--checked-icon-scale` aliases. They are inherited declarations, not
rendered button surface. `--lr-radio-radius` is the exception, and
the only one of the set worth setting on this tag.

```html
<lr-radio-group name="view" label="View" orientation="horizontal">
  <lr-radio-button value="day" checked>Day</lr-radio-button>
  <lr-radio-button value="week">Week</lr-radio-button>
</lr-radio-group>
```

---
