A selection control for binary and partial choices. 33 variants across isSelected (true/false/indeterminate) × State (Default/Pressed/Focused/Disabled/Error) × Size (Small/Medium/Large).
How the checkbox appears in real product screens — single and multi-select options on a loan Disclosure Statement (Schedule of Payment, Collateral), and checkboxes toggling which services a user can access.

CheckboxItem compound component provides label + description pairing.icon-check child layer (C6 resolved). All 5 interaction states and indeterminate defined across 3 sizes (C5 resolved). Carries its own visual states and token bindings.main/checkbox/color/...). isSelected uses true/false/indeterminate. All property values follow boolean and enum standards (C2 resolved).CheckboxItem compound component wraps Checkbox + Label + Description for accessible form groups.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Unchecked | Yes | Yes | isSelected=false | Border-only container. 3 sizes. |
| Checked | Yes | Yes | isSelected=true | Blue fill + separable icon-check layer. |
| Indeterminate | Yes | Yes | isSelected=indeterminate | Blue fill + icon-indeterminate dash. |
| Disabled | Yes | Yes | State=Disabled | 40% opacity. Checked: #9BC5FD fill. |
| Pressed | Yes | Yes | State=Pressed | Unchecked: #EBF2FF bg. Checked: #0F57C8. |
| Focused | Yes | Yes | State=Focused | Blue #1972F9 border stroke. |
| Error | Yes | Yes | State=Error | Red border / red #D81E1E fill. |
-
isSelected=Yes/Norenamed toisSelected=true/falsein Figma — now maps correctly to SwiftBooland KotlinBooleanC2 Fixed - Checkmark rebuilt as a separable
icon-checkchild layer inside each checked container — engineers can now tint, swap, and reference it in code C6 Fixed - Added 27 new variants — State (Pressed/Focused/Disabled/Error) × isSelected (true/false) × Size, plus
isSelected=indeterminateper size withicon-indeterminatedash layer. 6 → 33 total variants C5 Fixed
-
CheckboxItemcompound component created. Composes Checkbox + Label (Proxima Soft Bold) + Description (BarkAda Medium). 4 variants:isSelected(true/false) ×Size(Small 14px label / Medium 18px label). Node:17734:161220. Created Docs
Component Playground
Layout
VStack
Typography
Colors
iOS — Swift Package Manager
.package(url: "https://github.com/AY-Org/eb-ds-ios", from: "1.0.0")
Android — Gradle (Kotlin DSL)
implementation("com.eastblue.ds:checkbox:1.0.0")
Import
import EastBlueDS import com.eastblue.ds.checkbox.*
| Figma Property | SwiftUI | Compose |
|---|---|---|
| State — Default, Pressed, Focused, Disabled, Error | Disabled .disabled(true) · Focused .focused() · Error .ebError(true) · Pressed — | Disabled enabled = false · Pressed, Focused interactionSource · Error isError = true |
| isSelected — false, true, indeterminate | isOn: Binding<Bool> · indeterminate toggleIndeterminate | checked: Boolean · indeterminate TriStateCheckbox |
| Size — Small, Medium, Large | .controlSize() | size = EBCheckboxSize.* |
// Unchecked (default state)
EBCheckbox(isOn: $isSelected)
.controlSize(.regular)
// Small size
EBCheckbox(isOn: $isSelected)
.controlSize(.mini) // Unchecked (default state)
EBCheckbox(
checked = false,
onCheckedChange = { isSelected = it },
size = EBCheckboxSize.Medium
)
// Small size
EBCheckbox(
checked = false,
onCheckedChange = { isSelected = it },
size = EBCheckboxSize.Small
) // Checked (selected state)
EBCheckbox(isOn: .constant(true))
.controlSize(.regular)
// Bound to state
@State private var isChecked = true
EBCheckbox(isOn: $isChecked)
.controlSize(.large) // Checked (selected state)
EBCheckbox(
checked = true,
onCheckedChange = { isSelected = it },
size = EBCheckboxSize.Medium
)
// Bound to state
var isChecked by remember { mutableStateOf(true) }
EBCheckbox(
checked = isChecked,
onCheckedChange = { isChecked = it }
) // Indeterminate (partial selection)
EBCheckbox(isOn: $isSelected)
.controlSize(.regular)
.toggleIndeterminate(true) // Indeterminate (partial selection)
TriStateCheckbox(
state = ToggleableState.Indeterminate,
onClick = { /* cycle state */ },
modifier = Modifier.size(EBCheckboxSize.Medium)
) // Disabled unchecked
EBCheckbox(isOn: $isSelected)
.controlSize(.regular)
.disabled(true)
// Disabled checked
EBCheckbox(isOn: .constant(true))
.controlSize(.regular)
.disabled(true) // Disabled unchecked
EBCheckbox(
checked = false,
onCheckedChange = {},
enabled = false,
size = EBCheckboxSize.Medium
)
// Disabled checked
EBCheckbox(
checked = true,
onCheckedChange = {},
enabled = false,
size = EBCheckboxSize.Medium
) // Error state (form validation)
EBCheckbox(isOn: $isSelected)
.controlSize(.regular)
.ebError(true) // Error state (form validation)
EBCheckbox(
checked = false,
onCheckedChange = { isSelected = it },
isError = true,
size = EBCheckboxSize.Medium
) | Requirement | iOS | Android |
|---|---|---|
| Minimum touch target | 44 x 44 pt | 48 x 48 dp |
| Accessibility label | .accessibilityLabel("Accept terms") | semantics { contentDescription = "Accept terms" } |
| Checked state announcement | VoiceOver reads "checked" / "unchecked" automatically via Toggle | TalkBack reads state automatically via Checkbox semantics |
| Indeterminate | toggleIndeterminate reads "mixed" | TriStateCheckbox reads "partially checked" |
Do
Use it when people can select several options, or none.
Don't
Don’t use it for a single on/off setting that applies right away — use Toggle.
Do
Use indeterminate on a “select all” box when only some of the items below it are selected.
Don't
Don’t use indeterminate as a third answer for a single option.
Do
Pair the box with a label — use Checkbox Item for a labelled row.
Don't
Don’t place a bare box beside loose text.
Do
Give the Small (16px) box a touch area of at least 44pt / 48dp.
Don't
Don’t shrink the tap area down to the box.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Root frame named container. Simple, semantic hierarchy. No generic layer names. |
| C2 | Variant & Property Naming | Ready | isSelected now uses true/false — corrected in Figma. Maps directly to Swift Bool / Kotlin Boolean. indeterminate property is a C5 concern (missing state variant). |
| C3 | Token Coverage | Ready | All states have defined color values. Default, Pressed, Disabled, Error, Focused, and Indeterminate containers use distinct fills/strokes. Separable icon-check and icon-indeterminate layers present. |
| C4 | Native Mappability | Ready | Maps to Toggle(.checkbox) / Checkbox. Indeterminate maps to TriStateCheckbox. Label pairing via CheckboxItem compound component. |
| C5 | Interaction State Coverage | Ready | All 5 interaction states defined (Default, Pressed, Focused, Disabled, Error) × isSelected (true/false) × 3 sizes. Indeterminate added as isSelected=indeterminate with icon-indeterminate dash layer. 33 total variants. |
| C6 | Asset & Icon Quality | Ready | Checkmark rebuilt as a separable icon-check child vector layer inside each checked container. Can be tinted via selected/icon-check token and swapped natively. |
5 State × 3 isSelected × 3 Size = 45 theoretical. isSelected=indeterminate only ships State=Default, so actual count is (5 × 2 × 3) + (1 × 1 × 3) = 33 variants.
| isSelected | States | Sizes | Count |
|---|---|---|---|
| false | Default, Pressed, Focused, Disabled, Error | Small, Medium, Large | 15 |
| true | Default, Pressed, Focused, Disabled, Error | Small, Medium, Large | 15 |
| indeterminate | Default only | Small, Medium, Large | 3 |
View full State × isSelected × Size breakdown (33 rows)
| State | isSelected | Size | Node |
|---|---|---|---|
| Default | false | Small | 17143:2465 |
| Default | true | Small | 17143:2468 |
| Default | false | Medium | 17143:2471 |
| Default | true | Medium | 17143:2473 |
| Default | false | Large | 17143:2476 |
| Default | true | Large | 17143:2478 |
| Pressed | false | Small | 17733:968 |
| Focused | false | Small | 17733:971 |
| Disabled | false | Small | 17733:974 |
| Error | false | Small | 17733:977 |
| Pressed | true | Small | 17733:980 |
| Focused | true | Small | 17733:984 |
| Disabled | true | Small | 17733:988 |
| Error | true | Small | 17733:992 |
| Pressed | false | Medium | 17733:996 |
| Focused | false | Medium | 17733:998 |
| Disabled | false | Medium | 17733:1000 |
| Error | false | Medium | 17733:1002 |
| Pressed | true | Medium | 17733:1004 |
| Focused | true | Medium | 17733:1008 |
| Disabled | true | Medium | 17733:1012 |
| Error | true | Medium | 17733:1016 |
| Pressed | false | Large | 17733:1020 |
| Focused | false | Large | 17733:1022 |
| Disabled | false | Large | 17733:1024 |
| Error | false | Large | 17733:1026 |
| Pressed | true | Large | 17733:1028 |
| Focused | true | Large | 17733:1032 |
| Disabled | true | Large | 17733:1036 |
| Error | true | Large | 17733:1040 |
| Default | indeterminate | Small | 17733:1044 |
| Default | indeterminate | Medium | 17733:1048 |
| Default | indeterminate | Large | 17733:1052 |
Proxima Soft Bold, 14px/18px) + Description (BarkAda Medium, 12px). Wraps atomic Checkbox with label pairing for accessible form use.
CreatedisSelected=X, Size=Y, State=Z to State=X, isSelected=Y, Size=Z. State is now the first property axis in the component set. Section renamed from "Claude Testing" to "Checkbox".
Updated#0F57C8 (checked); Focused uses blue border; Disabled uses 40% opacity; Error uses red border / #D81E1E fill.
FixedisSelected=indeterminate variants for Small, Medium, and Large. Each contains a blue container frame with a named icon-indeterminate horizontal dash child layer. Maps to ToggleableState.Indeterminate (Android) and toggleIndeterminate (iOS).
FixedisSelected=true variants. Created clean blue container frames (4px radius) with a named icon-check child vector inside each. Engineers can now tint via selected/icon-check token and map to a native icon slot. Nodes: 17721:962 (Small), 17721:963 (Medium), 17721:964 (Large).
FixedisSelected=Yes/No to isSelected=true/false. Now maps directly to Swift Bool and Kotlin Boolean for Code Connect. C2 criterion resolved.
FixedisChecked. Figma metadata confirms the property is isSelected. All documentation updated.
FixedisSelected=Yes/No instead of true/false. Incompatible with Swift Bool and Kotlin Boolean for Code Connect mapping.
Fixed in v1.2.0