Design System
Components

PlanUsageBar

Filled block showing what is left of a plan allowance.

Figma description · .plan-usage-barOpen in Figma ↗

This component has no description in Figma yet.

20GBleftLocal Data
200minleftMinutes
0.6GBleftExpires 3 daysRoaming Data
Props
import SwiftUI
import EandTokens

// 48pt filled block: the fill shows what is LEFT, the amount reads inside it,
// the category sits in the track opposite. Green normally, orange on low data.
// Text the fill covers switches to inverse (white) ink.
struct PlanUsageBar: View {
  let label: String; let remaining: Double; let total: Double; let unit: String
  var note: String? = nil
  var lowData: Bool = false

  var body: some View {
    GeometryReader { geo in
      ZStack(alignment: .leading) {
        RoundedRectangle(cornerRadius: EandRadius.radius4)
          .fill(EandColor.surfaceCanvasInverse)
        RoundedRectangle(cornerRadius: EandRadius.radius4)
          .fill(lowData ? EandColor.orange500 : EandColor.green600)   // Figma green/550 = #54bc72
          .frame(width: geo.size.width * fraction)
        HStack {
          VStack(alignment: .leading, spacing: 0) {
            Text("\(Int(remaining)) \(unit) left")
              .font(.system(size: EandTypography.titleXs.size, weight: .semibold))
            if let note { Text(note).font(.system(size: EandTypography.bodySm.size)) }
          }
          Spacer()
          Text(label).font(.system(size: EandTypography.buttonMd.size, weight: .medium))
        }
        .padding(.horizontal, EandSpacing.lg)
      }
    }
    .frame(height: 48)
  }

  // Clamped: no allowance means nothing to draw, a top-up must not overflow the track.
  private var fraction: Double { total > 0 ? min(1, max(0, remaining / total)) : 0 }
}

When to use

  • Use to show the balance of one allowance — data, minutes, roaming — in a plan breakdown. Stack bars with a 4px gap, one per allowance.
  • Don't use for progress through a task or flow (use ProgressBar or Stepper), or for a loyalty tier (use SmilesBalance).

Anatomy

A 48px rounded row where a coloured block shows how much of the allowance is left. The amount reads inside the block; the category label sits in the track at the opposite end.

  • Value — the amount, the unit and "left", with an optional note underneath, such as "Expires in 3 days".
  • Label — the category, end-aligned.

remaining is what the block measures, not consumption. Pass total so the fill has something to be a share of; with total={0} there is no allowance and nothing is drawn.

status is explicit rather than derived: default fills green, low-data orange. The point at which an allowance counts as low is a product rule that differs for data, minutes and roaming, so the bar does not guess it.

Text on the fill

Where the block sits under the text, the text switches to its filled state: white, and for the value a Medium weight. The bar measures this itself, separately for the value and the label: each flips once the fill covers it completely. Pin either one with valueState or labelState when you need a fixed result, for example in a static mock-up.

Deprecated

used still works and is converted to remaining as total - used. Pass remaining.

Props

PropTypeDefaultDescription
totalnumber—Plan allowance the block is measured against.
label?ReactNodeDataCategory name, right-aligned in the track — e.g. "Local Data".
labelState?'default' | 'filled'—Ink of the category label. Omit to switch automatically once the fill covers it.
note?ReactNode—Second line under the value, e.g. "Expires 3 days".
remaining?number—Amount left in the plan. This is what the block shows and measures.
status?'default' | 'low-data'default
unit?stringGB
used?number—Deprecated. Pass remaining. Kept so used-based call sites keep working.
valueState?'default' | 'filled'—Ink of the value and note. Omit to switch automatically once the fill covers them.

Design

Figma source: .plan-usage-bar — etisalat Consumer App DSL V1.2 · states

  • Track: 48px tall, 12px radius (border-radius/4), on surface/canvas/inverse, with 16px side padding (spacing/lg).
  • Fill: the Progress status axis (28927:22814), green #54bc72 or orange #ff9f52. Two discrete states, not a gradient.
  • Value: title/xs in text/default/default; filled, button/md in text/default/inverse. The amount, unit and "left" are spaced by 2px (spacing/2xs). Note: body/sm at 130%.
  • Label: button/md, in text/default/default or, filled, text/default/inverse.

The bar is one part of the Figma plan-usage card (38351:53904). That card is a screen pattern, not a component; assemble it from the parts: a row of outline Chips (40px, 4px apart) and the stacked bars (4px apart) in a 20px-radius card on surface/canvas/default with 12px padding and an 8px gap, followed after 4px by a 72px ActionBar prompting a top-up.

On this page