East Blue
KeepNeeds Refinement
Table Transaction Component link

A display-only transaction row — a header of column labels, or a content row with a full-width label above however many peso-amount cells you drop into its slot.

Keep — now built from the family's shared atoms
The rebuild turned this from a parallel re-implementation into a genuine composition. Its header slot holds the same Table Cell component that Table Row uses; its content slot holds the same Table Amount Cell that Table Scheduling uses. Settings are Role (Header / Content) and State (Default / Disabled), matching Table Row exactly, and no. of columns is gone — column count is however many cells sit in the ⤷ ColumnSlot. The raster peso is a Peso Sign - Proxima vector instance inherited from the shared amount cell. Kept as its own component because it carries a full-width #label row above its amount cells, which a plain Table Row doesn't. Rows are display-only, and amounts are unsigned by approved design. A later pass closed the last gap in that composition: the header had been overriding Table Cell's text style rather than asking for a different version of it, so the primitive gained Role=Transaction and the override is gone. Every colour is bound, and they are the same seven Table Row uses. The peso glyph is still an unflattened boolean operation — delegated to the Iconography team, and the one thing holding this at Needs Refinement.
In Context

Per-variant descriptions cite account limits — e.g. daily / monthly send caps showing used vs. remaining peso amounts in aligned columns. Other GCash surfaces like transaction history and receipts use the Generic Transaction Card vertical stack, not this tabular layout.

Account Limits USED REMAINING TOTAL Daily ₱3,500 ₱6,500 ₱10,000 Monthly ₱42,000 ₱58,000 ₱100,000 Request Increase
Live Preview
Column Label
Column Label
Column Label
Properties
Role
State
hasLabel
#label
hasBorder
⤷ ColumnSlot
Table Cell ⤷ AssetSlot
Cells
DS Health
Reusable
Pass
⤷ ColumnSlot takes any number of cells, and the shared Table Amount Cell carries its own ⤷ CurrencySlot — a non-peso currency is a swap, not a detach.
Self-contained
Pass
The currency prefix is a Peso Sign - Proxima vector instance inherited from the shared amount cell. The raster image fill and its remote asset URL are gone.
Consistent
Pass
Role and State match Table Row exactly, slots use the family's ⤷ …Slot form, and container is named the same way across all four versions. The no. of columns string-with-period property is gone.
Composable
Pass
Built from the family's own atoms — Table Cell in the header, Table Amount Cell in the content row. Nothing is re-implemented locally.
Behavior
State iOS Android Figma Property Notes
Header Yes Yes Role=Header 360 × 66 on a #F6F9FD ground. ⤷ ColumnSlot holds Table Cell copies at Role=Transaction, each with a 24 × 24 ⤷ AssetSlot above a Proxima Soft Semibold 12 label.
Content Yes Yes Role=Content 360 × 68 on white. A full-width #label in #6780A9 sits above a row of Table Amount Cell instances, each pairing a label with a peso-prefixed #value.
Disabled Yes Yes State=Disabled Text dims to #C2CFE5 across labels and values alike.
Pressed N/A N/A Not built Not needed — rows are display-only and carry no tap target.
Issues
  • The header cell has its own version instead of an override. v2.2: the header's Table Cell copies were painting over the primitive's text style — Table Row's header draws Primary/Label/Small, this one drew Primary/Multi-line Label/Light/Fine, from the same version of the same component. Table Cell now publishes a Role=Transaction version that carries that style itself, so the two consumers declare their difference rather than one of them quietly repainting it. C2 · Variant & Property Naming
  • The label belongs to the content row by design. v2.2: reviewed and settled. hasLabel and Label appear on the panel for both roles, but the Header version has no #label layer for them to act on — a header's column names live in its cells. Recorded so it is not re-raised as a missing layer. C2 · Variant & Property Naming
  • Every colour is bound to a named system colour. v2.2: checked against Figma's selection-colours panel, which the earlier tooling could not read. Seven paints, seven names, and they are the same seven Table Row uses — which is what makes the two read as one family rather than two components that happen to look alike. C3 · Token Coverage
  • It composes the family's atoms instead of duplicating them. v2.0: rebuilt on node 5896:39727. The header's ⤷ ColumnSlot holds the same Table Cell component Table Row uses; the content row holds the same Table Amount Cell Table Scheduling uses. What were three parallel re-implementations are now one family sharing parts. C1 · Layer Structure & Naming
  • no. of columns is gone. v2.0: column count comes from the number of cells in ⤷ ColumnSlot. The period-in-name string enum inherited from the old Table is no longer there to fix. C2 · Variant & Property Naming
  • The row maps to native primitives. v2.0: frames and slots throughout — a VStack / Column with a nested row of cells. No platform table primitive required. C4 · Native Mappability
  • Interaction states are intentionally minimal. v2.0: reviewed and settled. Rows are display-only, so pressed and selected are not needed. State=Disabled is built and dims text to #C2CFE5. Amounts carry no + / − prefix and no positive/negative colouring — that is the approved design, not a gap. C5 · Interaction State Coverage
  • The peso sign is a vector. v2.0: the raster image fill and its remote figma.com/api/mcp/asset/* URL are gone, replaced by a Peso Sign - Proxima instance in ⤷ CurrencySlot. The header's hardcoded #C2C6CF circle is now a ⤷ AssetSlot holding a swappable Placeholder. C6 · Asset & Icon Quality
  • Staying a separate component is intentional. v2.0: reviewed and dismissed. Transaction keeps its own record because its content row carries a full-width #label above the amount cells, which a plain Table Row entry doesn't. Same reasoning as Table Scheduling — and since both now build from shared atoms, the family reads as one. C1 · Layer Structure & Naming
  • container is named consistently. v2.1: all four versions use lowercase container. The Header versions previously used Container. C1 · Layer Structure & Naming
  • The shared amount cell has a family-neutral name. v2.1: Table Scheduling Amount Cell renamed to Table Amount Cell. It is used by both Scheduling and Transaction, so the old name claimed a family it no longer belonged to exclusively. The rename propagated to both components. C1 · Layer Structure & Naming
Recommendations
  • Document amount-sign semantics. Amounts here are unsigned by approved design — used, remaining, and cap totals rather than signed movements. Write that down so the component isn't reached for on flows that need + / −; those belong to Generic Transaction Card. Docs
  • Flatten the peso glyph. For the Iconography team: Peso Sign - Proxima wraps a shape_full BOOLEAN_OPERATION. Flattening it to a plain vector removes a class of export and scaling surprises. Shared with Table Scheduling. Asset
  • See siblings: Table Row and Table Scheduling — all three share Table Cell and Table Amount Cell. Keep slot naming and state coverage aligned across the family. Family
  • Align the amount label token with Inline Text. v2.2: Applied — the alignment happened by both sides moving to the same place. This row draws from the system-wide text/color-text-weaker and text/color-text, and the inline-text/color/* set the recommendation wanted to align with no longer exists in the file. There is nothing left to reconcile here. What remains is on Inline Text's own page, which still documents those retired tokens. Token
  • Audit the colour token bindings. v2.2: Applied — done from Figma's selection-colours panel. All five colours this named are bound (#F6F9FD, #E5EBF4, #0A2757, #6780A9, #C2CFE5), and so are the white background and the #D7E0EF asset placeholder it did not know about. Details in the Resolved tab. Token
  • Remove Table - Transaction from core DS, or fold it into Table as a recipe. v2.0: Settled — neither. It keeps its own record because its content row carries a full-width label above the amount cells, and it now composes the family's shared atoms rather than duplicating them. Family
  • Consolidate with Table's row primitive. v2.0: Applied in substance — the header slot holds Table Row's Table Cell and the content slot holds Table Amount Cell, so the parts are shared even though the record stays separate. Composition
  • Rename or drop no. of columns. v2.0: Applied — dropped. Column count comes from the number of cells in ⤷ ColumnSlot. Rename
  • Replace the raster peso sign with a text glyph or vector icon. v2.0: Applied — a Peso Sign - Proxima vector instance in ⤷ CurrencySlot, inherited from the shared amount cell. Asset
  • Give the container one name across all versions. v2.1: Applied — lowercase container on all four. Rename
  • Give the shared amount cell a family-neutral name. v2.1: Applied — Table Scheduling Amount Cell is now Table Amount Cell, propagated to both Scheduling and Transaction. Rename
Roles
Header
Column Label
Column Label
Column Label
Properties
State
hasLabel
hasBorder
Label
Properties
Role Header
State Default
hasLabel True
hasBorder True
Label Label
⤷ ColumnSlot 3 × Table Cell · Role=Transaction
Colors
Background #F6F9FD
Border #E5EBF4
Cell text #0A2757
Cell asset #D7E0EF
Typography
Table Cell #text Primary/Multi-line Label/Light/Fine
Layout
Height Hug · 66
Width 360
Radius 0
Padding H 24 · on container
Padding V 12 · on container
Gap 10 · on container
Alignment Left · Top
Colors by Role

Seven paints, and the seven names Figma's selection-colours panel lists for node 5896:39727 — the same set Table Row uses, which is what makes the two read as one family. The ⤷ ColumnSlot holds a different primitive per role: Table Cell at Role=Transaction in Header, Table Amount Cell in Content with its own label switched off, so the row's single #label covers all three columns.

Role Element Token Value
Header Background bg/color-bg #F6F9FD
— Border border/color-border-weak #E5EBF4
— Cell text text/color-text #0A2757
— Cell asset border/color-border #D7E0EF
Content Background bg/color-bg-main #FFFFFF
— Border border/color-border-weak #E5EBF4
— #label text/color-text-weaker #6780A9
— Cell amount · currency text/color-text #0A2757
Disabled Every text layer text/color-text-disabled #C2CFE5
— Background · border · asset — unchanged –
Content
Label
X,XXX.XX
X,XXX.XX
X,XXX.XX
Properties
State
hasLabel
hasBorder
Label
Properties
Role Content
State Default
hasLabel True
hasBorder True
Label Label
⤷ ColumnSlot 3 × Table Amount Cell · hasLabel=false
Colors
Background #FFFFFF
Border #E5EBF4
#label #6780A9
Cell amount · currency #0A2757
Typography
#label Primary/Multi-line Label/Light/Fine
Table Amount Cell #amount Primary/Label/Light/Fine
Layout
Height Hug · 68
Width 360
Radius 0
Padding H 24 · on container
Padding V 16 · on container
Gap 8 · on container
Alignment Left · Top
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:table:1.0.0")

Import

import EastBlueDS
import com.eastblue.ds.table.*
Property Mapping

Table Transaction publishes six properties. The rows beneath them map the two nested primitives — Table Cell and Table Amount Cell — neither of which has a page of its own; their Role and State are driven by the row, so neither takes a parameter here. One property behaves differently by role: ⤷ ColumnSlot holds Table Cell at Role=Transaction in a Header row and Table Amount Cell in a Content row, so it maps to two different arrays rather than one. And hasLabel with Label apply to Content only — the Header variant has no #label layer for them to act on, which is deliberate.

Figma PropertySwiftUICompose
Role — Header, Content role: .header / .content role = EBTableRowRole.Header / Content
State — Default, Disabled .disabled(true) enabled = false
hasLabel — true, false (Content only) label == nil — nil hides the line label == null
hasBorder — true, false showsDivider: Bool = true showsDivider: Boolean = true
Label (text, Content only) label: String? label: String? = null
⤷ ColumnSlot (slot) — Table Cell in Header, Table Amount Cell in Content columns: [Column] /amounts: [AmountCell] columns: List<Column> /amounts: List<AmountCell>
Table Cell → Text (text) Column.text: String Column.text: String
Table Cell → hasAsset — true, false Column.asset == nil — nil hides the slot Column.asset == null
Table Cell → ⤷ AssetSlot (slot) Column.asset: AnyView? Column.asset: @Composable (() -> Unit)?
Table Amount Cell → Amount (text) AmountCell.amount: String AmountCell.amount: String
Table Amount Cell → Label (text) AmountCell.label: String? AmountCell.label: String? = null
Table Amount Cell → hasLabel — true, false AmountCell.label == nil — false here; the row's own Label covers all columns AmountCell.label == null
Table Amount Cell → ⤷ CurrencySlot (slot) AmountCell.currency: AnyView? AmountCell.currency: @Composable (() -> Unit)?
SwiftUI
ios/Components/Table/EBTableTransactionRow.swift
Jetpack Compose
android/components/table/EBTableTransactionRow.kt
Usage Snippets Planned API
Header
// One Header row above the content rows it labels.
EBTableTransactionRow(
    role: .header,
    columns: [
        Column("Daily", asset: Image("calendar")),
        Column("Weekly", asset: Image("week")),
        Column("Monthly", asset: Image("month"))
    ]
)
// One Header row above the content rows it labels.
EBTableTransactionRow(
    role = EBTableRowRole.Header,
    columns = listOf(
        Column("Daily"),
        Column("Weekly"),
        Column("Monthly")
    )
)
Content
// The row's Label names all three columns at once.
EBTableTransactionRow(
    role: .content,
    label: "Transaction limit",
    amounts: [
        AmountCell("3,500.00"),
        AmountCell("20,000.00"),
        AmountCell("80,000.00")
    ]
)
// The row's Label names all three columns at once.
EBTableTransactionRow(
    role = EBTableRowRole.Content,
    label = "Transaction limit",
    amounts = listOf(
        AmountCell("3,500.00"),
        AmountCell("20,000.00"),
        AmountCell("80,000.00")
    )
)
Accessibility
RequirementiOSAndroid
Amount semantics Each amount cell should read as a single element — wrap peso + value and use .accessibilityLabel("Three thousand five hundred pesos"). Merge peso + value with Modifier.semantics(mergeDescendants = true); set contentDescription to a spoken phrase, not the raw glyph.
Currency glyph fallback Use Unicode \u{20B1} inline — avoid a raster image that won't scale with Dynamic Type. Use Unicode ₱ inline — avoid a bitmap that won't respect font-scale settings.
Header vs content row Header rows carry .accessibilityAddTraits(.isHeader). Header rows use Modifier.semantics { heading() }.
Column label / amount pairing Group the preamble label with each amount column so VoiceOver reads "Daily, three thousand five hundred pesos". Group the preamble label with each amount column so TalkBack reads "Daily, three thousand five hundred pesos".
Usage Guidelines

Do

Use one Header row at the top, naming the columns beneath it.

Don't

Don't repeat it between groups — the header's cells carry column labels, not group titles.

Do

Put the row's subject in Label and only figures in the cells.

Don't

Don't reach for hasLabel on a Header row — that variant has no #label layer, so nothing happens.

Do

Give every row in one table the same number of cells.

Don't

Don't mix 3 in the header and 4 below: the ⤷ ColumnSlot divides its 312px evenly, so the two rows land on 104px and 78px columns and nothing lines up.

Do

Let the platform format each amount and its currency symbol.

Don't

Don't ship ₱ as literal text beside a preformatted number — the glyph is a slot so it can follow the locale.

Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Ready One container per role, a single ⤷ ColumnSlot, and text layers on the #… form. The primitives it composes carry their own names rather than being redrawn here.
C2 Variant & Property Naming Ready Role and State are PascalCase with Title Case values, matching Table Row; two has* booleans and one text property named for what it holds. hasLabel and Label apply to Content only — deliberate, since the Header role has no label line.
C3 Token Coverage Ready Verified against the selection-colours panel: seven paints, seven names — bg/color-bg, bg/color-bg-main, border/color-border, border/color-border-weak, text/color-text, text/color-text-weaker and text/color-text-disabled. The same set Table Row uses, which is what makes the two read as one family.
C4 Native Mappability Ready A row of evenly divided cells on both platforms — no table primitive needed. The one wrinkle is that ⤷ ColumnSlot holds a different primitive per role, so it maps to two arrays rather than one.
C5 Interaction State Coverage Ready Display-only by design, so pressed and selected have nothing to show. State=Disabled dims every text layer to text/color-text-disabled and leaves background, border and asset alone. Both primitives publish their own Disabled version, so the row swaps to them rather than overriding fills per copy.
C6 Asset & Icon Quality Needs Refinement The Peso Sign - Proxima instance inside each Content cell draws an unflattened BOOLEAN_OPERATION rather than a flat vector. It renders correctly and is tracked as a design recommendation; flattening is the iconography team's to do.
Variants Inventory (4 total)

Role (2) × State (2) = 4 published versions. Two booleans and a text property sit on top without adding any — hasBorder on every row, hasLabel and Label on Content only — so the row has 8 combinations behind 4 versions. Cell count is not a version either: the ⤷ ColumnSlot takes however many cells you drop in, three in every published variant, and which primitive fills it follows the role — Table Cell at Role=Transaction in Header, Table Amount Cell in Content.

RoleStateDimensionsBackgroundNode
HeaderDefault360 × 66#F6F9FD5896:39740
HeaderDisabled360 × 66#F6F9FD5900:40254
ContentDefault360 × 68#FFFFFF5896:39762
ContentDisabled360 × 68#FFFFFF5900:40274
2.2.0 — September 2026Minor
Header override closed; Style, Code and Overview rebuilt to the content guides · node 5896:39727
The header cell has its own version instead of an override. The header's Table Cell copies were painting over the primitive's text style — Table Row's header draws Primary/Label/Small, this one drew Primary/Multi-line Label/Light/Fine, from the same version of the same component. Table Cell now publishes Role=Transaction (nodes 9567:96672 and 9567:96676) carrying that style itself. The defect was invisible from either side: both components looked correct alone, and only comparing the two consumers showed one repainting the other.
C2 Resolved
The label belongs to the content row by design. hasLabel and Label appear on the panel for both roles, but the Header version has no #label layer for them to act on — a header's column names live in its cells. Reviewed and recorded so it is not re-raised as a missing layer.
C2 Resolved
Every colour is bound to a named system colour. Checked against Figma's selection-colours panel: seven paints, seven names, and they are the same seven Table Row uses. This closes the standing recommendation to audit the bindings, which had listed five colours and missed the #D7E0EF asset placeholder.
C3 Resolved
The Inline Text alignment resolved itself. A recommendation asked this row's label and value tokens to align with Inline Text's. Both sides moved: this row draws from the system-wide text/* set, and the inline-text/color/* tokens no longer exist in the file. Nothing left to reconcile here — what remains is on Inline Text's own page, which still documents the retired tokens.
Token
The Style tab pointed at nodes that no longer exist. Both spec cards cited 47:324703 and 47:324708 from before the rebuild. Heights read 36 and 72.5 against a real Hug · 66 and Hug · 68, and both text style names were wrong — Primary/Multi-line Label/Light/Tiny for a style that is /Fine, and Primary/Label/Small for one that is Primary/Label/Light/Fine.
Docs
Layout is read rather than derived. The Auto layout panels settle it: padding sits on the inner container, 24 both sides with 12 top and bottom on Header and 16 on Content. The two containers also carry different gaps — 10 on Header, 8 on Content — both inert today since Header has a single child. Documented as Figma has them rather than normalised, since removing either could disturb something already placed.
Docs
Code tab rebuilt to the content guide. Installation was empty — no SPM, no Gradle, no import — and now takes the Table family's coordinates. Property Mapping went from 10 rows to 13, regrouped into prose, with Label added (it had no row at all) and the two nested primitives mapped properly. Usage Snippets split one per Role; four usage guidelines written where there were none.
Docs
One native name meant two primitives. Table Row maps Table Cell to Column, and Table Scheduling maps Table Amount Cell to Column as well. This row is where both appear together, so it would have been the first place they collided in one API. Table Amount Cell is AmountCell here; Table Scheduling's five rows still need the same rename.
Docs
Two versions of history were missing. The v2.0 and v2.1 work shipped together in 7f5cafa (August 2026) and never got changelog entries, while the Overview tab referenced both throughout. Both are written above, reconstructed from that commit and from the Overview's own version-tagged records.
Docs
2.1.0 — August 2026Minor
Naming pass on the rebuild · node 5896:39727
The container is named the same way in every version. The four versions had drifted onto different names for the same wrapper.
C1 Resolved
The shared amount cell got a family-neutral name. It had been named for the component that first used it, which reads wrong the moment a second one adopts it — as Table Scheduling and this row both now do.
C1 Resolved
2.0.0 — August 2026Major
2026 Working File · rebuilt on the family's atoms · node 5896:39727
It composes the family's atoms instead of duplicating them. The header row was a parallel re-implementation of what Table Row already drew. Its ⤷ ColumnSlot now holds the same Table Cell component, and the content row holds the same Table Amount Cell that Table Scheduling uses.
C1 Resolved
The column-count setting is gone. no. of columns fixed the number of cells at build time; the slot now takes however many you drop in.
C2 Resolved
The peso sign became a vector. It was a raster image that would not scale with Dynamic Type, and is now a Peso Sign - Proxima instance inherited from the shared amount cell.
C6 Resolved
The row maps to native primitives. Evenly divided cells in a stack — no platform table primitive needed on either side.
C4 Resolved
Disabled shipped; pressed and selected were dropped on purpose. The row is display-only and carries no tap target, so those two states have nothing to show.
C5 Resolved
Transaction stays its own component. The proposal was to remove it from core DS or fold it into Table Row as a recipe. It keeps a full-width label above a row of amount cells, which a standard row has no shape for, so it stays separate and shares the atoms instead.
C1 Resolved
1.0.0 — April 2026Major
Initial Assessment · node 47:324709
Family assessed — 6 variants (2 type × 2 columns × 2 icon, pruned). Only used for account-limit surfaces in the sticker sheet. Documented
Initial
Duplicate of Table — Reuses Table's variant schema with narrower column coverage and a peso-specialised content row. Recommend folding into Table as a recipe. Open
C1 Open
no. of columns inherited naming — String enum with a period in the property name, carried over from Table. Open
C2 Open
No native mobile primitive — Same mobile problem as Table; transaction totals should be Inline Text stacks. Open
C4 Open
No interaction or amount-sign states — No pressed / disabled, no positive / negative amount differentiation. Open
C5 Open
Peso sign is a raster asset — Content row currency prefix is an image fill, not a text glyph or vector. Header icon placeholder is still hardcoded #C2C6CF. Open
C6 Open
Code Connect mappings — Not registered. Open
C7 Open