Design System
Components

Voucher

A 144px ticket tile — a title, a description and an Apply action, light or dark, default or applied.

Figma description · VoucherOpen in Figma ↗

This component has no description in Figma yet.

Title with 2 lines
above AED 60+
Free delivery
on any order
Props
import SwiftUI
import EandTokens

// Voucher: a fixed 144×144 ticket (r16). The outline, side notches and perforation are
// ONE exported Figma path (28278:13819) — import it as a vector asset; don't redraw it.
// Light = white ticket, dark = midnight-raised. Title title/xs over body/md, centred
// (padding 16/8), then the button/lg "Apply" action (padding 16/12).
struct Voucher: View {
  let title: String; let description: String
  var applied = false
  var onApply: () -> Void = {}
  var body: some View {
    VStack(spacing: 0) {
      VStack(spacing: 0) {
        Text(title).font(.system(size: EandTypography.titleXs.size, weight: EandTypography.titleXs.weight))
        Text(description).font(.system(size: EandTypography.bodyMd.size))
      }
      .foregroundColor(EandColor.textDefaultDefault)
      .multilineTextAlignment(.center)
      .padding(.vertical, EandSpacing.lg).padding(.horizontal, EandSpacing.sm)
      .frame(maxHeight: .infinity)
      Button(action: onApply) {
        HStack(spacing: EandSpacing.xs) {
          Text(applied ? "Applied" : "Apply")
            .font(.system(size: EandTypography.buttonLg.size, weight: EandTypography.buttonLg.weight))
          Image(systemName: applied ? "checkmark" : "plus.circle.fill").font(.system(size: EandIcon.xl))
        }
        .foregroundColor(applied ? EandColor.textPositiveSubtle : EandColor.buttonTertiaryTextMidnightDefault)
      }
      .disabled(applied)
      .padding(.vertical, EandSpacing.lg).padding(.horizontal, EandSpacing.md)
    }
    .frame(width: EandScale.scale144, height: EandScale.scale144)
    .background(Image("voucher-shape").renderingMode(.template).foregroundColor(EandColor.surfaceBaseInverse))
    .clipShape(RoundedRectangle(cornerRadius: EandRadius.radius6))
  }
}

When to use

  • Use to offer a promo code or voucher the user can apply at checkout, in a row of tiles.
  • Don't use for a deal the user browses (use DealCard).

Anatomy

  • Ticket — one exported shape with side notches and a perforation.
  • Text — title in title/xs over description in body/md, centred.
  • Action — "Apply" with an add glyph, which calls onApply. Once state="applied", it reads "Applied" with a check, in green, and is disabled.

display="light" is the white ticket; display="dark" the midnight-raised one.

<Voucher title="20% off" description="above AED 60+" onApply={apply} />
<Voucher display="dark" state="applied" title="Free delivery" description="on orders over AED 100" />

Deprecated

status is the V1.0 model: active becomes state="default" and redeemed becomes state="applied". expired has no Figma counterpart; it renders the applied tile dimmed.

Props

PropTypeDefaultDescription
description?ReactNodeabove AED 60+Second line under the title, e.g. "above AED 60+".
display?'light' | 'dark'light
onApply?(() => void)—
state?'default' | 'applied'—
status?'active' | 'redeemed' | 'expired'—Deprecated. V1.0. active->Default, redeemed->Applied; expired dims the tile.
title?ReactNodeTitle with 2 lines

Design

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

  • A fixed 144 × 144 tile. The ticket fills surface/base/inverse, or surface/raised/midnight when dark.
  • Text block padding 16 × 8 (spacing/lg, spacing/sm); action padding 16 × 12.
  • The action uses button/lg with a 24px glyph. Applied green is text/positive/subtle on light and text/positive/muted on dark, for contrast.
  • The Voucher page in Figma is still marked work in progress.

On this page