A tappable list row with a leading asset slot, label and description, and a trailing chevron with optional counter. Merges the former Action List, Action List with Counter and Action List with Description.
4628:19843 in the 2026 Working File and renamed Action Row, merging the three former Action List siblings into TrailingContent (2) × State (4) × Density (2) = 16 variants, plus a hasDescription boolean. Interaction states now exist where there were none, the leading asset and counter are real Figma Slots, and layer and property naming matches the conventions the rest of the family settled on. All four DS Health traits pass; the only item still open is Code Connect, blocked until the native library exists.Action-list rows stack inside Settings / Profile / Help menus. A typical screen mixes variants with/without description and with/without trailing counter.
TrailingContent, State and Density are orthogonal, Density is used exactly as §1 defines it, hasDescription carries the correct verb prefix, and TrailingContent matches View Only Field for the same concept. Every addressable layer is semantically named; the skeleton loader keeps working names by design.Asset Slot and Counter Slot are real Figma Slots, and the chevron and counter come from library instances — teams drop in content without detaching. Stacks cleanly into lists.| 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. |
- Code Connect mappings not registered. Blocked — no native library exists yet. The schema is otherwise clean: three enums plus two swappable slots. C7 · Code Connect Linkability
- Three Action List components merged into Action Row. v2.0: Rebuilt on node
4628:19843in the 2026 Working File and renamed Action Row. Action List, Action List with Counter and Action List with Description are now one set: the counter is aTrailing=Countervalue, and the description is part of the row rather than a separate component. Confirmed as a permanent merge by the component owner. (C4 · Family) C4 · Native Mappability - Variant matrix is orthogonal and complete. v2.0:
Trailing(CTA · Counter) ×State(Default · Pressed · Disabled · Loading) ×Density(Compact · Expanded) = 16 variants, all authored.Densityis used exactly as §1 defines it — adjusting row height and padding, 56px against 64px — rather than gating content. (C2 · Property) C2 · Variant & Property Naming - Interaction states added. v2.0:
Statenow covers Default, Pressed, Disabled and Loading, where the original components had no state coverage at all. Pressed carries its own#F6F9FDsurface. (C5) C5 · Interaction State Coverage - Leading asset and counter are real Figma Slots. v2.0:
Asset SlotandCounter SlotareSLOTnodes, and the chevron and counter are library instances. Teams can drop in real content without detaching — the composition pattern the rest of the family has converged on. (C6 · Slot) C6 · Asset & Icon Quality - Frame naming aligned to the family. v2.1: The two frames both called
Containerare nowRowandTrailingGroup, and the spaced names are hyphenated to match the convention Toast, Upload File and Section Header settled on —Asset-Slot,Counter-Slot,Text-Container,Icon-Container,Bottom-Stroke. (C1) C1 · Layer Structure & Naming -
Trailingrenamed toTrailingContent. v2.1: The axis now carries the same name View Only Field uses for the same concept, satisfying §6 on consistent terminology. Any future component with a trailing area has one name to adopt rather than a choice between two. (C2 · Family) C2 · Variant & Property Naming - Text and action layer naming completed. v2.2:
#label→Labeland#blurb→Description, mapping onto §3 — the content is supporting text beneath a label rather than the promotional summaryBlurbdenotes.leading icon→Leading-IconandAction Button→Action-Buttoncomplete the hyphenated convention. (C1) C1 · Layer Structure & Naming -
hasDescriptionboolean added. v2.3: Confirmed by the component owner — ahasDescriptionboolean now toggles the description line, restoring the label-only row the original Action List provided. Implemented as a boolean component property rather than a fourth variant axis, so the matrix stays at 16 rather than doubling. Not independently verifiable from the assessment tooling, which cannot read component property definitions. (C2 · Property) C2 · Variant & Property Naming - Nested
Wrappercollision resolved. v2.3: TheWrapperframe nested inside anotherWrapperin the Loading variants is nowSkeleton-Trailing, naming what it actually holds. (C1) C1 · Layer Structure & Naming - Skeleton loader retained as authored. v2.3: Closed by owner decision — the Loading variants keep their skeleton geometry as built, including the repeated
trailing iconandlinerectangle names. These are internal placeholder shapes with no property surface and no override target, so their names never reach a consumer or a Code Connect mapping — the same reasoning applied to View Only Field's wrapper frames. (C1) C1 · Layer Structure & Naming
- Record
State=Loadingunder the existing State/Status exception.Loadingis a process status rather than an interaction state, the same shape asErroron the form fields. It falls under the exception already documented for that family rather than needing its own justification. Docs
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