FixNeeds Refinement
Radio Button with Label Component link

A form row pairing a Radio Button with a text label. 4 variants across a size property with mixed values: default, large, default - error, large - error. Only the unselected state is documented across sizes.

Split size + state props
Split the size property into size=default/large and isError: Bool. Add disabled and selected variants. Instance-swap (or Figma Slot) the radio so the large label pairs with a large radio. The label component should track the atom's state via a single selected prop forwarded down.
In Context

Contexts are illustrative. Final screens will reference actual GCash patterns. Radio Button with Label appears in form questions and preference settings, stacked vertically as a group.

Continue
Live Preview
Label
Proposed API
size
isError
selected
DS Health
Reusable
Pass
Used for mutually exclusive form choices: payment method, shipping option, preference questions.
Self-contained
Pass
Carries gap, vertical padding, and typography. Body color, font, and indent all token-bound.
Consistent
Warn
size values include "default - error" and "large - error" — space-hyphen-space strings that encode state in a size prop. Breaks native enum mapping. C2
Composable
Warn
Always instances the small radio — even when size=large. The large label doesn't visually scale the radio accordingly. C6
Open Issues
  • size property encodes state. Values include "default - error" and "large - error". Should be two orthogonal props: size = default | large + isError: Bool. C2 · Variant & Property Naming
  • Missing state variants. No disabled or selected variants. Forms need all four selection states (selected/unselected × enabled/disabled) plus the error variant. C5 · Interaction State Coverage
  • Radio is hardcoded to the small instance. Even in size=large variants, the radio uses the 16×16 small atom. Large label should pair with the 20×20 large radio. C6 · Asset & Icon Quality
  • Code Connect mappings not registered. Blocked until the size prop split and missing state variants land. C7 · Code Connect Linkability
Design Recommendations
  • Split the size prop :
    size: default / large
    isError: Bool
    selected: Bool
    disabled: Bool (or unified state enum)
    Flat orthogonal props — eliminates the compound string values. Property
  • Pair radio size to label sizesize=default → small radio (16 × 16); size=large → large radio (20 × 20). The current always-small behavior breaks visual hierarchy. Composition
  • Adopt a Figma Slot for the radio — lets consumers swap in a Radio Button with any state (selected/disabled/error/etc.) from the atom component. Maps to @ViewBuilder / @Composable slots. Slot
  • Add disabled + selected variants — forms need all state combinations documented. State
  • Make the whole row tappable — labels should be tap targets, not just the radio. Document in Accessibility. A11y
Styles
Default
DES DEV

Small radio + 14 / 16 label. Default unselected state.

Label
Properties
size
isError
selected
Properties
Variant Default
State Default
Colors
Border #D7E0EF
Fill #005CE5
Label #445C85
Error border #D61B2C
Layout
Outer ring 24 × 24
Inner dot 12 × 12
Padding 12 vertical · 0 horizontal
Gap (radio ↔ label) 12px
Typography
Label style Primary/Multi-line Label/Light/Base
Label font Proxima Soft Semibold · 16 / 20 · +0.25
Typography
Role Token Spec
default Primary/Multi-line Label/Light/Small Proxima Soft Semibold · 14 / 16 · +0.25
large Primary/Multi-line Label/Light/Base Proxima Soft Semibold · 16 / 20 · +0.25
Large
DES DEV

16 / 20 label (Proxima Soft Semibold). Still uses the 16 × 16 radio — should be 20 × 20.

Label
Properties
size
isError
selected
Properties
Variant Large
State Default
Colors
Border #D7E0EF
Fill #005CE5
Label #445C85
Error border #D61B2C
Layout
Outer ring 24 × 24
Inner dot 12 × 12
Padding 12 vertical · 0 horizontal
Gap (radio ↔ label) 12px
Typography
Label style Primary/Multi-line Label/Light/Base
Label font Proxima Soft Semibold · 16 / 20 · +0.25
Layout
Role Token Value
Radio → label gap space/space-12 12px
Radio icon offset padding (top) space/space-4 4px
Text container padding (default) space/space-4 4px vertical
Text container padding (large) 3t / 5b
Default width (demo instance) 63px
Large width (demo instance) 69px
Default — error
DES DEV

Default size with red radio border. Label text color unchanged.

Label
Properties
size
isError
selected
Properties
Variant Default — error
State Error
Colors
Border #D7E0EF
Fill #005CE5
Label #445C85
Error border #D61B2C
Layout
Outer ring 24 × 24
Inner dot 12 × 12
Padding 12 vertical · 0 horizontal
Gap (radio ↔ label) 12px
Typography
Label style Primary/Multi-line Label/Light/Base
Label font Proxima Soft Semibold · 16 / 20 · +0.25
Large — error
DES DEV

Large size with red radio border.

Label
Properties
size
isError
selected
Properties
Variant Large — error
State Error
Colors
Border #D7E0EF
Fill #005CE5
Label #445C85
Error border #D61B2C
Layout
Outer ring 24 × 24
Inner dot 12 × 12
Padding 12 vertical · 0 horizontal
Gap (radio ↔ label) 12px
Typography
Label style Primary/Multi-line Label/Light/Base
Label font Proxima Soft Semibold · 16 / 20 · +0.25
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
size=default / large size: EBRadioSize .controlSize(.small/.large)
"- error" suffix isError: Bool .ebError(true)
selected: Bool selected: Bool
label: String title: String
disabled: Bool .disabled(true)
onToggle onChange: (Bool) -> Void
SwiftUI
ios/Components/Radio/EBRadioButtonWithLabel.swift
Jetpack Compose
android/components/radio/EBRadioButtonWithLabel.kt
Usage Snippets Planned API
Usage
// Default row
EBRadioButtonWithLabel("Pay with GCash", selected: $selection == .gcash) {
    selection = .gcash
}

// Large row with error
EBRadioButtonWithLabel("Option A", selected: isSelected)
    .controlSize(.large)
    .ebError(true)

// Disabled
EBRadioButtonWithLabel("Not available", selected: false)
    .disabled(true)
// Default row
EBRadioButtonWithLabel(
    label = "Pay with GCash",
    selected = selection == Option.Gcash,
    onCheckedChange = { selection = Option.Gcash }
)

// Large row with error
EBRadioButtonWithLabel(
    label = "Option A",
    selected = isSelected,
    onCheckedChange = { isSelected = it },
    size = EBRadioSize.Large,
    isError = true
)

// Disabled
EBRadioButtonWithLabel(
    label = "Not available",
    selected = false,
    onCheckedChange = { },
    enabled = false
)
Accessibility
RequirementiOSAndroid
Whole-row tap target Wrap the row in a Button or tap gesture — label must be tappable, not just the radio Apply Modifier.selectable(...) to the row
Group semantics Wrap options in .accessibilityElement(children: .contain) Use Modifier.selectableGroup() on parent
Role announcement Radio Button role inherited from the atom Role.RadioButton via selectable
Error announcement Include error state in the option's accessibility label Use semantics { error(...) }
Touch target size Row should be at least 44pt tall 48dp minimum
Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Ready icon-ofsset (typo — "offset"), text-container. Minor spelling issue.
C2 Variant & Property Naming Requires Rework size values encode error state with space-hyphen-space strings.
C3 Token Coverage Ready Label colors, gap, padding all token-bound.
C4 Native Mappability Ready HStack / Row with radio + label.
C5 Interaction State Coverage Requires Rework No disabled, selected, or pressed variants.
C6 Asset & Icon Quality Needs Refinement Always uses small radio instance — large label doesn't scale the radio.
C7 Code Connect Linkability Needs Refinement Blocked by C2 prop split.
Variants Inventory (4 total)

After the prop split, these 4 become 2 size × 2 isError = 4 clean variants, with selected + disabled added as booleans (not variants).

size (current)DecomposedNode ID
defaultsize=default, isError=false18482:35674
largesize=large, isError=false18482:35686
default - errorsize=default, isError=true18482:35680
large - errorsize=large, isError=true18482:35692
1.0.0 — April 2026Major
Initial Assessment · node 18482:35673
Component assessed — 4 variants (default / large × error). Documented
Initial
size prop encodes state — "default - error" and "large - error" mix size + state. Should split. Open
C2 Open
Missing state variants — No disabled or selected. Open
C5 Open
Radio hardcoded to small — large label still uses 16 × 16 radio. Open
C6 Open
Code Connect mappings — Not registered. Open
C7 Open