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.
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.
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) —
iconin a 40px tile onsurface/glass/midnight/sm, orlogofull-bleed in a 40px tile onsurface/base/default. Both tiles have a 12px radius. - Content —
labelintitle/xs;tooltipadds an info glyph after it (onTooltipClickmakes the glyph a button);sublabelinbody/md, subtle;actionbelow, typically a tertiarymdButton. - Trailing — end-aligned:
valueintitle/xs,hintinbody/mdsubtle, andbadge. Thenchevron(on by default), a 16pxchevron-right. Setchevron={false}for pure data.
Choosing a type
type | Layout | Use for |
|---|---|---|
info (default) | Padded card, 16px all round, on a surface | Standalone data rows |
price | Flat, 16px bottom padding, no surface | Line 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 wheninverseis 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
| Prop | Type | Default | Description |
|---|---|---|---|
label | ReactNode | — | 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? | boolean | true | Trailing 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' | info | Figma 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) atborder-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 hintbody/md(14px) intext/default/subtle. - The chevron sits in a 20px box, 4px after the value block, vertically centred.