East Blue
KeepNeeds Refinement
Modal - Transaction Receipt Component link

The receipt dialog — a title, a block of transaction rows, a copyable reference number, and one or two actions.

Keep — the receipt half of the old Modal, now standing on its own
This is the half of the old Modal that dealt with receipts, split out into its own component. The rebuild did the important thing right: ⤷ TransactionSlot holds real Modal - Transaction Receipt Entry instances rather than hand-drawn rows, so the primitive's renames flowed straight through. Layer names, the property name, both slot names and the button component were all brought into line during this assessment, and nothing is outstanding on the component itself — Code Connect stays open only because the native library does not exist yet. The Needs Refinement badge is about handoff rather than structure: the copy control has no confirmation state and its row falls short of the platform tap-target minimums.

Contexts are illustrative. Final screens will reference actual GCash patterns.

Put the title here
First line of text goes here
Second line of text goes here
LabelPut content here
LabelPut content here
LabelPut content here
Reference Number165A25912345
Label
Label
Properties
ActionOrientation
Content
#title
Reference #value
Reusable
Pass
Every receipt in the product is this component with different rows in the slot. Nothing about the structure is tied to one transaction type.
Self-contained
Pass
Carries its own surface, card, hairline, type styles and spacing. The scrim sits outside it deliberately — Overlay owns that, and this component owns the card.
Consistent
Pass
Content, TransactionDetails, Container, Reference, ReferenceNumber, ⤷ TransactionSlot and ⤷ ActionSlot all follow the family conventions after this pass, and ActionOrientation matches the property name on Modal.
Composable
Pass
It composes the row primitive three times through a real slot, and takes its buttons from Button_New rather than drawing them. Both slots accept substituted content without detaching.
State iOS Android Figma Property Notes
ActionOrientation=Vertical N/A N/A 320 × 434 Two full-width buttons stacked, primary on top. The action area is 156 tall.
ActionOrientation=Horizontal N/A N/A 320 × 376 Two 132-wide buttons side by side, primary on the right. The action area is 98 tall; everything above it is unchanged.
⤷ TransactionSlot N/A N/A 3 × Entry Holds Modal - Transaction Receipt Entry instances in their Inline layout. Row count is set by the content, not by a variant.
Copy N/A N/A 24 × 24 icon Copies the reference number. The whole reference row is the tap target, not just the icon.
  • Copying the reference number says nothing happened. The copy control has no confirmation state. Nothing in the component tells the user the tap worked — no toast, no icon swap, no label change — so the only feedback is the press itself, which is gone the moment the finger lifts. Copying an identifier is the one action on this screen where a user needs to know it landed, because the result is invisible until they paste it somewhere else. Which feedback to use is a product call; that there is none today is the gap. C5 · Interaction State Coverage
  • The transaction rows are real instances. ⤷ TransactionSlot holds three Modal - Transaction Receipt Entry instances. This is what proves the split worked — when the primitive's #name and #text were renamed to #label and #value, all three rows updated without being touched. C4 · Native Mappability
  • The property name lost its space. Action Orientation became ActionOrientation. It was the only variant property in the system with a space in it, and because the property name becomes the native parameter, a space would have forced a rename at the Code Connect boundary. C2 · Variant & Property Naming
  • Layer names match the rest of the family. content became Content, transaction-details became TransactionDetails, and container became Container — the PascalCase convention already settled on Date Picker, Voucher and Countdown. C1 · Layer Structure & Naming
  • The spacer-shaped name is gone. reference-offset became Reference. The old name described a layout side-effect rather than the content, which is the kind of name that survives into a native layout and confuses whoever reads it next. C1 · Layer Structure & Naming
  • The reference row says what it is. For Receipt became ReferenceNumber, and its two text layers picked up #label and #value to match the primitive's vocabulary even though the row is not an instance of it. The name landed spaced at first and was joined in a later pass, settling the convention against TransactionDetails beside it — the displayed copy still reads "Reference Number", since that is text rather than a layer name. C1 · Layer Structure & Naming
  • Both modals now use the same button. This component was instancing Button - Large/Medium while Modal used Button_New — two different masters with different internal structure in one family. Button_New won, so the label is addressable as #label in both. C4 · Native Mappability
  • The reference row is a deliberate duplicate, not an oversight. It is styled identically to a Modal - Transaction Receipt Entry in its Inline layout, and the obvious move is to instance it. Confirmed as intentional instead: it does not function as a transaction entry — it sits outside the receipt card on the grey strip and carries a copy control. It is documented here so the parallel styling is maintained on purpose rather than drifting by accident. C4 · Native Mappability
  • The title's tracking is a token style. #title is the only text in the component at 0 tracking while everything else sits at +0.25. Confirmed as the bound text style rather than a loose override. C3 · Token Coverage
  • No close control is intentional. There is no X and no scrim in the component. Dismissal runs through the action buttons, and the scrim belongs to Overlay — this component is the card only. C5 · Interaction State Coverage
  • Both slots carry the ⤷ prefix. Transaction Slot and Action Slot became ⤷ TransactionSlot and ⤷ ActionSlot on both variants. The prefix is how a slot is told apart from an ordinary frame at a glance, so a half-applied convention was worse than none — the Action Slot rename landed last and closed it. C2 · Variant & Property Naming
  • Give the reference row a real tap target height. The copy affordance is a 24×24 icon, below the 44×44 iOS and 48×48 Android minimums. Confirmed that the whole row is the tap target rather than the icon alone, which solves the width — but the row is only 14 tall inside a 40 tall strip, so the height still needs to reach 44. Raising the Reference frame to 48 and centring its contents covers both platforms without changing the layout. A11y
  • Keep the reference row in step with the primitive. It duplicates Modal - Transaction Receipt Entry's Inline styling by hand, deliberately. That means any change to the primitive's type or colour has to be mirrored here, and nothing in Figma will warn you. Worth a note on the component so the next person to touch the primitive knows to check this row. Docs
  • Publish the token names once Dev Mode is read. Fills across the component resolve to library variables and the text carries shared library styles — verified on the reference row's own nodes — but the read-only tools return IDs rather than names, so the spec tables carry hex values only. Token
  • See siblings: Modal - Transaction Receipt Entry supplies every row in the slot. Modal is the general-purpose dialog this was split away from. Overlay supplies the scrim behind both. Family
ActionOrientation
Horizontal
Put the title here
First line of text goes here
Second line of text goes here
LabelPut content here
LabelPut content here
LabelPut content here
Reference Number165A25912345
Label
Label
Properties
hasDescription
hasReferenceNo
Properties
ActionOrientation Horizontal
hasDescription true
hasReferenceNo true
⤷ ActionSlot (slot) 2 items · horizontal flow, 320 × 98, 24px padding, 8px gap — the two buttons sit side by side
⤷ TransactionSlot (slot) 2 items · holds 3 Modal - Transaction Receipt Entry instances at Layout=Inline
Reference strip 320 × 40 · horizontal, 24px sides, 16px top and none at the bottom, gap 0 — the 40 is the Copy icon’s 24 plus that 16
Action order Outlined left, filled right — the platform convention for a side-by-side pair
Versions 2
Colors
Surface #F6F9FD
Content card #FFFFFF
Card hairline #E5EBF4
#title · #description · every #value #0A2757
Every #label #6780A9
Copy icon #005CE5
Typography
#title Primary/Headlines/Section
#description Primary/Multi-line Label/Light/Base
Reference #label · #value Primary/Label/Light/Small
ActionSlot #label Primary/Label/Large
Layout
Height 376 — Hug
Width 320
Radius 6
Padding H 0
Padding V 0
Gap 0
Alignment Top left
Colors by Orientation

Ten roles, identical in both orientations — the action area changes shape, not colour. The last four belong to the appearance/* set, which is the Button’s own variable mode rather than anything this modal sets: the buttons arrive through ⤷ ActionSlot and bring their palette with them. The transaction rows are the same: they are Modal - Transaction Receipt Entry instances, so their label and value colours are that component’s.

Role Token Value
Surface bg/color-bg #F6F9FD
Content card bg/color-bg-main #FFFFFF
Card hairline border/color-border-weak #E5EBF4
#title · #description · every #value text/color-text #0A2757
Every #label text/color-text-weaker #6780A9
Copy icon border/color-border-primary #005CE5
ActionSlot · filled fill appearance/container/fill #005CE5
ActionSlot · filled label appearance/label/color #FFFFFF
ActionSlot · outlined stroke appearance/stroke/color #005CE5
ActionSlot · outlined label appearance/label/on-surface/color #005CE5
Vertical
Put the title here
First line of text goes here
Second line of text goes here
LabelPut content here
LabelPut content here
LabelPut content here
Reference Number165A25912345
Label
Label
Properties
hasDescription
hasReferenceNo
Properties
ActionOrientation Vertical
hasDescription true
hasReferenceNo true
⤷ ActionSlot (slot) 2 items · vertical flow, 320 × 156, 24px padding, 8px gap — the two buttons stack
⤷ TransactionSlot (slot) 2 items · holds 3 Modal - Transaction Receipt Entry instances at Layout=Inline
Reference strip 320 × 40 · horizontal, 24px sides, 16px top and none at the bottom, gap 0 — the 40 is the Copy icon’s 24 plus that 16
Action order Filled on top, outlined beneath
Versions 2
Colors
Surface #F6F9FD
Content card #FFFFFF
Card hairline #E5EBF4
#title · #description · every #value #0A2757
Every #label #6780A9
Copy icon #005CE5
Typography
#title Primary/Headlines/Section
#description Primary/Multi-line Label/Light/Base
Reference #label · #value Primary/Label/Light/Small
ActionSlot #label Primary/Label/Large
Layout
Height 434 — Hug
Width 320
Radius 6
Padding H 0
Padding V 0
Gap 0
Alignment Top left

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:modal:1.0.0")

Import

import EastBlueDS
import com.eastblue.ds.modal.*

Planned API — the native library does not exist yet. The artifact is the Modal family: Modal, this receipt and <a href="/components/modal-transaction-receipt-entry">Modal - Transaction Receipt Entry</a> all ship in <code>com.eastblue.ds:modal</code> and import <code>com.eastblue.ds.modal.*</code>.

Five properties, in the order the Figma property panel lists them. Three native parameters have no row because Figma has no property behind them — title, description and the reference value are text layers, so a designer sets them by overriding the layer. Each boolean keeps its own parameter alongside the content it gates rather than the content being inferred from a nil, so every Figma property traces 1:1 for Code Connect. hasReferenceNo keeps Figma's spelling for the same reason. actionOrientation is a parameter in both languages rather than a SwiftUI modifier: it changes the layout of the action area, not the component's appearance.

Figma PropertySwiftUICompose
ActionOrientation — Horizontal, Vertical actionOrientation: EBActionOrientation actionOrientation: EBActionOrientation
⤷ ActionSlot (slot) @ViewBuilder actions: () -> Actions actions: @Composable () -> Unit
hasDescription — true, false hasDescription: Bool = true hasDescription: Boolean = true
hasReferenceNo — true, false hasReferenceNo: Bool = true hasReferenceNo: Boolean = true
⤷ TransactionSlot (slot) @ViewBuilder entries: () -> Content entries: @Composable ColumnScope.() -> Unit
EBTransactionReceiptModal(
    title: "Payment successful",
    actionOrientation: .horizontal
)
EBTransactionReceiptModal(
    title = "Payment successful",
    actionOrientation = EBActionOrientation.Horizontal
)
EBTransactionReceiptModal(
    title: "Payment successful",
    actionOrientation: .vertical
)
EBTransactionReceiptModal(
    title = "Payment successful",
    actionOrientation = EBActionOrientation.Vertical
)
RequirementiOSAndroid
Dialog announced on present .accessibilityAddTraits(.isModal), focus moves to #title Dialog with Modifier.semantics { paneTitle = title }
Each receipt row reads as one unit Handled by the entry primitive's .combine Handled by the entry primitive's mergeDescendants
Copy control has a name and a tap target .accessibilityLabel("Copy reference number"), 44×44 minimum contentDescription = "Copy reference number", 48dp minimum
Copy result is announced UIAccessibility.post(.announcement, "Copied") announceForAccessibility("Copied")
Primary action is reachable first Primary precedes secondary in the accessibility order in both orientations Primary precedes secondary in the traversal order in both orientations

Do

Use vertical actions when both labels are long or the secondary action matters.

Don't

Don't use horizontal actions with labels that will truncate at 132px.

Do

Fill the slot with entry instances so type and colour stay inherited.

Don't

Don't detach a row to change one value — override the text on the instance.

Do

Keep the reference number in the grey strip where the copy control lives.

Don't

Don't add the reference as a fourth row in the transaction slot; it has different behaviour.

Do

Pair the modal with Overlay for the scrim.

Don't

Don't build a scrim into the card — this component is the card only.

ID Criterion Status Notes
C1 Layer Structure & Naming Ready Content, TransactionDetails, Container, Reference and ReferenceNumber all renamed to the family convention this pass.
C2 Variant & Property Naming Ready ActionOrientation closed its space and both slots picked up the ⤷ prefix. hasReferenceNo. lost its trailing period this pass — it would not have survived as a native identifier in either language, and the name now traces 1:1. Nothing outstanding on this axis.
C3 Token Coverage Ready Every colour is bound and named. Six belong to this component — bg/color-bg, bg/color-bg-main, border/color-border-weak, text/color-text, text/color-text-weaker and border/color-border-primary. The other four are the appearance/* set, which is the Button’s own variable mode arriving through ⤷ ActionSlot rather than anything this modal sets. All five text layers resolve to shared library styles.
C4 Native Mappability Ready A column of text, a repeated row, a strip and an action area. Both slots map to view builders and the transaction rows are Modal - Transaction Receipt Entry instances, so the composition maps as cleanly as the primitive does. The one gap is that the three strings are text layers rather than properties — see C7.
C5 Interaction State Coverage Needs Refinement Button states come from Button_New, and the transaction rows are display-only. What is missing is the copy control’s confirmation state — nothing in the component says a tap succeeded. The reference strip’s 40px height also falls short of the 44/48 tap minimum; the component ships live, so that is raised as a Design Recommendation rather than scored here.
C6 Asset & Icon Quality Ready The copy glyph is a clean vector instance. Its master carries hidden icon-grid scaffolding, which is an icon-library concern rather than a fault here.

1 component set × 2 ActionOrientation values = 2 variants. Everything above the action area is identical between them; row count comes from the slot, not from a variant.

ActionOrientationSizeAction areaButtonsNode
Vertical320 × 434320 × 1562 × 272 × 50, primary on top5879:41046
Horizontal320 × 376320 × 982 × 132 × 50, primary on right5945:180029
The missing copy confirmation is on the record — node 5879:41048
C5 was flagged with nothing behind it. The scorecard had read Needs Refinement and the reason sat only in its own note. The gap is now an Open Issue: the copy control has no confirmation state, so nothing tells the user the tap worked. Copying an identifier is the one action here where that matters, because the result stays invisible until it is pasted somewhere else.
C5 open
The same finding had been sitting in two lists. A State recommendation asked for the confirmation to be settled while the scorecard called it missing. One finding, one place — it is an Open Issue, and the half worth keeping from the recommendation, that which feedback to use is a product call, folded into it.
Docs
hasReferenceNo renamed; Style and Code tabs rebuilt — node 5879:41048
hasReferenceNo. lost its trailing period. The name would not have survived as a native identifier in either language, so the mapping would have had to rename it and break the 1:1 trace Code Connect needs. It is hasReferenceNo in Figma and in both platform signatures now, spelled the same in all three.
C2 resolved
One card became two. ActionOrientation is the driving property, so Horizontal and Vertical each get a card, in the panel’s order. The single card was titled with the component’s own name and specified Vertical only.
Docs
Two properties were undocumented. The Figma panel carries hasDescription and hasReferenceNo, both defaulting to True; neither was on the page. Nothing changed in Figma for these two — get_node_info cannot read property definitions, and this is the fifth component in this run of reviews where the panel held more than the tree could show.
C2 resolved
Layout described the children rather than the component. Thirteen rows including Card inset, Row pitch, Button gap and Button radius. The seven canonical keys now come off the container’s own auto-layout panel: 434 or 376 tall and hugging, 320 wide, radius 6, no padding, no gap, top left.
Docs
The 58px between the two variants is one thing, not several. Both action slots carry the same 24px padding and 8px gap; only the flow changes. Vertical stacks two 272-wide buttons for 156; Horizontal puts two 132-wide buttons side by side for 98. Recorded on each card’s Properties row, where the old page implied two unrelated action-area specs.
Docs
The reference strip had invented padding. The preview drew 13px 24px — symmetric, and not what the panel says. It is 24 at the sides, 16 on top and none at the bottom, with a gap of 0, which is why the strip had been floating away from the action area. Its 40px height is the Copy icon’s 24 plus that 16.
Docs
Five text styles were an IOU. The page carried five font specs and "shared library styles · names pending Dev Mode read". #title is Primary/Headlines/Section, #description is Primary/Multi-line Label/Light/Base, the reference row is Primary/Label/Light/Small, and the action label is Primary/Label/Large.
C3 resolved
All ten colours are named, and four of them are not this component’s. Six belong here — bg/color-bg, bg/color-bg-main, border/color-border-weak, text/color-text, text/color-text-weaker, border/color-border-primary. The other four are the appearance/* set, which is the Button’s own variable mode arriving through ⤷ ActionSlot. The transaction rows are the same story: they are Entry instances, so their colours are that component’s.
C3 resolved
The preview drew in the documentation font. .eb-preview-mtr declared font-family: inherit, which resolves to the site’s --font-body — BarkAda. Every text layer here carries a Primary/* style and is Proxima Soft in Figma, so the whole card was rendering in the wrong face. The root now names Proxima Soft; no per-layer override is needed, because nothing in this component is Secondary.
Docs
Both previews are server-rendered. The static markup on each card now holds the component’s real default state, generated by the same renderer the demo uses, so what ships before JavaScript runs matches what runs after it.
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 Modal family artifact com.eastblue.ds:modal:1.0.0 and imports com.eastblue.ds.modal.*.
Docs
Property Mapping missed two properties and mapped three things that are not properties. Six rows became five: the two booleans gained rows, and #title, #description and the reference value came out — all three are text layers. They are named in the description instead, since they still have to be parameters natively.
Docs
The two tabs declared different APIs. The mapping had .ebActionOrientation(...) — a SwiftUI modifier — against its own Compose column’s parameter, while the Style tab used a parameter in both. Settled on the parameter: actionOrientation changes the layout of the action area, not the component’s appearance. The same disagreement was found and settled the same way on the Entry primitive.
Docs
A snippet claimed a default that could not be read. "Vertical actions (default)"; the property panel lists Horizontal first, so naming Vertical the default was a guess. Both snippets are keyed plainly to the values now and ordered to match the panel and the cards.
Docs
Three strings have nothing to bind to. Because title, description and the reference value are text layers rather than component properties, Code Connect will have no anchor for them when the native library lands. The five properties that do exist all trace 1:1. Recorded against C7, which was already open on registration.
C7 open
C5 is narrowed to one item. Its note bundled the copy control’s missing confirmation state with the reference strip’s 40px height. The height falls short of the 44/48 tap minimum, but the component ships live, so that is raised as a Design Recommendation rather than scored. The confirmation state is a genuine missing interaction state and holds C5 open.
C5 open
Code Connect emptied, and DEV code is live for the first time. The demo script had no getSnippet, so both language tabs were frozen on a static string; they now track both booleans.
Docs
First assessment · node 5879:41048
New component. Split out of the old Modal (18507:71705), which was carrying both a general-purpose dialog and this receipt layout in one set.
Added
Action Orientation renamed to ActionOrientation — the only variant property in the system that still had a space in it.
C2 resolved
content, transaction-details and container renamed to PascalCase; reference-offset renamed to Reference.
C1 resolved
For Receipt renamed to ReferenceNumber, with its text layers renamed to #label and #value. The space was closed in a follow-up so the layer matches TransactionDetails; the displayed copy still reads "Reference Number".
C1 resolved
Transaction Slot and Action Slot renamed to ⤷ TransactionSlot and ⤷ ActionSlot.
C2 resolved
Buttons switched from Button - Large/Medium to Button_New, so both modals in the family share one button component.
C4 resolved
Renamed from Transaction Receipt Modal to Modal - Transaction Receipt.
C2 resolved