RestructureNeeds Refinement
Radio Button Component link

A circular radio control used inside single-select groups; supports default, selected, and disabled.

Split properties + rebuild as vector
Replace the sparse selected × style matrix with orthogonal props: selected: Bool + state: default/disabled/error. Retire the checkmark style (it's a checkbox affordance, not a radio). Rebuild the large radio with token-bound vector layers instead of raster SVG images. Rename .base/checkbox.base/radio. Add pressed + focused states.
In Context

Radio Buttons appear in Radio Button with Label groups — see the Radio Button with Label preview for the composed form row.

Live Preview
Properties
selected
size
style
DS Health
Reusable
Pass
Used in forms, surveys, preference pickers. Two sizes cover 360px and 414px screen needs.
Self-contained
Warn
Small variants use token-bound borders + fills. Large variants export a pre-rendered SVG image for each state — tokens won't propagate to the large size. C3
Consistent
Warn
Two property-naming issues: selected mixes selection with modifiers (disabled/error), and style is conditional (only meaningful when selected is true). Sparse matrix with ~50% invalid combinations. C2
Composable
Warn
Internal frame is named .base/checkbox instead of .base/radio. Suggests checkbox primitives were reused here. Also the checkmark style is a checkbox affordance, not standard radio iconography. C6
Open Issues
  • Sparse variant matrix. selected × size × style = 24 theoretical, ~11 valid. The style property is only meaningful when a selection is present, and selected conflates selection with modifier states. C2 · Variant & Property Naming
  • Large radio is raster-baked. Every large variant exports the ring+dot as a pre-rendered SVG image (imgContainer). Token changes won't propagate to the large size until this is rebuilt with layered vectors. C3 · Token Coverage
  • Internal frame named .base/checkbox. Misleading layer naming — the small radio nests a frame called .base/checkbox instead of .base/radio. C6 · Asset & Icon Quality
  • Checkmark style is not standard radio iconography. Radios use filled dots universally; checkmarks communicate "checked" — a checkbox affordance. The style=checkmark variant visually overlaps with Checkbox. C6 · Asset & Icon Quality
  • No pressed or focused states. Engineers must improvise these affordances. C5 · Interaction State Coverage
  • Code Connect mappings not registered. Blocked until property split (selected/state/size) and large-radio vector rebuild land. C7 · Code Connect Linkability
Design Recommendations
  • Split properties into orthogonal axes :
    selected: Bool (true/false) — pure selection state
    state: default / disabled / error — modifier state (can combine with selected)
    size: small / large — unchanged
    Eliminates invalid combinations, maps to Swift Bool and native radio APIs. Property
  • Retire the checkmark style. Pick filled (blue dot) as the single visual style — it's the universally understood radio affordance. The checkmark variant is visually a checkbox and may cause user confusion when placed next to actual checkboxes. Rename
  • Rebuild the large radio as vector layers. Each variant today exports a flat SVG image; convert to a base ring + inner dot, both with token-bound fills. Matches the small radio's structure and lets tokens flow to both sizes. Asset
  • Rename internal frame .base/checkbox.base/radio. Minor but signals correct primitive ownership. Rename
  • Add pressed + focused states. Pressed = darker blue ring/fill; focused = outer 2px focus ring. Documents the interactive affordances native needs to render. State
Styles
All states — large + small
DES DEV

Pick a state, size, and style to preview. The same matrix would otherwise be 12 static cells; here it's controlled.

Properties
selected
size
style
Properties
Variant All states — large + small
Selected true/false (boolean)
Colors
Border #D7E0EF
Fill #005CE5
Dot #FFFFFF
Error border #D61B2C
Layout
Outer ring 20 × 20
Inner dot 10 × 10 (when selected)
Border radius 50% (circle)
Border 1.5px solid
Typography
Inline label Primary/Multi-line Label/Light/Small
Label font Proxima Soft Semibold · 14 / 16
Colors by State
Role Token TokenValue
Unselected border main/radio-button/color/default/unselected/border #D7E0EF
Selected bg (fill + ring) main/radio-button/color/default/selected/bg #005CE5
border main/radio-button/color/default/selected/border #005CE5
inner dot / checkmark main/radio-button/color/default/selected/icon #FFFFFF
Disabled bg main/radio-button/color/disabled/selected/bg #C2CFE5
border main/radio-button/color/disabled/selected/border #C2CFE5
inner icon main/radio-button/color/disabled/selected/icon #FFFFFF
Error border (unselected) main/radio-button/color/error/unselected/border #D61B2C
bg (selected) main/radio-button/color/error/selected/bg #D61B2C
border (selected) main/radio-button/color/error/selected/border #D61B2C
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android — Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:radio:1.0.0")
}
Property Mapping
Figma PropertySwiftUICompose
selected=unselected/selected selected: Bool selected: Bool
selected=disabled state=disabled (combine w/ selected) .disabled(true)
selected=error state=error state: .error
size=small/large size: EBRadioSize .controlSize(.small)
style=filled/checkmark (retire — pick filled only)
style=default (unselected/error) implicit from selected=false
onToggle onChange: (Bool) -> Void
SwiftUI
ios/Components/Radio/EBRadioButton.swift
Jetpack Compose
android/components/radio/EBRadioButton.kt
Usage Snippets Planned API
Usage
// Basic selection
EBRadioButton(selected: $isSelected)

// Large size with error state
EBRadioButton(selected: $isSelected, state: .error)
    .controlSize(.large)

// Disabled
EBRadioButton(selected: true)
    .disabled(true)
// Basic selection
EBRadioButton(selected = checked, onCheckedChange = { checked = it })

// Large size with error state
EBRadioButton(
    selected = checked,
    onCheckedChange = { checked = it },
    size = EBRadioSize.Large,
    state = EBRadioState.Error
)

// Disabled
EBRadioButton(
    selected = true,
    onCheckedChange = { },
    enabled = false
)
Accessibility
RequirementiOSAndroid
Role Inherit radio semantics via Toggle(isOn:) with radio style Use Modifier.selectable(role = Role.RadioButton)
Selected state .accessibilityAddTraits(.isSelected) selected = true in semantics
Group label Wrap options in a .accessibilityElement(children: .contain) with group label Use Modifier.selectableGroup() on parent
Tap target Radio is 20px; wrap in 44pt hit area Wrap in 48dp hit area
Error announcement Pair with a label and announce the error message after the label Use semantics { error(...) }
Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Needs Refinement Internal frame named .base/checkbox instead of .base/radio.
C2 Variant & Property Naming Requires Rework selected mixes selection with modifiers; style is conditional.
C3 Token Coverage Requires Rework Large variants are raster — tokens don't flow to the large size.
C4 Native Mappability Ready Maps to Toggle / RadioButton with custom style.
C5 Interaction State Coverage Requires Rework No pressed or focused states.
C6 Asset & Icon Quality Requires Rework Checkmark style conflicts with Checkbox visually; large radio is a pre-rendered image.
C7 Code Connect Linkability Needs Refinement Blocked by C2. Clean mapping lands after prop split.
Variants Inventory (11 total)

After the proposed restructure: 2 selected × 3 state × 2 size = 12 well-formed orthogonal variants (no invalid combinations possible).

selectedsizestyleNode ID
unselectedlargedefault18482:35699
unselectedsmalldefault18482:35702
selectedlargefilled18482:35715
selectedsmallfilled18482:35718
selectedlargecheckmark18482:35721
selectedsmallcheckmark18482:35724
disabledlargefilled18482:35704
disabledsmallfilled18482:35707
disabledlargecheckmark18482:35710
disabledsmallcheckmark18482:35713
errorlargedefault18482:35726
1.0.0 — April 2026Major
Initial Assessment · node 18482:35698
Component assessed — 11 variants across sparse selected × size × style matrix. Documented
Initial
Sparse matrix, mixed paradigmsselected mixes selection with modifier states; style is only meaningful when selected. Open
C2 Open
Large radio is raster-baked — tokens don't flow to the large size. Open
C3 Open
Internal frame named .base/checkbox + checkmark style overlaps with Checkbox. Open
C6 Open
No pressed/focused states. Open
C5 Open
Code Connect mappings — Not registered. Open
C7 Open