A display-only scheduled-payment row — a date and total on the first line, then however many label/amount cells you drop into its slot. Default and disabled states.
type enum with its sentence-shaped values ("2 amounts display") is gone, replaced by a ⤷ AmountRowSlot that takes however many Table Amount Cell instances you need. The raster peso is now a Peso Sign - Proxima vector instance sitting in a ⤷ CurrencySlot, used consistently on both the primary line and the detail cells — the old mix of a bitmap glyph and a literal "PHP" string is gone. Slot names match the Table Row convention, the details row is named the same way in both states, and the date now reads #month / #day / #year with its separators kept as plain layers. Code Connect stays unmapped because the native library doesn't exist yet. A later pass went through the primitive and the panel: Amount and Label became text properties so the row's own values are settable, Table Amount Cell's #value became #amount to match its property, its Disabled version now carries the dimming rather than each copy, and the disabled row got back the 16px gap it had lost under its breakdown. Every colour is bound. The peso glyph is still an unflattened boolean operation — delegated to the Iconography team, and the one thing holding this at Needs Refinement.Scheduled payments screen (auto-debit, installment plans, standing orders): a list of upcoming payment rows stamped with a date, the total debit amount, and — where relevant — a breakdown of principal / interest / fee components.
⤷ AmountRowSlot takes any number of detail cells rather than a fixed 0 / 2 / 4, and ⤷ CurrencySlot means a non-peso currency is a swap rather than a detach.Peso Sign - Proxima vector instance used the same way on the primary line and inside each detail cell. The raster asset and the literal "PHP" string are both gone.⤷ CurrencySlot, ⤷ AmountRowSlot), State is a PascalCase variant property, and both states name the details row identically. The date's three text properties are distinguishable from its separators. Text properties now match their layer names across the whole Table family, and Table Amount Cell carries its own Disabled version the same way the Table Row primitives do.Table Amount Cell instances placed in a slot, and each nests its own ⤷ CurrencySlot. The old inline re-implementation of the label / value pair is gone.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default | Yes | Yes | State=Default | 360 × 90. Date on the left at Proxima Soft Semibold 12, total in #005CE5 Bold 14, then a details row of label/amount cells. |
| Disabled | Yes | Yes | State=Disabled | Every text layer dims to #C2CFE5 — date, total, labels, and cell values alike. |
| Pressed | N/A | N/A | Not built | Not needed — the row is display-only and carries no tap target. |
- The peso glyph is still a boolean operation.
Peso Sign - Proximawraps ashape_fullBOOLEAN_OPERATION rather than a flattened vector. It renders correctly and is a clear improvement on the old raster, but boolean operations are the pattern this review process routes to the Iconography team to flatten. Delegated — not this component's owner to fix. C6 · Asset & Icon Quality
- The disabled row lines up with the default one again. v2.2:
State=Disabledhad lost the 16px gap between its row label and the breakdown cells, so its cells sat 16px left of where the same row sits enabled — a visible jump if a row ever toggled. Found by comparing the two exports side by side; the layer tree gives no hint of it. Restored. C1 · Layer Structure & Naming - Every text layer is named after the property that fills it. v2.2:
Table Amount Cell's value layer was#valuewhile its property wasAmount. It is#amountnow. The whole Table family follows one rule — the layer is the property name with a#in front. C1 · Layer Structure & Naming - The row's own text can be set from the panel. v2.2: the scheduled total and the breakdown label were bare layers — the component's headline value could not be set without selecting the layer.
AmountandLabelare text properties now, andTable Amount Cellgained aLabelof its own alongside itsAmount. C2 · Variant & Property Naming - The disabled look lives in the primitive, not in each copy. v2.2:
Table Amount Cellpublishes its own Disabled version, so a disabled row swaps to it rather than having its colours painted over copy by copy. Same pattern as theTable Rowprimitives. C5 · Interaction State Coverage - 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. Six paints, six names —
bg/color-bg-main,border/color-border-weak,text/color-text,text/color-text-primary,text/color-text-weaker,text/color-text-disabled. The two-tier currency is deliberate: the scheduled total sits ontext/color-text-primary, the itemised cells ontext/color-text. C3 · Token Coverage - The
typeenum is gone. v2.0: rebuilt on node5868:40468. The sentence-shaped values ("no display amount","2 amounts display","4 amounts display") are replaced by⤷ AmountRowSlot, which holds however manyTable Amount Cellinstances the surface needs. C2 · Variant & Property Naming - The peso sign is a vector. v2.0: the raster image fill is replaced by a
Peso Sign - Proximacomponent instance in a⤷ CurrencySlot, on both the primary line and inside each detail cell. The literal"PHP"prefix on detail amounts is gone, so one treatment now covers the whole component. The custom glyph is deliberate — it matches Proxima and takes less width than spelling outPHP. C6 · Asset & Icon Quality - The row maps to native primitives. v2.0: frames and slots throughout, so it builds as a
VStack/Columnwith a nested row of cells. No platform table primitive required. C4 · Native Mappability - Interaction states are intentionally minimal. v2.0: reviewed and settled. The row is display-only, so pressed is not needed.
State=Disabledis built for past, cancelled, or skipped entries and dims every text layer to#C2CFE5. C5 · Interaction State Coverage - Both states name the details row the same way. v2.1:
details-rowin Default (5868:40492) and Disabled (5878:41670) alike. The strayitem-rowis gone. C1 · Layer Structure & Naming - The date's text properties are distinguishable. v2.1: the five identically-named
#labellayers are now#month,separator,#day,separator,#year. The three a developer sets are obvious from the layer tree, and the two restylable separators no longer read as text properties. C2 · Variant & Property Naming - Staying a separate component is intentional. v2.0: reviewed and dismissed. Scheduling carries more controls than a standard Table Row entry — a date line, a primary total, and a variable row of label/amount cells — so it keeps its own record rather than folding into Table Row or shipping as a recipe. It now shares Table Row's slot naming, so the two read as one family. C1 · Layer Structure & Naming
- Collapse
Month,DayandYearinto oneDate. Three text fields hold one value, and all five layers involved — the three fields and the twoseparatorslashes — use the same text style and colour, so the split buys no styling control.Dateis the catalogue name for this;Month,DayandYearare not in it. It also makes the accessibility guidance actionable: the Code tab already tells developers to format with the reader's locale, and three preformatted strings make that impossible. Deferred rather than done, because collapsing it would break instances already placed on screens — worth doing the next time those screens are touched. Property - Flatten the peso glyph. For the Iconography team:
Peso Sign - Proximawraps ashape_fullBOOLEAN_OPERATION. Flattening it to a plain vector removes a class of export and scaling surprises. Asset - See siblings: Table Row — the standard row for column-aligned data. Scheduling stays separate because it carries a date line and a variable detail row on top of that; keep slot naming and state coverage aligned across both. Family
- Align the value layer name with Table Row. v2.2: Applied, by a different rule than the one proposed. This asked both components to use
#value. The family settled instead on the layer is named after the property that fills it — soTable Row's cell became#textfor itsTextproperty, and this one became#amountfor itsAmount. One rule that keeps holding as properties are added, rather than one shared word that has to be renegotiated each time. Rename - Document scheduling semantics. v2.2: Applied — the Code tab's first usage guideline now says the scheduled total goes on the primary line and only its parts in the breakdown cells, with the warning not to repeat the total as one of the cells. That was the guidance this asked for, in the place a developer reads before building. Docs
- Audit the colour token bindings. v2.2: Applied — done from Figma's selection-colours panel. All four colours this named are bound (
#0A2757,#6780A9,#005CE5,#C2CFE5), and so are the background and border. Details in the Resolved tab. Token - Rename
typevalues to integers, or drop the property. v2.0: Applied — dropped entirely. Detail count comes from the number of cells in⤷ AmountRowSlot. Rename - Unify the currency prefix treatment. v2.0: Applied — one
Peso Sign - Proximavector instance in a⤷ CurrencySlot, on the primary line and in every detail cell. The raster and the"PHP"literal are both gone. Asset - Compose each detail cell rather than re-implementing it. v2.0: Applied — detail cells are
Table Amount Cellinstances placed through a slot, each nesting its own currency slot. Composition - Add row interaction states if Scheduling stays. v2.0: Applied as far as it goes —
State=Disabledshipped. Pressed was reviewed and dropped: the row is display-only. State - Remove Table - Scheduling from core DS, or collapse the family. v2.0: Settled — neither. Scheduling stays its own component because it carries more controls than a standard row entry, and it now shares Table Row's slot conventions so the family reads as one. Family
- Give the details row one name across both states. v2.1: Applied —
details-rowin Default and Disabled alike. Rename - Name the date's separator layers distinctly from its text properties. v2.1: Applied —
#month,separator,#day,separator,#year. The three editable properties are now obvious from the layer tree. Rename
Six paints, and the six names Figma's selection-colours panel lists for node 5868:40468 — nothing is off-token. The currency glyph and amount are deliberately two-tier: text/color-text-primary on the scheduled total, text/color-text on the itemised breakdown beneath it.
| Role | Element | Token | Value |
|---|---|---|---|
| Default | Background | bg/color-bg-main | #FFFFFF |
| — | Border | border/color-border-weak | #E5EBF4 |
| — | Date | text/color-text | #0A2757 |
| — | Primary amount · currency | text/color-text-primary | #005CE5 |
| — | Labels | text/color-text-weaker | #6780A9 |
| — | Cell value · currency | text/color-text | #0A2757 |
| Disabled | Background | bg/color-bg-main | #FFFFFF |
| — | Border | border/color-border-weak | #E5EBF4 |
| — | Every text layer | text/color-text-disabled | #C2CFE5 |
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.*
Table Scheduling publishes ten properties. The four rows beneath them map Table Amount Cell, the primitive that fills ⤷ AmountRowSlot and has no page of its own; its State is driven by the row rather than set on its own, so it takes no parameter here. Two mappings are deliberately not one-to-one. Month, Day and Year are three Figma text fields for one value — natively that is a single date the platform formats for the reader's locale, which is what the Accessibility section below asks for and what three preformatted strings would prevent. And Amount is text in Figma because Figma has no number type; a native implementation should take a decimal and let the formatter supply both the grouping and the currency symbol.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| State — Default, Disabled | .disabled(true) | enabled = false |
| hasAmountRow — true, false | breakdown: [AmountCell]? — nil hides the row and its label | breakdown: List<AmountCell>? = null |
| hasBorder — true, false | showsDivider: Bool = true | showsDivider: Boolean = true |
| Label (text) | label: String | label: String |
| Amount (text) | amount: String | amount: String |
| Month (text) | date: Date — one value, formatted by the platform | date: LocalDate |
| Day (text) | date: Date | date: LocalDate |
| Year (text) | date: Date | date: LocalDate |
| ⤷ CurrencySlot (slot) | currency: AnyView? | currency: @Composable (() -> Unit)? |
| ⤷ AmountRowSlot (slot) — Table Amount Cell instances | breakdown: [AmountCell] | breakdown: List<AmountCell> |
| Table Amount Cell → Label (text) | AmountCell.label: String | AmountCell.label: String |
| Table Amount Cell → hasLabel — true, false | AmountCell.label == nil — nil hides it | AmountCell.label == null |
| Table Amount Cell → Amount (text) | AmountCell.amount: String | AmountCell.amount: String |
| Table Amount Cell → ⤷ CurrencySlot (slot) | AmountCell.currency: AnyView? | AmountCell.currency: @Composable (() -> Unit)? |
// The primary line carries the scheduled total; the cells itemise it. EBTableSchedulingRow( date: schedule.dueDate, amount: "1,250.00", label: "Monthly due", breakdown: [ AmountCell(label: "Principal", amount: "1,100.00"), AmountCell(label: "Interest", amount: "150.00") ] )
// The primary line carries the scheduled total; the cells itemise it. EBTableSchedulingRow( date = schedule.dueDate, amount = "1,250.00", label = "Monthly due", breakdown = listOf( AmountCell("Principal", "1,100.00"), AmountCell("Interest", "150.00") ) )
// For a schedule that has already run or been cancelled. EBTableSchedulingRow( date: schedule.dueDate, amount: "1,250.00", label: "Monthly due", breakdown: [ AmountCell(label: "Principal", amount: "1,100.00"), AmountCell(label: "Interest", amount: "150.00") ] ) .disabled(true)
// For a schedule that has already run or been cancelled. EBTableSchedulingRow( date = schedule.dueDate, amount = "1,250.00", label = "Monthly due", breakdown = listOf( AmountCell("Principal", "1,100.00"), AmountCell("Interest", "150.00") ), enabled = false )
| Requirement | iOS | Android |
|---|---|---|
| Row semantics | If tappable, wrap as Button with .accessibilityLabel("Scheduled payment, May 10 2026, 1,250 pesos") — combine the date and amount into one spoken phrase. | Wrap in Modifier.clickable + Modifier.semantics(mergeDescendants = true); set contentDescription to a full spoken phrase. |
| 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. |
| Date formatting | Use Date.FormatStyle with the user's locale; don't hardcode MMM DD, YYYY. | Use DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); don't hardcode format string. |
| Detail label / value pairing | Group each detail cell so VoiceOver reads "Principal, 1,100 pesos" as one element, not two. | Group each detail cell so TalkBack reads "Principal, 1,100 pesos" as one element, not two. |
| Disabled / past rows | Past or cancelled schedules: .accessibilityHint("Past payment") + muted label tokens. | Past or cancelled schedules: Modifier.semantics { stateDescription = "Past payment" } + muted label tokens. |
Do
Put the scheduled total on the primary line and only its parts in the breakdown cells.
Don't
Don't repeat the total as one of the cells — the primary line already states it, and a reader will add it twice.
Do
Keep the breakdown to two Table Amount Cells per line.
Don't
Don't add a third: the ⤷ AmountRowSlot is 201px with 16 of right padding and each cell is 72, so a third wraps to a second line and the row grows past 90.
Do
Pass a date and a decimal, and let the platform format both.
Don't
Don't ship MM / DD / YYYY or a hardcoded peso — those are Figma placeholders for layout, not the format a reader should see.
Do
Use Disabled for a schedule that no longer applies — already paid, or cancelled.
Don't
Don't use it to mean read-only. Every row is display-only; Disabled is about the schedule, not the interaction.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Text layers all take the #… form and both swappable areas the ⤷ …Slot form. Table Amount Cell's value layer is #amount, matching its Amount property — it was #value. |
| C2 | Variant & Property Naming | Ready | One State setting with standard values, two has* booleans, five text properties named for what they hold. Month, Day and Year would be one Date under the naming catalogue; they stay split because collapsing them would break instances already placed, and that is tracked as a recommendation rather than a defect. |
| C3 | Token Coverage | Ready | Verified against the selection-colours panel: six paints, six names — bg/color-bg-main, border/color-border-weak, text/color-text, text/color-text-primary, text/color-text-weaker and text/color-text-disabled. The currency glyph and total sit on text/color-text-primary while the breakdown sits on text/color-text, which is the component's own rule about primary versus itemised values. |
| C4 | Native Mappability | Ready | A stack of two rows with a wrapping list of cells — no platform table primitive needed. The only mapping that is not one-to-one is the date, which is three Figma fields and one native value. |
| 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 the background alone, and Table Amount Cell publishes its own Disabled version so the row swaps to it rather than overriding fills per copy. |
| C6 | Asset & Icon Quality | Needs Refinement | The Peso Sign - Proxima instance 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. |
State (2) = 2 published versions. Two booleans sit on top without adding any — hasAmountRow and hasBorder — so the row has 8 combinations behind 2 versions. The five text properties and the two swappable areas add none either: ⤷ AmountRowSlot takes however many Table Amount Cell copies you drop in, replacing the old type setting that hard-coded 0 / 2 / 4.
| State | Dimensions | Background | Node |
|---|---|---|---|
| Default | 360 × 90 | #FFFFFF | 5868:40481 |
| Disabled | 360 × 90 | #FFFFFF | 5878:41658 |
Column meant two different primitives. This page mapped Table Amount Cell to a native Column, while Table Row already used Column for Table Cell. Two pages, one name, two components. Table Transaction is where both appear together and would have collided in a single API, so it settled on AmountCell — and this page follows. The Figma side is unchanged; only the native type name moves.State=Disabled had lost the 16px gap between its row label and its breakdown cells, so its cells sat 16px left of where the same row sits enabled. Found by diffing the two exports — the layer tree gives no hint of it, since both variants report the same 95px label and a slot that simply fills what is left.Table Amount Cell's value layer was #value while its property was Amount; it is #amount now. This replaced a standing recommendation that both components adopt #value — the rule that survived is the more useful one, because it keeps holding as properties are added instead of having to be renegotiated each time.Amount and Label are text properties now, and Table Amount Cell gained a Label alongside its Amount.Table Amount Cell publishes its own Disabled version, so a disabled row swaps to it rather than having its colours painted over copy by copy — the same pattern as the Table Row primitives.text/color-text-primary, the itemised cells on text/color-text. This closes the standing recommendation to audit the bindings.State gives two. Height documented as 132.5 against a real Hug · 90. Five colour tokens — table/color/bg, table/color/label-amount, table/color/icon-currency-primary and two more — that do not exist in the file. And every colour and layout override keyed to a type control removed in v2.0, so none of them had fired since.container, an 8 gap between the two lines, and Auto horizontal spacing in the ⤷ AmountRowSlot with 16 of right padding. That last one explains the 72 + 41 + 72 + 16 the export measures, which is otherwise unguessable.Table family's coordinates. Property Mapping went from 9 rows to 14, regrouped into prose and extended to cover Table Amount Cell, whose whole API had been one row naming two layers. Usage Snippets split one per State; four usage guidelines written where there were none.Month, Day and Year onto a single native date, because the Accessibility section asks for locale formatting and three preformatted strings make that impossible. The Style tab's snippets were still emitting three parameters. Both say date: now, with the three controls moving a trailing comment. Collapsing the three Figma fields into one Date stays open as a recommendation — it would break instances already placed.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.State versions had drifted onto different layer names for the same row, which is the kind of thing that only shows up when someone diffs the two.#month, #day and #year are the fields; the two slashes between them are separator layers, and the names now say which is which.type held sentence-shaped values — "2 amounts display" — and fixed the number of breakdown cells at build time. The ⤷ AmountRowSlot now takes however many Table Amount Cell copies you drop in.Peso Sign - Proxima instance in a ⤷ CurrencySlot, used the same way on the primary line and inside every detail cell.Table Amount Cell copy placed in the slot, carrying its own ⤷ CurrencySlot, rather than a label and an amount re-implemented inline.State=Disabled covers a schedule that no longer applies.Table Row. It carries a date line and a variable breakdown row on top of what a standard row does, so it stays separate — with slot naming and state coverage kept aligned across both.type enum. Third parallel Table-family record (Table, Table - Transaction, Table - Scheduling). Documentedtype enum instead of reusing Table's type × no. of columns × icon or Transaction's schema. Recommend folding into Inline Text / Table data-driven row. Open"no display amount" / "2 amounts display" / "4 amounts display" bake the detail count into natural-language strings. Open"PHP" string. Open