East Blue
KeepReady
List Item Component link

One row in a list — a leading marker, a filling label, and an optional trailing slot. Three nesting levels set the indent.

Keep — composes the primitive properly and fills its container
The April assessment left three recommendations here. Two were taken directly: level became ListLevel, and the trailing slot that was only a suggestion now exists as ⤷ TrailingSlot behind a hasTrailing boolean. The third asked for the leading asset to become a Figma Slot; the rebuild kept it as a List Item - Asset instance instead, which reaches the same place by a better route — that component now carries its own Type=Slot, so arbitrary 16 × 16 content already has a way in without loosening this row to accept anything. This pass set #label to fill and closed the space in TrailingContainer. Nothing is outstanding on the component itself; Code Connect stays open because the native library does not exist yet.
In Context

Shown as three rows so the indent reads against the left edge — a single row tells you nothing about what ListLevel does. Lists appear on terms pages, onboarding steps and task checklists.

Live Preview
List body with level-1 style
List body with level-2 style
List body with level-3 style
Properties
ListLevel
hasLeading
hasTrailing
Nested instances
List Item - Asset
DS Health
Reusable
Pass
Terms pages, onboarding steps, task checklists — every bulleted or numbered list in the product is this row repeated. Nothing about it is tied to one surface.
Self-contained
Pass
Owns its indent, the gap to the label, and the label's type and colour, all bound to library variables. It does not own the marker's artwork — that belongs to the primitive it instances.
Consistent
Pass
ListLevel is PascalCase, hasTrailing follows the boolean prefix used by hasIcon and hasCTA, #label carries the # prefix and ⤷ TrailingSlot the arrow. TrailingContainer closed its space this pass.
Composable
Pass
It instances List Item - Asset for the marker and exposes a slot for trailing content, and #label fills — so the row stretches to whatever width the list gives it rather than sitting at a fixed size.
Behavior
State iOS Android Figma Property Notes
ListLevel=1 N/A N/A indent 0 Top-level row. The marker sits flush with the list's left edge.
ListLevel=2 N/A N/A indent 16 One level in. The label narrows by 16 because it fills the remaining width.
ListLevel=3 N/A N/A indent 32 Two levels in. Depth is carried by indentation alone — the marker and type stay the same at every level.
hasTrailing=false N/A N/A default TrailingContainer is hidden and the label runs to the right edge.
hasTrailing=true N/A N/A 16 × 20 Reveals ⤷ TrailingSlot at the end of the row. The slot ships empty.
Leading marker N/A N/A instance A List Item - Asset instance, Bullet by default. Swapping its Type is a nested instance property, not a property of this row.
Issues
  • The trailing slot exists now. The April assessment raised it as "consider a Trailing slot" — it is now ⤷ TrailingSlot inside TrailingContainer, revealed by a hasTrailing boolean that defaults to false. C4 · Native Mappability
  • The level property was renamed. level became ListLevel, matching the PascalCase convention used for variant properties across the system. C2 · Variant & Property Naming
  • The label fills instead of sitting at a fixed width. Every version is now 294 wide with #label at 277, 261 and 245 — the label absorbs the indent exactly, which is what fill looks like from the outside. A fixed label would have left the row unable to stretch to the width a real list gives it. C4 · Native Mappability
  • The leading asset stayed an instance, deliberately. The April assessment asked for it to become a Figma Slot so consumers could drop in any 16 × 16 component. It is still a List Item - Asset instance — and that is the better answer now, because the primitive carries its own Type=Slot. Arbitrary content gets in through there, while this row keeps the constraint that its marker is a real design system marker. C4 · Native Mappability
  • TrailingContainer closed its space. It was Trailing Container. Renamed this pass to match TransactionDetails and the other joined multi-word layer names in the system. C1 · Layer Structure & Naming
  • Depth is carried by indentation alone. All three levels use the same marker and the same type — BarkAda SemiBold 14 / 20 in #445C85. Nested lists often alternate markers so depth reads without counting indents, and Square was available for it. Confirmed as intentional: the indent is the signal. C1 · Layer Structure & Naming
  • The sample copy means indentation, not type. The placeholder strings read "List body with level-1 style" through to level-3, which can be read as promising three different text styles when the type is identical at every level. Confirmed that "style" refers to the visual treatment of the indentation. C1 · Layer Structure & Naming
Recommendations
  • The leading column is not pinned, which is what makes mixed markers break. #label starts 8px after the marker ends, and the marker's width is set by whichever List Item - Asset version is in the instance — 9 for Bullet and Square, 12 for Numbered, 16 for the status icons. Swapping Bullet for Check therefore pushes the label 7px right, so a list mixing markers loses its left alignment. Mixing is out of scope by design and users are briefed, so this is a note rather than a fault — but pinning the marker to a fixed 16 and centring inside it would remove the constraint altogether and make the briefing unnecessary. Composition
  • Confirm what a wrapping label does to the marker. The row is 20 tall with a single-line label. Real list copy wraps, and when it does the marker should stay pinned to the first line rather than centring against the whole block. Worth setting the cross-axis alignment explicitly so it does not depend on the default. Composition
  • Expose the list as a list to assistive tech. Rows should be children of a list container with their nesting level exposed, so a screen reader announces position and depth rather than reading a flat run of sentences. Indentation is a visual signal only — it does not reach assistive tech on either platform. A11y
  • Publish the token names once Dev Mode is read. #label resolves to a library variable — verified on the component's own node — but the read-only tools return variable IDs rather than names, so the spec tables carry the hex value only. Token
  • See siblings: List Item - Asset supplies the leading marker and is the only other component in the family. List was removed in the 2026 rebuild — it was a frame of eight hardcoded rows rather than a container, so rows are now stacked with auto layout and list-level concerns like row spacing sit with whatever composes them. Family
ListLevel
1
List body with level-1 style
Properties
hasLeading
hasTrailing
Nested instances
List Item - Asset
Properties
ListLevel 1
hasLeading true
hasTrailing false
⤷ TrailingSlot (slot) 3 items — ships empty in all three versions
Leading marker List Item - Asset, placed as Bullet
Versions 3
Colors
#label #445C85
Marker Set by the List Item - Asset version placed
Surface None — inherits the list background
Typography
#label Secondary/Bold/Base
Layout
Height 20 — Hug
Width 294 — Fill
Radius 0
Padding H 0 left · 0 right
Padding V 0
Gap 8
Alignment Top left
Colors by Level

The row paints one thing — its label. The marker's colour belongs to whichever List Item - Asset version is placed, which is why it is named here rather than given a value, and the surface is whatever the list sits on. Depth is carried by the indent alone: all three levels are identical in colour and in type.

Role Token Value
#label text/color-text-weak #445C85
Marker set by the List Item - Asset version placed —
Surface none — inherits the list background —
2
List body with level-1 style
List body with level-2 style
Properties
hasLeading
hasTrailing
Nested instances
List Item - Asset
Properties
ListLevel 2
hasLeading true
hasTrailing false
⤷ TrailingSlot (slot) 3 items — ships empty in all three versions
Leading marker List Item - Asset, placed as Bullet
Versions 3
Colors
#label #445C85
Marker Set by the List Item - Asset version placed
Surface None — inherits the list background
Typography
#label Secondary/Bold/Base
Layout
Height 20 — Hug
Width 294 — Fill
Radius 0
Padding H 16 left · 0 right
Padding V 0
Gap 8
Alignment Top left
3
List body with level-1 style
List body with level-2 style
List body with level-3 style
Properties
hasLeading
hasTrailing
Nested instances
List Item - Asset
Properties
ListLevel 3
hasLeading true
hasTrailing false
⤷ TrailingSlot (slot) 3 items — ships empty in all three versions
Leading marker List Item - Asset, placed as Bullet
Versions 3
Colors
#label #445C85
Marker Set by the List Item - Asset version placed
Surface None — inherits the list background
Typography
#label Secondary/Bold/Base
Layout
Height 20 — Hug
Width 294 — Fill
Radius 0
Padding H 32 left · 0 right
Padding V 0
Gap 8
Alignment Top left
Installation Planned API

iOS — Swift Package Manager

.package(url: "https://github.com/AY-Org/eb-ds-ios", from: "1.0.0")

Android — Gradle (Kotlin DSL)

implementation("com.eastblue.ds:list:1.0.0")

Import

import EastBlueDS
import com.eastblue.ds.list.*

Planned API — the native library does not exist yet. The artifact is the List family: List, List Item and List Item - Asset all ship in <code>com.eastblue.ds:list</code> and import <code>com.eastblue.ds.list.*</code>.

Property Mapping

Four properties: one enum-shaped variant, two booleans and a slot. Two native parameters have no row because Figma has no property behind them — label is the #label text layer, and marker is the nested List Item - Asset instance, which Figma lists under Nested instances rather than Properties. level is an Int, not a three-value enum. The Figma variant stops at 3 because a variant has to enumerate its values; a real list nests as deep as its content does, and capping the native API at three would bake a tooling limit into the platform. Same shape as hasWeek6 → weeks: Int on Date Picker - Calendar.

Figma PropertySwiftUICompose
ListLevel — 1, 2, 3 level: Int = 1 level: Int = 1
hasLeading — true, false hasLeading: Bool = true hasLeading: Boolean = true
hasTrailing — true, false hasTrailing: Bool = false hasTrailing: Boolean = false
⤷ TrailingSlot (slot) @ViewBuilder trailing: () -> Trailing — shown when hasTrailing trailing: @Composable (() -> Unit)? — shown when hasTrailing
Usage Snippets Planned API
1 — a flat list
EBListItem("Delivery fee", level: 1)
EBListItem(label = "Delivery fee", level = 1)
2 — one level in
EBListItem("Service charge", level: 2, marker: .square)
EBListItem(label = "Service charge", level = 2, marker = EBListItemAssetType.Square)
3 — as deep as the variants go
EBListItem("Processing", level: 3, marker: .checkPositive) {
    Image(systemName: "chevron.right")
}
EBListItem(label = "Processing", level = 3, marker = EBListItemAssetType.CheckPositive) {
    Icon(EBIcons.ChevronRight)
}
Accessibility
RequirementiOSAndroid
Rows are exposed as a list Wrap in a container with .accessibilityElement(children: .contain) and list traits Modifier.semantics { collectionInfo = CollectionInfo(rowCount, 1) }
Nesting level is announced Expose ListLevel in the row's accessibility value — indentation alone is invisible CollectionItemInfo carries the row's depth
Decorative markers are hidden Bullet, Square and Numbered are hidden; status markers put their meaning in the row's label Same split — see List Item - Asset
Label wrapping is not truncated No line limit on #label; the marker stays on the first line maxLines = Int.MAX_VALUE, marker aligned to the top
Usage Guidelines

Do

Let the row fill the width its list gives it.

Don't

Don't set a fixed width on the row — the label is built to absorb the space.

Do

Use ListLevel for depth and keep the marker the same throughout.

Don't

Don't mix markers within one list — widths differ from 9 to 16 and the labels stop aligning.

Do

Turn on hasTrailing only when there is something to put in the slot.

Don't

Don't leave an empty trailing slot switched on; it reserves 16px for nothing.

Do

Keep the gap between rows on the auto-layout frame that stacks them.

Don't

Don't add vertical padding inside the row; it will double up with the list's spacing.

Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Ready TrailingContainer closed its space this pass. #label and ⤷ TrailingSlot carry the right prefixes, and identical type across levels is confirmed intentional.
C2 Variant & Property Naming Ready ListLevel is PascalCase, and hasLeading and hasTrailing both follow the boolean prefix convention while staying off the variant name, so the set holds at three. The one caveat is that ListLevel has to enumerate at all — see C4.
C3 Token Coverage Ready #label resolves to a library variable and carries a shared library text style — verified on the component's own node. Marker colour comes from the primitive.
C4 Native Mappability Ready Maps to a single row view or composable. level is an Int natively rather than the three-value enum Figma shows: a variant has to enumerate, but a list nests as deep as its content does, and capping the API at three would bake a tooling limit into the platform. One constraint inherited from the marker: List Item - Asset is 9, 12 or 16 wide by version, so swapping the marker moves where the label starts. That is raised as a Design Recommendation on that component, not scored here.
C5 Interaction State Coverage Not Applicable Display only — the row is not tappable. Anything interactive belongs to whatever is dropped into the trailing slot.
C6 Asset & Icon Quality Ready No artwork of its own — the marker is a List Item - Asset instance and the trailing slot is whatever the consumer supplies.
Variants Inventory (3 total)

1 component set × 3 ListLevel values = 3 variants. hasTrailing is a boolean component property, so it toggles the trailing slot without doubling the set, and the marker is a nested instance rather than an axis.

ListLevelIndent#label widthRowNode
10277294 × 205800:39052
216261294 × 205800:39056
332245294 × 205800:39060
2.0.2 — September 2026Patch
Preview typeface corrected — node 5728:37276
The preview drew in the documentation font, and one layer was right only by accident. .eb-preview-litem declared font-family: inherit, which resolves to BarkAda. #label is Secondary/Bold/Base and genuinely is BarkAda, so it happened to look correct — but the marker inherited the same face, and the numbered marker is Primary/Label/Light/Small, which is Proxima Soft. The root now names Proxima Soft and #label names BarkAda for itself, so each layer states its own face rather than depending on what it inherits.
Docs
2.0.1 — September 2026Patch
Style and Code tabs rebuilt to the content guides — node 5728:37276
One card became three. ListLevel is the driving property, so each of its values gets a card. The single card was titled with the component’s own name and specified level 1 only.
Docs
hasLeading was undocumented. The Figma panel carries it, defaulting to True; neither the node tree nor this page had it at all. Nothing changed in Figma — get_node_info cannot read property definitions, and this is the fourth component in a row where the panel held more than the tree could show.
C2 resolved
The previews showed three rows all at the same level, which demonstrated nothing. The indent is the only thing ListLevel changes, and an indent is only legible against what it is indented from. Each card now renders the ladder down to its own level — level 1 alone, level 2 under a level 1, level 3 under both — which is also how the nesting actually works.
Docs
The four spec sections were in the wrong order. Properties, Colors, Layout, Typography; the order is Properties, Colors, Typography, Layout.
Docs
Layout had none of the seven keys. It carried Indent, #label width, Marker to label gap, Marker width and TrailingContainer. All seven are now read off the auto-layout panel: 20 — Hug, 294 — Fill, padding 0/16/32 left by level, gap 8, top left.
Docs
Typography was three font specs and an IOU. BarkAda SemiBold · 14 / 20 · 0, a Face row, a Per level row, and "shared library style · name pending Dev Mode read". #label resolves to Secondary/Bold/Base.
C3 resolved
The label colour is named, and the two it does not own are declared as such. #label is text/color-text-weak at #445C85. The marker’s colour belongs to whichever List Item - Asset version is placed and the surface to the list beneath, so the new Colors by Level table says so rather than inventing values for them.
C3 resolved
The live preview was missing a control and would have flashed. Its panel had no hasLeading, so the renderer was reading a control that did not exist and silently falling back to true; and its server-rendered markup did not match what the script produces, so it would have been replaced on load. Both the live preview and all three cards are now generated by the same renderer.
Docs
The nested-instance control was missing Slot. List Item - Asset has eight Type values and the dropdown listed seven. Slot draws as the same 16 × 16 dashed footprint the trailing slot uses, which is honest — they are the same thing, a reserved box the consumer fills.
Docs
The control panel mirrors Figma’s two blocks. Properties holds the two booleans; Nested instances holds List Item - Asset. Figma lists them separately because the marker belongs to that component rather than to this row, and reproducing the split says something true about ownership.
Docs
The install block pointed at coordinates that will never exist. gcash/east-blue-ios and com.gcash.eastblue:components:1.0.0, with no Import line. It now cites the List family artifact com.eastblue.ds:list:1.0.0 and imports com.eastblue.ds.list.*, matching List Item - Asset.
Docs
Property Mapping missed one property and mapped two things that are not properties. hasLeading had no row; #label is a text layer and the List Item - Asset instance is a nested instance, which Figma lists under Nested instances rather than Properties. Four rows now, one per panel property, with label and marker named in the description instead.
Docs
level is an Int, not a three-value enum. The Figma variant stops at 3 because a variant has to enumerate its values; a real list nests as deep as its content does, and capping the native API at three would bake a tooling limit into the platform. Recorded in the mapping description and in C4, alongside the hasWeek6 → weeks: Int precedent from Date Picker - Calendar.
C4 resolved
The Code tab contradicted the Style tab. Code emitted EBListItem("Top level", level: 1) and the Style tab EBListItem(level: 1, label: "List body"). The Style tab moved: an unlabelled leading string matches EBButton("Save Changes"), which is the house convention. Kotlin names the parameter, having no equivalent idiom.
Docs
Usage Snippets were keyed to use-cases, not to the property. "A flat bulleted list", "Nesting by level", "With trailing content". One per ListLevel value now, with marker and the trailing slot riding along on levels 2 and 3 rather than taking subheadings of their own.
Docs
Swapping the marker moves where the label starts. List Item - Asset is 9, 12 or 16 wide depending on its version, so a list mixing Bullet with Slot has its labels 7px apart. Recorded in C4 as an inherited constraint, pointing at the Design Recommendation being raised on that component rather than scored again here.
Docs
DEV code is live for the first time, and Code Connect is emptied. The demo script had no getSnippet, so both language tabs were frozen on a static string.
Docs
2.0.0 — August 2026Major
Rebuilt on the 2026 Working File · node 5728:37276
Trailing slot added — the April assessment raised it as a suggestion; ⤷ TrailingSlot now sits in TrailingContainer behind a hasTrailing boolean defaulting to false.
C4 resolved
level renamed to ListLevel, matching the PascalCase convention for variant properties.
C2 resolved
#label set to fill — every version is 294 wide with the label at 277, 261 and 245, absorbing the indent. The row now stretches to whatever width its list gives it.
C4 resolved
Leading asset kept as an instance — the April recommendation was to make it a Slot. Resolved by a different route: List Item - Asset now carries its own Type=Slot, so arbitrary content gets in without loosening this row.
C4 resolved
Trailing Container renamed to TrailingContainer.
C1 resolved
Identical type at all three levels confirmed intentional — depth is carried by indentation alone, and the sample copy's "style" refers to that indentation rather than to a text style.
C1 resolved
Node moved from 18482:34429 (Sticker Sheets v2) to 5728:37276 (2026 Working File).
Rebuilt
1.0.0 — April 2026Major
Initial Assessment · node 18482:34429
Component assessed — 3 variants (level 1/2/3). Composes List Item - Asset via instance swap. Documented
Initial
level uses string values — Should be integer or dropped in favor of nesting-based indent. Open
C2 Open
Asset is instance-swap — Adopt Figma Slots for first-class slot mapping. Open
C6 Open
Code Connect mappings — Not registered. Open
C7 Open