A row primitive used in lists of tappable rows — title, optional trailing CTA, and chevron.
List component with description?: String, trailing: .cta | .counter | .chevron | .none, plus a named leading slot for the icon. Align label typography across the family (currently Semibold 16 Neutral vs. Bold 18 Brand). Add a Pressed state — these rows are primary nav targets. Reconcile with List Item (display-only body rows) and clarify when to use which.Action-list rows stack inside Settings / Profile / Help menus. A typical screen mixes variants with/without description and with/without trailing counter.
main/action-list/* tokens. Loading skeleton uses bg/color-bg-strong for placeholders. Leaked internal spacer annotations (_space_2, _space_16) are rendered inside production instances. C1List and List - with Description use Semibold 16 Neutral (#0A2757); List - with Counter uses Bold 18 Brand Blue (#005CE5). C2trailing enum. C4 C6| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default | Yes | Yes | State=Default | Baseline row. Label in Neutral Dark (or Brand Blue on the Counter variant). |
| Disabled | Yes | Yes | State=Disabled | Label → #C2CFE5, chevron → #9BC5FD, CTA → #9BC5FD, counter bg stays #EEF2F9 but label → #C2CFE5. |
| Loading | Yes | Yes | State=Loading | Icon becomes a neutral ring; label + trailing become 16 px pill placeholders filled with #EEF2F9. |
| Pressed | N/A | N/A | Not built | Action rows are tap targets — a pressed state (row tint + possibly label darken) is a baseline expectation for native. |
| Focused | N/A | N/A | Not built | TV / keyboard focus ring not defined. Android a11y also relies on it. |
- Three sibling components for one row pattern.
List,List - with Counter, andList - with Descriptiondiffer only by the presence of a description line and/or a trailing counter. Collapse into a singleListcomponent with optionaldescriptionand atrailingunion. C1 · Layer Structure & Naming - Label typography diverges across the family.
List+List - with Descriptionuse Proxima Soft Semibold 16 / Neutral Dark (#0A2757).List - with Counteruses Proxima Soft Bold 18 / Brand Blue (#005CE5). Same row family should read as one thing. Pick one token (labelorlabel-brand) and one size. C2 · Variant & Property Naming - Leaked spacer annotation layers. Internal
_space_2(4115:3220) and_space_16(21:40139) annotation frames are rendered as opacity-0 layers inside every production instance. Artifacts of authoring, not part of the public component. Remove or move to a separate documentation artboard. C1 · Layer Structure & Naming - Leading icon is a gray placeholder. All three siblings default to a
#C2C6CFfilled 32 px circle under a frame named Placeholder. Same instance-swap anti-pattern as List Item. Adopt a Figma Slot so consumers can drop in a real icon (or an Avatar). C6 · Asset & Icon Quality - No Pressed state. These rows are the primary tap surface for navigation menus. The State enum exposes only Default / Disabled / Loading. Add Pressed (tinted bg and/or chevron darken). C5 · Interaction State Coverage
- Trailing content is baked per sibling. CTA text lives on the base, a filled Counter lives on the Counter sibling, and a chevron appears only sometimes. Introduce a
trailingenum (.cta(String) | .counter(Int) | .chevron | .none) so one component covers all three patterns. Maps cleanly to native enums. C4 · Native Mappability - Code Connect mappings not registered. Blocked on the consolidation + slot adoption. Adding mappings for three siblings would cement the wrong schema. C7 · Code Connect Linkability
- Consolidate into one
Action Rowcomponent. One component with propertiestitle: String,subtitle?: String(replaces "with Description"),trailing: chevron | counter | switch | badge | none(replaces "with Counter"),state: default | pressed | disabled | loading, plus aleadingslot for icon/avatar. Replaces 15 variants across 3 components with clean slot-based composition. Family - Add a
leadingFigma Slot for the icon. Maps 1:1 to@ViewBuilder(SwiftUI) and a@Composableslot (Compose). Accepts Icon, Avatar, or a custom 32 px component. Remove the placeholder fill entirely — empty slot means no leading. Slot - Reconcile label typography. Pick one: either Neutral Dark Semibold 16 (matches
List Item+ most action rows) or Brand Blue Bold 18 (matches the current Counter sibling). Neutral is the safer default — Brand Blue reads like a link, which the whole row already behaves as. Apply the choice to all three shapes. Token - Add Pressed (and ideally Focused) states. Pressed =
bgtints to#F4F6FA, chevron / CTA darkens one step. Focused = 2 px brand ring at 2 px offset. Baseline for native row components. State - Remove
_space_2/_space_16spacer annotations. These are authoring artifacts. Move to a separate "Annotations" artboard or delete once the auto-layout is settled. They export as opacity-0 layers to consumers. Composition - Disambiguate vs
List Item. This component is tappable action navigation (icon + label + trailing CTA/counter/chevron).List Itemis display body rows (bullet + text for terms/steps). Document the distinction and cross-link the two — today the names don't telegraph which is which. Docs - Rename the family to
Action Row. "Action List" implies a collection, but each component here is a single row. Native name:EBActionRow. Disambiguates from theListcontainer (scroll primitive) and theList Itemdisplay rows (terms/steps). Single-component consolidation eliminates the "with X" naming pattern entirely. Rename
Baseline row. 6 variants (State × Density). Label in Neutral Dark Semibold 16. Trailing CTA text + 24 px chevron icon. 360 × 48 (compact) / 360 × 56 (expanded).
| Role | Token | Default | Disabled | Loading |
|---|---|---|---|---|
| Row bg | main/action-list/color/default/bg | #FFFFFF | #FFFFFF | #FFFFFF |
| Label (base & with-description) | main/action-list/color/default/label | #0A2757 | #C2CFE5 | — |
| Label (with-counter) | main/action-list/color/default/label-brand | #005CE5 | #C2CFE5 | — |
| Description | main/action-list/color/default/description | #6780A9 | #C2CFE5 | — |
| Trailing CTA label | main/action-list/color/default/label-link | #005CE5 | #9BC5FD | — |
| Chevron | main/action-list/color/default/chevron | #005CE5 | #9BC5FD | — |
| Counter bg | main/counter/color/filled/bg | #EEF2F9 | #EEF2F9 | — |
| Counter label | main/counter/color/filled/label | #072592 | #C2CFE5 | — |
| Skeleton fill | bg/color-bg-strong | — | — | #EEF2F9 |
Adds a trailing <a href="#" onclick="showPanelById('counter');return false;">Counter</a> pill. 6 variants (Density × State). Card-like container with <code>radius-2</code> (6 px) corners and <code>Depth/D0</code> drop-shadow — differs from the base's flat row. Label switches to Bold 18 Brand Blue. 360 × 56 / 360 × 64.
| Role | Token | Value |
|---|---|---|
| Frame width | — | 360px (fill container in product) |
| Row height — base | — | 48 (compact) / 56 (expanded) |
| Row height — with Counter | — | 56 (compact) / 64 (expanded) |
| Row height — with Description | — | 60 (no density axis) |
| Icon size | — | 32 × 32 |
| Icon → label gap | space/space-12 | 12px |
| Wrapper padding (compact) | space/space-12 + 7/11 | 12px / 7px (compact) · 12px / 11px (expanded) |
| Description gap | space/space-6 | 6px |
| Counter radius | radius/radius-round | 99999px (pill) |
| Counter size | — | 24 × 24 (filled) / h24 (empty) |
| Card radius (with Counter) | radius/radius-2 | 6px |
| Card shadow (with Counter) | Depth/D0 | 0 1 3 0 · #E8EEF2C9 |
| Chevron size | — | 24 × 24 (base + with-description) / 32 × 32 (with-counter) |
| Spacer annotations | — | _space_2, _space_16 leak through (opacity 0) |
Adds a secondary description line under the label. 3 variants (State only — no Density axis). Label matches the base (Semibold 16 Neutral). Description uses Semibold 12 / tracking-wider / <code>main/action-list/color/default/description</code> (<code>#6780A9</code>). 360 × 60.
| Role | Token | Spec |
|---|---|---|
| Label — base & with-description | Primary/Label/Light/Base | Proxima Soft Semibold · 16 / 16 · +0.25 |
| Label — with-counter | Primary/Label/Large | Proxima Soft Bold · 18 / 18 · +0.25 |
| Description | Primary/Multi-line Label/Light/Fine | Proxima Soft Semibold · 12 / 14 · +0.5 |
| Trailing CTA | Primary/Label/Light/Base | Proxima Soft Semibold · 16 / 16 · +0.25 |
| Counter label | Primary/Label/Small | Proxima Soft Bold · 14 / 14 · +0.25 |
iOS — Swift Package Manager
// In Xcode: File → Add Package Dependencies "https://github.com/AY-Org/eb-ds-ios"
Android — Gradle (Kotlin DSL)
dependencies { implementation("com.eastblue.ds:list:1.0.0") }
| Figma Property | SwiftUI | Compose |
|---|---|---|
| 3 sibling components | 1 component: List | EBActionListRow |
| icon (Placeholder) | leading (Slot) | @ViewBuilder leading |
| label: String | label: String | label: String |
| description (only on sibling) | description?: String | description: String? |
| trailingComponent (bool) / counter (bool) / chevron (bool) | trailing: .cta(String) | .counter(Int) | .chevron | .none | trailing: EBRowTrailing |
| density: Compact/Expanded | density: .compact / .expanded | .controlSize(.regular / .large) |
| state: Default/Disabled/Loading | state: .default / .pressed / .disabled / .loading | .disabled(Bool) + intrinsic press + loading: Bool |
| bottomBorder: Bool | bottomBorder: Bool | divider: Bool |
| (not modeled) | onTap | action: () -> Void |
// Base — icon + label + CTA + chevron EBActionListRow("Payment methods", trailing: .cta("View")) { Image(systemName: "creditcard.fill") } action: { openPaymentMethods() } // With counter — shows 3 pending items EBActionListRow("Notifications", trailing: .counter(3)) { Image(systemName: "bell.fill") } action: { openNotifications() } // With description + chevron EBActionListRow( "Profile", description: "Name, photo, and contact info", trailing: .chevron ) { Image(systemName: "person.crop.circle") } action: { openProfile() } // Loading EBActionListRow.skeleton()
// Base — icon + label + CTA + chevron EBActionListRow( label = "Payment methods", leading = { Icon(Icons.Default.CreditCard, contentDescription = null) }, trailing = EBRowTrailing.Cta("View"), onClick = { openPaymentMethods() } ) // With counter EBActionListRow( label = "Notifications", leading = { Icon(Icons.Default.Notifications, contentDescription = null) }, trailing = EBRowTrailing.Counter(3), onClick = { openNotifications() } ) // With description + chevron EBActionListRow( label = "Profile", description = "Name, photo, and contact info", leading = { Icon(Icons.Default.Person, contentDescription = null) }, trailing = EBRowTrailing.Chevron, onClick = { openProfile() } ) // Loading EBActionListRow.Skeleton()
| Requirement | iOS | Android |
|---|---|---|
| Row as button | Wrap row in Button; mark decorative leading icon with .accessibilityHidden(true). | Modifier.clickable { … }.semantics(mergeDescendants = true) { role = Role.Button }. |
| Combined label | Announce label + description + trailing counter as one phrase: "Notifications, 3 unread". | Same — build via contentDescription. |
| Touch target | Minimum 44 × 44 — expanded density hits this; compact (48 px row) is safe; ensure whole row is the tap target, not just the chevron. | Minimum 48 × 48dp — same. |
| Loading | Announce "Loading" once; disable tap while loading. | Same — enabled = false plus contentDescription = "Loading". |
| Focus ring | Provide a focused treatment for external keyboards. | Focus ring required for TV / external keyboards. |
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Requires Rework | 3 sibling components for one pattern. Spacer annotations leak into production. |
| C2 | Variant & Property Naming | Requires Rework | Inconsistent label typography across siblings. Counter sibling uses a different text style than its peers. |
| C3 | Token Coverage | Ready | All colors / paddings bound to main/action-list/*, space/*, radius/* tokens. |
| C4 | Native Mappability | Needs Refinement | Maps cleanly once trailing is a single enum instead of three booleans across three components. |
| C5 | Interaction State Coverage | Requires Rework | No Pressed / Focused. Disabled + Loading present. |
| C6 | Asset & Icon Quality | Needs Refinement | Leading icon is a gray placeholder circle — move to a Figma Slot. |
| C7 | Code Connect Linkability | Not Mapped | Blocked on consolidation. Mapping three siblings would cement the wrong schema. |
3 sibling components. Base + Counter multiply State (3) × Density (2) = 6 each. Description axis = State (3). Total 6 + 6 + 3 = 15 variants.
| Component | Axes | Count | Node |
|---|---|---|---|
| List | State (3) × Density (2) | 6 | 18577:14545 |
| List - with Counter | Density (2) × State (3) | 6 | 18577:14637 |
| List - with Description | State (3) | 3 | 18577:14604 |
List row. Reconcile label typography. Add Pressed state. Open_space_2 / _space_16 are authoring artifacts exported as opacity-0 layers. Opentrailing enum. Open#C2C6CF circle. Adopt a Figma Slot. Openmain/action-list/*. Noted