Design System
Components

ListRow

A single data row — a title and subtitle set against a value, price or badge, on a card or as a flat line item.

Figma description · list-itemOpen in Figma ↗

A single data row presenting a label and either a price value or rich informational content.

Use for: Itemised lists inside bottom sheets, accordion panels, or section body slots — such as plan charge breakdowns, usage line items, or service details.

Not for: Standalone page rows that require a tap target for navigation — use action-bar instead. Not for filter or selection rows — use chips instead.

Choosing: Use type=price for billing or usage line items where a right-aligned value and chevron are needed. Use type=info for descriptive rows that need a leading visual (icon, art, or logo at 40×40px), a label, a description, and an optional tertiary button for a secondary action. The info type renders with a card surface; price renders flat with a bottom divider only.

Rules: Always stack list items vertically within a containing section or panel slot. Do not use type=price when the row needs a leading visual — use type=info instead. Do not place list items directly on a screen or inside a section header. In RTL, layout and icon direction mirror automatically. Use variants without surface only when it's a plain white of surface-default background. In other cases, use 'padding=true' + 'surface' variants. Select surface variant based on background, it should have contrast.

Freedom Live 200Renews 12 NovAED 200.00per month
Data roaming packGCC · 7 daysAED 49.00one-off
Props
import SwiftUI
import EandTokens

// V1.2 list-item (type=info): a padded r20 card. 40pt icon tile (r12, glass midnight 7%),
// title/xs + body/md subtitle; trailing value (title/xs) over a hint, then a 16pt chevron.
// type=price is flat: no surface, padding 0/0/16. Not a tap target — use ActionBar for that.
struct ListRowView: View {
  let label: String; let sublabel: String; let value: String; let hint: String
  var body: some View {
    HStack(spacing: EandSpacing.md) {
      HStack(alignment: .top, spacing: EandSpacing.sm) {
        Image(systemName: "simcard.fill")
          .frame(width: EandScale.scale32, height: EandScale.scale32)
          .padding(EandSpacing.xs)
          .background(EandColor.surfaceGlassMidnightSm, in: RoundedRectangle(cornerRadius: EandRadius.radius4))
        VStack(alignment: .leading, spacing: EandSpacing.spacing2xs) {
          Text(label).font(.system(size: EandTypography.titleXs.size, weight: EandTypography.titleXs.weight))
          Text(sublabel).font(.system(size: EandTypography.bodyMd.size))
            .foregroundColor(EandColor.textDefaultSubtle)
        }
      }
      Spacer(minLength: 0)
      HStack(spacing: EandSpacing.xs) {
        VStack(alignment: .trailing, spacing: EandSpacing.spacing2xs) {
          Text(value).font(.system(size: EandTypography.titleXs.size, weight: EandTypography.titleXs.weight))
          Text(hint).font(.system(size: EandTypography.bodyMd.size))
            .foregroundColor(EandColor.textDefaultSubtle)
        }
        Image(systemName: "chevron.right").font(.system(size: EandIcon.md))
          .frame(width: EandIcon.lg, height: EandIcon.lg)
      }
    }
    .foregroundColor(EandColor.textDefaultDefault)
    .padding(EandSpacing.lg)
    .background(EandColor.surfaceRaisedDefault)
    .clipShape(RoundedRectangle(cornerRadius: EandRadius.radius7))
  }
}

When to use

  • Use to present one piece of data: a balance, an add-on and its price, a line on a bill, a setting's current value. Inside a Section, rows stack 4px apart.
  • Don't use as a tap target. Figma defines the list item as a data row and points tappable rows to ActionBar. Not for section headings (use Section).

A div, not a button

ListRow renders a <div>. Passing onClick still renders a <button> so existing screens keep working, but new tappable rows should use ActionBar.

Anatomy

  • Leading (optional) — icon in a 40px tile on surface/glass/midnight/sm, or logo full-bleed in a 40px tile on surface/base/default. Both tiles have a 12px radius.
  • Content — label in title/xs; tooltip adds an info glyph after it (onTooltipClick makes the glyph a button); sublabel in body/md, subtle; action below, typically a tertiary md Button.
  • Trailing — end-aligned: value in title/xs, hint in body/md subtle, and badge. Then chevron (on by default), a 16px chevron-right. Set chevron={false} for pure data.

Choosing a type

typeLayoutUse for
info (default)Padded card, 16px all round, on a surfaceStandalone data rows
priceFlat, 16px bottom padding, no surfaceLine items in a price summary

padding overrides the type's default.

Pass inverse on dark or glass backgrounds: the title and value turn white, the subtitle and hint inverse-subtle, and the icon tile surface/glass/white/sm. The surface options follow the scheme:

  • Light — default (surface/raised/default), subtle (surface/base/default), sunken (surface/sunken/default), glass.
  • Inverse — glass (the default when inverse is set), midnight-base, midnight-raised, midnight-transparent, white-transparent.
<ListRow type="price" label="Data add-on" sublabel="10 GB, 30 days" value="AED 49.00" chevron={false} />
<ListRow inverse surface="midnight-raised" label="Balance" tooltip value="AED 120.50" hint="Due 12 Oct" />

Props

PropTypeDefaultDescription
labelReactNode—Title, title/xs.
action?ReactNode—Under the subtitle — a tertiary md Button (Figma .content button).
badge?ReactNode—Trailing badge under the value (Figma badges-offers md).
chevron?booleantrueTrailing chevron-right. Default on.
hint?ReactNode—Trailing second line, body/md subtle.
icon?ReactNode—Leading glyph, shown in the 40px icon tile (Figma .logo type=icon).
inverse?boolean—Figma .list-item-scheme inverse — white ink for dark or glass surroundings.
logo?ReactNode—Leading brand logo / art, 40px on surface/base/default (Figma .logo type=logo).
onTooltipClick?(() => void)—Makes the tooltip glyph a button. Ignored when the row itself has onClick.
padding?boolean—Figma .list-item-padding. Defaults to true for info, false for price.
sublabel?ReactNode—Subtitle, body/md subtle.
surface?'default' | 'glass' | 'midnight-base' | 'midnight-raised' | 'subtle' | 'sunken' | 'white-transparent' | 'midnight-transparent'—Card surface (padded rows only). Light: default·subtle·sunken·glass. Inverse: glass·midnight-base·midnight-raised·midnight-transparent·white-transparent. Defaults to default, or glass when inverse.
tooltip?boolean—Info glyph after the title (Figma tooltip).
type?'info' | 'price'infoFigma type. info (default) = padded card on a surface; price = flat line item.
value?ReactNode—Trailing first line, title/xs (e.g. "AED 20.00").

Design

Figma source: list-item — etisalat Consumer App DSL V1.2

  • Padded rows: 16px padding (spacing/lg) at a 20px radius (border-radius/7).
  • Gaps: 12 between leading, content and trailing (spacing/md); 8 between the tile and the text (spacing/sm); 2 inside the content and trailing blocks (spacing/2xs).
  • Leading tile 40px (scale/40) at border-radius/4, holding a 32px icon box; it stays top-aligned when the content runs to several lines.
  • Title and value title/xs (14px); subtitle and hint body/md (14px) in text/default/subtle.
  • The chevron sits in a 20px box, 4px after the value block, vertically centred.

On this page