Button
Pill-shaped action button — primary, secondary, tertiary, link and glass, in three sizes, with a scheme for light, red and dark backgrounds.
buttonOpen in Figma ↗import SwiftUI import EandTokens // V1.2 button: pill, lg 48 / md 40 / sm 32 tall, inline inset 16 / 16 / 10. // Secondary is a 1pt INSIDE stroke (strokeBorder), so it never grows the box. // Tertiary and link hug their text: no height, no padding. struct EandButton: View { enum Variant { case primary, secondary, tertiary } let title: String var variant: Variant = .primary var action: () -> Void var body: some View { Button(action: action) { Text(title) .font(.system(size: EandTypography.buttonLg.size, weight: EandTypography.buttonLg.weight)) .foregroundColor(ink) .padding(.horizontal, variant == .tertiary ? 0 : EandSpacing.lg) .frame(minWidth: variant == .tertiary ? nil : EandScale.scale48, minHeight: variant == .tertiary ? nil : EandScale.scale48) .background(variant == .primary ? EandColor.buttonPrimarySurfaceBrandDefault : .clear) .overlay { if variant == .secondary { Capsule().strokeBorder(EandColor.buttonSecondaryBorderBrandDefault, lineWidth: EandBorder.sm) } } .clipShape(Capsule()) } } private var ink: Color { switch variant { case .primary: return EandColor.buttonPrimaryTextBrandDefault case .secondary: return EandColor.buttonSecondaryTextBrandDefault case .tertiary: return EandColor.buttonTertiaryTextBrandDefault } } }
When to use
- Use
primaryfor the one main action on a screen or sheet,secondarybeside it for the alternative, andtertiaryorlinkfor low-emphasis text actions. Useglasson imagery and brand surfaces. - Use
blockfor full-width actions, such as those in a CTA Bar. - Don't use for the legal links in a consent row (ConsentCheckbox sets them), for filters (Chip, FilterPill) or for a section's “See all” (SectionLink).
Choosing a scheme
scheme | Primary, tertiary, link | Secondary |
|---|---|---|
default | Brand red | Red outline |
inverse | White, for red and dark backgrounds | Dark (midnight) outline |
midnight | Ink | White outline |
Secondary follows the published symbol, whose bindings are crossed relative to every other
type: scheme="inverse" is the dark outline and scheme="midnight" the white one. On a red or
dark background, pair primary scheme="inverse" with secondary scheme="midnight". Whether
the crossing is intended is an open design question. Glass has no scheme.
Sizes and states
- Sizes —
lg48,md40,sm32. The icon slots are 24 / 20 / 16, so pass<Icon size>20 / 16 / 12 toleadingIconandtrailingIcon. - Tertiary and link hug their text, with no height or padding. Link is set in body type and is underlined only while pressed.
- Pressed follows the pointer, or Space and Enter while held; it never lingers on focus. Keyboard focus shows a 2px ring. Figma has no focus state, so the ring is a code-side accessibility addition.
- Disabled uses the disabled tokens, with no opacity. Loading puts a spinner in the leading slot and suppresses clicks.
surface and tone are deprecated aliases from V1.1; scheme wins when both are set.
Rendering is identical except for secondary:
| Deprecated | New (primary, tertiary, link, glass) | New (secondary) |
|---|---|---|
surface="brand" | scheme="default" | scheme="default" |
surface="inverse-brand", "white", tone="inverse" | scheme="inverse" | scheme="midnight" |
surface="midnight" | scheme="midnight" | scheme="inverse" |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
block? | boolean | — | Stretch to fill the container width (block CTA). |
leadingIcon? | ReactNode | — | Leading icon. The slot is 24 / 20 / 16 (lg / md / sm) with 2px inset, so pass an
<Icon size> of 20 / 16 / 12. |
loading? | boolean | — | Show a spinner in the leading-icon slot and suppress clicks (Figma .button-icon loader=true).
Keeps the default palette — loading is not the disabled state. |
scheme? | 'default' | 'inverse' | 'midnight' | — | Colour scheme (Figma V1.2 scheme). Takes precedence over surface and tone.
Secondary follows the published symbol, whose bindings are crossed relative to the
other types: scheme="inverse" is the dark (midnight) outline and scheme="midnight"
is the white outline. Logged as a design question. |
size? | 'lg' | 'md' | 'sm' | md | |
surface? | 'brand' | 'midnight' | 'white' | 'inverse-brand' | — | Deprecated. Use scheme. Kept as a back-compat alias; scheme wins when both are set. |
tone? | 'inverse' | 'brand' | — | Deprecated. Use scheme. Kept as a back-compat alias. |
trailingIcon? | ReactNode | — | Trailing icon — same slot sizes as leadingIcon. |
variant? | 'link' | 'glass' | 'primary' | 'secondary' | 'tertiary' | primary |
For UAE PASS sign-in, use UaePassButton: it is its own published component, not a Button variant.
Design
Figma source: button — etisalat Consumer App DSL V1.2
- Height and minimum width
scale/48·40·32; radiusborder-radius/9. - Inline inset 16 / 16 / 10: the outer
spacing/sm(spacing/xsatsm), plus the content'sspacing/xs, plus the label'sspacing/xs(spacing/2xsatsm). - Secondary has a 1px (
border/sm) inside stroke; pressed is a solid fill with no stroke. - Labels are
button/lg·md·sm; link usesbody/lg·md·sm. Glass isbutton/glass/*behind a 20px backdrop blur.