Skip to content

radio — Radio

The semantics of a radio is “only one in a group is active.” core/radio abstracts it into the simplest possible check: does the current group value modelValue equal my value? Unlike checkbox’s set operations, radio is a single-value comparison.

  • Each port implements LhRadio and LhRadioGroup (the group holds modelValue; each item derives its own selected state).
  • Mutually exclusive selection: gender, payment method, tab-style form branches.
  • Should not use it: multi-select uses checkbox; a toggle uses switch; segmented switching uses segmented (visually lighter).
import { isRadioChecked, getRadioClassNames } from '@lh-ui/share/core/radio'
import type { LhRadioState } from '@lh-ui/share/core/radio'

isRadioChecked uses Object.is(modelValue, value) rather than ===, for the same reason as checkbox: consistent set semantics for NaN / -0, and “values of any type can serve as a radio key.”

Radio needs no toggle function — selecting is assignment (modelValue = value); this is the mathematical difference between single-select and multi-select (assignment of one variable vs. addition/removal in a set).

Function Signature Returns Purpose
isRadioChecked (state: LhRadioState) => boolean boolean Object.is(modelValue, value)
getRadioClassNames (type: string) => string[] class name array lh-radio + type modifier
getRadioClassNames('primary') // ['lh-radio', 'lh-radio--primary']
getRadioClassNames('') // ['lh-radio']
export interface LhRadioState {
modelValue: unknown // the group's current value
value: unknown // this radio's value
checked: boolean
}

Same reasoning as checkbox: use the native <input type="radio"> — semantics, keyboard support, and form submission all come for free. The radio’s name attribute is key — radios in the same group must share a name; the browser’s arrow-key navigation (moving between radios in the same group) depends on it.

// Screen readers announce the label text plus the "selected/unselected" state — the native radio covers all of it
import { isRadioChecked, getRadioClassNames } from '@lh-ui/share/core/radio'
isRadioChecked({ modelValue: 'b', value: 'b', checked: false }) // true
isRadioChecked({ modelValue: 'b', value: 'a', checked: false }) // false
<!-- Vue3 -->
<div class="lh-radio-group" :class="`lh-radio-group--${size}`">
<label
v-for="opt in options"
:key="String(opt.value)"
:class="getRadioClassNames(type)"
:class-mod="{ 'lh-radio--checked': isRadioChecked({ modelValue, value: opt.value, checked: false }) }"
>
<span class="lh-radio__box">
<input
class="lh-radio__input"
type="radio"
:name="groupName"
:checked="modelValue === opt.value"
@change="emit('update:modelValue', opt.value)"
/>
</span>
<span class="lh-radio__label">{{ opt.label }}</span>
</label>
</div>
// React
const [value, setValue] = useState('a')
return options.map((opt) => (
<label key={opt.value} className="lh-radio">
<input
type="radio"
name={groupName}
checked={value === opt.value}
onChange={() => setValue(opt.value)}
/>
<span className="lh-radio__label">{opt.label}</span>
</label>
))
// uniapp: <radio-group> + <radio> are built-in components, and the change event gives the
// selected item's value directly; note that uniapp's radio value only supports strings
Class Semantics
.lh-radio Base class
.lh-radio--checked / --disabled States
.lh-radio--{small|large} Size tiers
.lh-radio__box Custom radio circle (overrides the native appearance)
.lh-radio__input Native input (visually hidden, semantics preserved)
.lh-radio__label Text
.lh-radio-group / --{small|large} Group container

The selected state’s inner dot uses var(--lh-primary-color); the disabled state uses --lh-text-disabled.

  • The type parameter and checkbox-group sizing are two different things: radio’s getRadioClassNames(type) emits a type modifier (like primary); the size modifier lives on the group (lh-radio-group--{size}).
  • name must be consistent within a group: otherwise the browser’s keyboard navigation (arrow keys between group members) breaks, and form submission would submit multiple values.
  • The initial value of modelValue: it should be one of the options’ values, or undefined (nothing selected). Giving it a value that is not among the options leaves all radios unselected, and it cannot be recovered through the UI.
  • Both radio and segmented can do mutually exclusive switching: radio carries “form control” semantics (native, submittable), while segmented carries “view switching” semantics (lighter, more like a button group).
@lh-ui/share/core/radio → getRadioClassNames( )
返回值
(填好参数后点运行)

Copyright © 2024-2026 FengMo WenXue