Design System
Components

Checkbox

Square selection mark at md (20) or sm (16), with the icon-set check, on default or inverse surfaces.

Figma description · checkboxOpen in Figma ↗

A binary selection control for forms and lists, available in square (multi-select) and circular (radio-style) shapes.

Use for: Form fields, settings screens, and list items where the user needs to select one or more options. Use the square shape (radio=false) when multiple items can be selected simultaneously. Use the circular shape (radio=true) when the checkbox needs to feel softer or less rigid in context — note this is still a checkbox that allows multi-select, not a true radio button.

Not for: Mutually exclusive single-select choices where only one option can be active — use a switcher or a true radio input pattern instead.

Choosing: Use size=md (20×20px) for standard forms, settings, and main content areas. Use size=sm (16×16px) for dense layouts, compact lists, or inline use. Set inverse=false on light or neutral backgrounds. Set inverse=true on midnight or brand red surfaces. Set selected=true to show a checked state — brand red fill with white checkmark. Set disabled=true to make the control non-interactive; a selected disabled checkbox renders with desaturated grey fill.

Rules: Always pair with a visible label — do not use the checkbox alone without accompanying text. Do not use radio=true to imply single-select behaviour; its shape is cosmetic. Do not mix radio=true and radio=false within the same list.

Props
import SwiftUI
import EandTokens

// V1.2 checkbox: md 20 / sm 16, radius 4, 1.5pt off ring; on = brand fill with the icon-set
// check (12pt in a 16pt box). Every state is a color/mark token — disabled is a colour.
// radio=yes draws the same mark as a circle.
struct CheckboxView: View {
  let label: String; @Binding var on: Bool
  var body: some View {
    HStack(spacing: EandSpacing.sm) {
      CheckboxMark(on: on)
      Text(label).font(.system(size: EandTypography.bodyMd.size))
        .foregroundColor(EandColor.textDefaultDefault)
    }
    .onTapGesture { on.toggle() }
  }
}

struct CheckboxMark: View {
  let on: Bool
  var body: some View {
    RoundedRectangle(cornerRadius: EandRadius.radius1)
      .fill(on ? EandColor.markDefaultSurfaceSelected : .clear)
      .overlay {
        if !on {
          RoundedRectangle(cornerRadius: EandRadius.radius1)
            .strokeBorder(EandColor.markDefaultBorderDefault, lineWidth: EandBorder.md)
        }
      }
      .overlay {
        if on {
          Image(systemName: "checkmark").font(.system(size: EandIcon.sm - 2, weight: .bold))
            .foregroundColor(EandColor.markDefaultIconSelected)
        }
      }
      .frame(width: EandCheckbox.md, height: EandCheckbox.md)
  }
}

When to use

  • Use for independent options the user can combine, or for a single opt-in.
  • Use ConsentCheckbox when the label carries legal links, such as Terms and Privacy Policy.
  • Don't use for one choice out of several — that is Radio. For a setting that takes effect at once, use the Switcher.

radio draws the circular checkbox from the same Figma set: a filled circle with a tick, on the same tokens. For the ring-and-dot control, use Radio. Pass inverse on brand-red and midnight surfaces.

Props

PropTypeDefaultDescription
inverse?boolean—Inverse color scheme for dark (midnight/brand) surfaces (Figma color-scheme=inverse).
label?ReactNode—
radio?boolean—Render as a radio (single-selection) — a circle instead of a square. Same mark, colors and states (Figma radio (single selection)=yes, node 27685:5017).
size?'md' | 'sm'mdControl size — md 20×20 (default) or sm 16×16 (Figma V1.1 size axis).

Design

Figma source: checkbox — etisalat Consumer App DSL V1.2

  • md 20 and sm 16 (checkbox/md·sm), border-radius/1 (4); unchecked boxes have a 1.5px (border/md) border.
  • The tick is the icon set's check-filled: 12px in a 16px box at md, 8px in 12px at sm.
  • Every state binds mark/{default,inverse}/{surface,icon,border}/*. Disabled is a colour, not opacity.
  • The label is body/md, spacing/sm from the box, in text/default/default; inverse uses text/default/inverse-subtle.

On this page