A transaction-summary card with merchant info, amount, date, and tappable detail surface.
5488:32955 in the 2026 Working File. The type enum that packed five layouts into one axis is replaced by real Leading-Slot and Trailing-Slot nodes; the single state axis is split into State = Default | Pressed | Disabled beside Status = Default | Read | Skeleton per §6, so read-and-pressed is expressible; the heading weight matches Generic Card at Proxima Soft Bold 18/23; and pressed and disabled both ship. Naming is complete across every variant including the skeleton — no offset frames, no # sigil, no spaces, no duplicate names, and slots kebab-cased per §4. The label overflow is resolved, with the 270px LabelAmountRow budget honoured as 172 + 98. All four DS Health traits pass; the only item still open is Code Connect, blocked until the native library exists.Transaction-history rows stack vertically in the Activity / Transactions screen. Different rows use different variants depending on the context (recipient avatar, reference number, action menu).
Label
type enum needed five variants for.Avatar and Badge instances rather than redrawing them.State and Status are separate PascalCase properties with Title Case values per §1 and §5, following the §6 split. Layer naming is clean across all five variants including the skeleton: slots kebab-cased per §4, text layers on the §3 vocabulary, every frame named for what it holds, and no duplicates, sigils, spaces or offset frames left anywhere.leadingElement and trailingElement are both real Figma Slots, and the avatar, badge and trailing icon are all shared instances — the composition problem the previous assessment raised is solved.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default | Yes | Yes | type=default | Label + badge + date + amount. The baseline transaction row. |
| With avatar | Yes | Yes | type=with avatar | Adds a 32 × 32 Avatar at the leading edge. Used for person-to-person transactions. |
| More information | Yes | Yes | type=more information | Replaces the badge with an overflow menu button (⋯). Used when a row has context-menu actions. |
| No amount | Yes | Yes | type=no amount | Swaps the amount for a trailing badge; swaps date for a reference number. Used for confirmations without monetary value. |
| Skeleton loader | Yes | Yes | type=skeleton loader | Loading placeholder pattern. Worth documenting alongside Generic Card's skeleton as a DS-wide convention. |
| Pressed | N/A | N/A | Not built | Rows typically drill into transaction detail — need pressed tint for tap feedback. |
-
typeenum replaced by slot-based composition. v2.0: Rebuilt on node5488:32955in the 2026 Working File. The axis that packed five structurally different layouts into one enum is gone — the leading and trailing positions are now realSLOTnodes, so an avatar, an icon or nothing at all is the consumer’s choice rather than a variant. The set drops from a layout enum to five states. This is the headline recommendation applied. (C2 · Property) C2 · Variant & Property Naming -
no amountandmore informationvalues retired. v2.0: Both described what was absent rather than what the card was for, and both are gone with the enum that carried them. (C2 · Rename) C2 · Variant & Property Naming - Heading weight aligned with Generic Card. v2.0: The label is now Proxima Soft Bold 700 at 18/23, matching Generic Card’s
Titleexactly. The two cards sit next to each other in lists, so a Semibold/Bold split between them read as an error rather than a distinction. (C3) C3 · Token Coverage - Pressed and disabled states added. v2.0:
state=pressedfills the surface#F6F9FDandstate=disabledships alongside it, closing the missing-states finding. The card is a primary tap target in transaction lists, so touch-down feedback was the most-felt gap. (C5) C5 · Interaction State Coverage - Axis split into
StateandStatus. v2.1: Verified on the live node.State = Default | Pressed | Disablednow sits besideStatus = Default | Read | Skeleton, so interaction, content status and loading are three separate concerns rather than five values competing for one axis. Read-and-pressed is expressible, which in a transaction list is the common case. Both properties are PascalCase per §1 with Title Case values per §5, matching the split Generic Card made and the rule in §6. (C2 · Property) C2 · Variant & Property Naming - Slots brought onto the family convention. v2.1:
⤷ leadingElementand⤷ trailingElementare nowLeading-SlotandTrailing-Slot— the literal arrow character is gone and both follow §4 kebab-case with the-Slotsuffix used across Bottom Sheet, Alert and Generic Card. (C1 · Rename) C1 · Layer Structure & Naming - The three
offsetframes named, and the typo fixed. v2.1:offset→BadgeContainer,offset→AmountContainer, and the misspelledamout-offset→Amount. A newLabelAmountRownow holds the label and amount columns as an explicit 270px row — 172 + 98 — so the horizontal structure is declared rather than implied by position. (C1 · Rename) C1 · Layer Structure & Naming - Legacy sigil dropped and frames moved to PascalCase. v2.1:
#label→Label,#date→Date,#amount→Amount, on the §3 vocabulary. The frames follow —container→CardContainer,content-container→ContentContainer,transaction-detail→TransactionDetail— and the space inleading elementis closed asLeadingElement. (C1 · Rename) C1 · Layer Structure & Naming - Label overflow resolved. v2.2: Re-measured on the live node —
Label(5488:32986) is now 172px, matchingTransactionDetailexactly rather than running 33px past it. TheLabelAmountRowbudget of 270px is now honoured on both sides: 172 for the label column, 98 for the amount. Nothing overlaps, and a native implementation reading these bounds gets one consistent answer instead of two. (C4) C4 · Native Mappability - Skeleton brought onto the shared vocabulary. v2.2: The inner layers are renamed to match the default variant —
transaction-detail→TransactionDetail, the lastoffsetframe →BadgeContainer, andtag/header/badge→Tag/Header/Badge. No frame in the component is namedoffsetany more, and the skeleton no longer needs a second vocabulary to read. (C1 · Rename) C1 · Layer Structure & Naming -
Amountduplication resolved. v2.2: The trailing column now readsAmountRow→AmountContainer→Amount— three distinct names across three levels, with the text layer keeping the §3 vocabulary name and the wrappers describing what they hold. (C1 · Rename) C1 · Layer Structure & Naming
Label
| Role | Element | Token | Value |
|---|---|---|---|
| Default | Background | — | #FFFFFF |
| Default | Border | — | #E5EBF4 |
| Default | Label | — | #0A2757 |
| Default | Amount | — | #0A2757 |
| Default | Date | — | #6780A9 |
| Default | Badge background | — | #E5F1FF |
| Default | Badge label | — | #005CE5 |
| Default | Avatar background | — | #005CE5 |
| Default | Avatar initials | — | #FFFFFF |
| Default | Avatar border | — | #E5EBF4 |
| Default | Trailing icon | — | #005CE5 |
| Default · Pressed | Background | — | #F6F9FD |
| Default · Disabled | Label | — | #C2CFE5 |
| Default · Disabled | Amount | — | #C2CFE5 |
| Default · Disabled | Date | — | #C2CFE5 |
| Default · Disabled | Badge background | — | #D7E0EF |
| Default · Disabled | Badge label | — | #FFFFFF |
| Default · Disabled | Avatar background | — | #9BC5FD |
| Default · Disabled | Avatar initials | — | #F6F9FD @ 72% |
| Default · Disabled | Avatar border | — | #F6F9FD @ 72% |
| Default · Disabled | Trailing icon | — | #9BC5FD |
| Read | Amount | — | #90A8D0 |
| Skeleton | Background | — | #FFFFFF |
| Skeleton | Border | — | #E5EBF4 |
| Skeleton | Placeholder bars | — | #EEF2F9 |
| Skeleton | Leading placeholder | — | #EEF2F9 |
Label
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:card:1.0.0")
Import
import EastBlueDS import com.eastblue.ds.card.*
State and Status are the set’s two variant properties. Leading-Slot and Trailing-Slot are Figma Slots. Label, Amount, Date and the Badge are read from the layer tree of 5488:32979; whether they are exposed on the property panel is not readable with the plugin.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| State — Default, Pressed, Disabled | .disabled(true) for Disabled · Pressed is the platform touch-down | enabled = false for Disabled · Pressed from interactionSource |
| Status — Default, Read, Skeleton | .ebStatus(.read) · loading: true | status = EBTransactionStatus.Read · isLoading = true |
| Leading-Slot (slot) | leading: EBTransactionLeading — .avatar("G") | leading: @Composable (() -> Unit)? |
| Trailing-Slot (slot) | trailing: EBTransactionTrailing — .menu | trailing = EBTransactionTrailing.Menu |
| Label (text) | title: String | title = "…" |
| Amount (text) | amount: String | amount = "…" |
| Date (text) | date: String | date = "…" |
| Badge (nested Badge instance) | badge: EBBadge | badge = EBBadge("Label", EBBadgeIntent.Information) |
EBTransactionCard(
title: "Label",
amount: "XXX.XX",
date: "Date XX, XXXX, Time (AM,PM)",
badge: EBBadge("Label", intent: .information),
leading: .avatar("G"),
trailing: .menu
) EBTransactionCard(
title = "Label",
amount = "XXX.XX",
date = "Date XX, XXXX, Time (AM,PM)",
badge = EBBadge("Label", EBBadgeIntent.Information),
leading = { EBAvatar(initials = "G") },
trailing = EBTransactionTrailing.Menu
) EBTransactionCard(
title: "Label",
amount: "XXX.XX",
date: "Date XX, XXXX, Time (AM,PM)",
badge: EBBadge("Label", intent: .information),
leading: .avatar("G"),
trailing: .menu
)
.ebStatus(.read) EBTransactionCard(
title = "Label",
amount = "XXX.XX",
date = "Date XX, XXXX, Time (AM,PM)",
badge = EBBadge("Label", EBBadgeIntent.Information),
leading = { EBAvatar(initials = "G") },
trailing = EBTransactionTrailing.Menu,
status = EBTransactionStatus.Read
) EBTransactionCard(loading: true) EBTransactionCard(isLoading = true) | Requirement | iOS | Android |
|---|---|---|
| Row as a button | Whole row in Button; one accessibilityLabel combining label, badge, amount and date. | Modifier.clickable { onTap() }.semantics(mergeDescendants = true) on the row. |
| Amount announcement | Pass the amount through the localized currency formatter, not the raw string. | contentDescription carries the formatted amount. |
| Read status | .accessibilityValue("Read") when Status is Read — the grey amount alone is not announced. | stateDescription = "Read" in the row semantics. |
| Disabled | .disabled(true) adds the .notEnabled trait. | enabled = false sets the disabled semantics. |
| Loading | Announce "Loading" once; .accessibilityHidden(true) on the placeholder bars. | contentDescription = "Loading" on the skeleton container; placeholders cleared with clearAndSetSemantics. |
| Minimum target | 82 pt row ≥ 44 pt. | 82 dp row ≥ 48 dp. |
Do
Use Status Read once the user has opened the transaction.
Don't
Don’t use Read as a disabled look — Disabled is a State value.
Do
Keep the label to one line inside its 172 px column.
Don't
Don’t let a long counterparty name push the amount out of its 98 px column.
Do
Fill Leading-Slot with an Avatar or a merchant icon that identifies the counterparty.
Don't
Don’t put a second text layer or a badge in the slot.
Do
Show one Skeleton row per expected transaction while the list loads.
Don't
Don’t use Skeleton for a refresh of a single amount.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | PascalCase frames, Leading-Slot / Trailing-Slot named slots, no offset frames — read on 5488:32979. |
| C2 | Variant & Property Naming | Ready | State and Status split, Title Case values, no absence-based names. |
| C3 | Token Coverage | Ready | Label and Amount resolve to Primary/Headlines/Block, Date to Secondary/Bold/Caption. Colour variable bindings are not readable with the plugin — confirm in Dev Mode. |
| C4 | Native Mappability | Ready | Two enums, two slots, four text values — maps 1:1 onto EBTransactionCard. |
| C5 | Interaction State Coverage | Ready | Default, Pressed and Disabled built; Read and Skeleton exist at State Default only. |
| C6 | Asset & Icon Quality | Needs Refinement | Avatar and Badge are instances. The Trailing-Slot icon (Others) carries a shape_full BOOLEAN_OPERATION — flatten it in the icon library. |
3 State × 3 Status, with Read and Skeleton built at State Default only = 5 variants
| State | Status | Node | Dimensions |
|---|---|---|---|
| Default | Default | 5488:32979 | 360 × 82 |
| Pressed | Default | 5492:33839 | 360 × 82 |
| Disabled | Default | 5492:33889 | 360 × 82 |
| Default | Read | 5501:38441 | 360 × 82 |
| Default | Skeleton | 5488:33001 | 360 × 79 |
Status — Default, Read, Skeleton — with State as a panel control; colours, text styles and the 82 / 79 heights read off 5488:32979, 5501:38441 and 5488:33001. Padding, gap, alignment and colour tokens are not readable with the plugin and stay —.State × Status schema. Property Mapping is one row per property, Usage Snippets and Guidelines added, Scorecard regraded, Variants Inventory moved from the five type nodes to the five live nodes.shape_full; flatten it in the icon library. Filed from the scorecard for the Component Review.type enum replaced by slot-based composition; no amount and more information values retired; Heading weight aligned with Generic Card; Pressed and disabled states added; Axis split into State and Status; Slots brought onto the family convention; The three offset frames named, and the typo fixed; Legacy sigil dropped and frames moved to PascalCase; Label overflow resolved; Skeleton brought onto the shared vocabulary; Amount duplication resolved.type enum (5 layouts) with slot-based composition. Align heading weight with Generic Card. Add pressed state. OpenFull Width. Split into leadingMedia, badge, trailing, loading. Openno amount / more information describe what's missing. Semantic slot names replace them. Open