Basic
Use a radio group when users must pick exactly one option from a small set.
Icons by @ng-icons/tabler-icons
The component library does not provide icons.
Native radio group primitives with descriptions, choice cards, fieldset grouping, reactive forms validation, custom styling, and RTL support.
Preview
TS
import { FrFieldModule } from '@frame-ui-ng/components/field';
import { FrRadioGroupModule } from '@frame-ui-ng/components/radio-group';HTML
<div frRadioGroup aria-label="Notification channel">
<label frRadioGroupField>
<input frRadioGroupItem type="radio" name="channel" value="email" checked />
<span frFieldLabel>Email</span>
</label>
<label frRadioGroupField>
<input frRadioGroupItem type="radio" name="channel" value="slack" />
<span frFieldLabel>Slack</span>
</label>
</div>Use a radio group when users must pick exactly one option from a small set.
Pair radio items with Field content when each option needs supporting explanation.
Use the card variant and card primitives when each radio option should render as a larger selection surface.
Use FieldSet and FieldLegend when a group needs a visible question or section heading.
Disable individual radios when a policy or upstream dependency prevents selecting that value.
Use Angular validation to drive invalid styling and error text while keeping each option a native radio input.
Radio rows use logical spacing and native inputs, so labels and controls follow the document direction.
Scope radio group token overrides to a wrapper when a form section needs a stronger selected state, larger target, or different focus treatment.
Hover the group, field, radio item, content, label, description, or disabled item to inspect the tokens that shape the radio group pattern.
Use these CSS custom properties to tune radio group spacing, radio item sizing, checked state, focus ring, invalid state, and disabled opacity.
SCSS
--frame-radio-group-gap: 0.75rem;
--frame-radio-group-horizontal-gap: 1rem;
--frame-radio-group-card-gap: 0.75rem;
--frame-radio-group-card-padding: 1rem;
--frame-radio-group-card-radius: var(--frame-radius-lg);
--frame-radio-group-card-bg: var(--frame-surface);
--frame-radio-group-card-border: var(--frame-border);
--frame-radio-group-card-hover-border: color-mix(in srgb, var(--frame-border) 70%, var(--frame-foreground));
--frame-radio-group-card-checked-border: var(--frame-primary);
--frame-radio-group-card-checked-shadow: 0 0 0 3px color-mix(in srgb, var(--frame-primary) 14%, transparent);
--frame-radio-group-card-disabled-opacity: 0.55;
--frame-radio-group-card-meta-color: var(--frame-muted-foreground);
--frame-radio-group-card-meta-font-size: 0.875rem;
--frame-radio-group-card-meta-font-weight: 600;
--frame-radio-group-item-size: 1rem;
--frame-radio-group-item-bg: var(--frame-surface);
--frame-radio-group-item-border: var(--frame-border);
--frame-radio-group-item-color: var(--frame-primary);
--frame-radio-group-item-hover-border: color-mix(in srgb, var(--frame-border) 72%, var(--frame-foreground));
--frame-radio-group-item-checked-border: var(--frame-primary);
--frame-radio-group-item-checked-bg: var(--frame-surface);
--frame-radio-group-item-dot-size: 0.5rem;
--frame-radio-group-item-focus-shadow: 0 0 0 3px color-mix(in srgb, var(--frame-ring) 32%, transparent);
--frame-radio-group-item-invalid-border: var(--frame-destructive);
--frame-radio-group-item-disabled-opacity: 0.5;
--frame-radio-group-transition-duration: 150ms;
--frame-radio-group-transition-easing: ease;