East Blue
KeepReady
Modal Component link

The general-purpose dialog — an optional illustration, a centred title and description, and one or two actions.

Keep — the restructure the first assessment asked for has happened
The April assessment flagged this component for carrying two unrelated things in one set: a general-purpose dialog and a transaction receipt. It recommended splitting them and extracting EBTransactionReceipt. That is exactly what the 2026 rebuild did — the receipt layouts are now Modal - Transaction Receipt, and what remains here is the dialog on its own. The _space_* spacer rectangles are gone, the hardcoded placeholder circle is a real ⤷ IconSlot, and the mixed-casing enums are one clean ActionOrientation. Nothing is outstanding on the component itself; Code Connect stays open because the native library does not exist yet.

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

Icon
Put the title here
Add description here.
Add description here.
Label
Label
Properties
ActionOrientation
hasIcon
Content
#title
#description
Reusable
Pass
Now that the receipt layouts have moved out, this is a dialog and nothing else — a slot, two text layers and an action area. Any confirmation, warning or prompt in the product is this component with different content.
Self-contained
Pass
Carries its own surface, radius, type styles and spacing, all bound to library variables. The scrim sits deliberately outside it in Overlay, which is what resolved the duplicate-scope flag from the first assessment.
Consistent
Pass
Container, Content, ⤷ IconSlot, ⤷ ActionSlot, #title and #description all match the conventions settled across Date Picker, Voucher and Countdown. hasIcon follows the boolean prefix already used by Ad Carousel's hasCTA.
Composable
Pass
Two real slots, both accepting substituted content without detaching. The buttons come from Button_New, the same component the receipt modal now uses.
State iOS Android Figma Property Notes
ActionOrientation=Vertical N/A N/A 320 × 370 Two full-width buttons stacked, primary on top. The action area is 156 tall.
ActionOrientation=Horizontal N/A N/A 320 × 312 Two 132-wide buttons side by side, primary on the right. Everything above the action area is unchanged.
hasIcon=true N/A N/A default Shows the 92 × 92 illustration slot with its Placeholder instance.
hasIcon=false N/A N/A collapsed Collapses the slot and the 16px below it, taking 108 off the height.
  • The receipt layouts moved out. The first assessment flagged this set for compressing a general-purpose dialog and a transaction receipt into one component with different information architectures and different native mappings. transaction_v1 and transaction_v2 are now Modal - Transaction Receipt, and the row inside it is Modal - Transaction Receipt Entry. C4 · Native Mappability
  • The spacer rectangles are gone. The old build used opacity-0 _space_* rectangles to fake gaps. Spacer nodes have no native equivalent — auto layout spacing does the job — and the rebuild removed them. C1 · Layer Structure & Naming
  • The placeholder circle is a real slot. The illustration used to be a hardcoded circle drawn into the component. It is now ⤷ IconSlot, a 92 × 92 slot holding a Placeholder instance that consumers swap. C6 · Asset & Icon Quality
  • The illustration can be switched off. The slot was empty and always on, so every dialog reserved 116px for an illustration whether or not it had one, with no way to collapse it. A hasIcon boolean now controls it, defaulting to true, and a Placeholder gives consumers a starting point. C5 · Interaction State Coverage
  • The enums are one clean property. The old set mixed casing and styles across two axes — transaction_v1 alongside 1 - vertical — with a sparse CTA matrix. What is left is ActionOrientation = Vertical | Horizontal, renamed during this pass to close the space it used to carry. C2 · Variant & Property Naming
  • Both modals share one button. This component was on Button_New while the receipt modal was still on Button - Large/Medium — two masters with different internal structure in one family. The receipt moved across, so the label is addressable as #label in both. C4 · Native Mappability
  • The BarkAda description is deliberate. The first assessment left this open as a question for design. Confirmed: BarkAda is the secondary face, used for longer copy at smaller sizes, while Proxima Soft carries titles, labels and shorter or larger text. That is why this description is BarkAda 14/20 and the receipt modal's is Proxima Soft 16/20 — a rule, not drift. C3 · Token Coverage
  • The Container wrapper earns its place. It has the same bounds and radius as the variant frame around it, which usually means a level that can be removed. Confirmed that it holds the auto layout, so it stays. C1 · Layer Structure & Naming
  • Centred here, left-aligned in the receipt. #title and #description are centred in this component and left-aligned in Modal - Transaction Receipt. Confirmed as intentional: a confirmation dialog centres, and a receipt is left-aligned tabular data. C1 · Layer Structure & Naming
  • Tidy the Placeholder's own layer names. The instance filling ⤷ IconSlot wraps a lowercase container frame. It belongs to the Placeholder component rather than to Modal, so it is not a fault here, but it is the same PascalCase convention the rest of the family has adopted and worth passing upstream. Docs
  • Give the dialog a loading state. Confirmations that trigger a network call leave the primary button with nowhere to go while it waits. The first assessment raised this and it is still true. Whether it lives on the button or on the modal is a Button family decision, so it belongs there rather than here. State
  • Publish the token names once Dev Mode is read. Fills resolve to library variables and the text carries shared library styles — verified on the component's own nodes, and #description shares its variable with the receipt modal's row labels. The read-only tools return IDs rather than names, so the spec tables carry hex values only. Token
  • Pair it with Overlay in documentation. This component is the card. The scrim is Overlay. That split resolved the duplicate-scope flag from the first assessment, but it only works if both pages say so — otherwise the next person builds a scrim into the card again. Docs
  • See siblings: Modal - Transaction Receipt is the receipt half that was split out of this component, and Modal - Transaction Receipt Entry is the row inside it. Overlay supplies the scrim. Bottom Sheet is the non-blocking alternative. Family
ActionOrientation
Vertical
Icon
Put the title here
Add description here.
Add description here.
Label
Label
Properties
hasDescription
hasIcon
Properties
ActionOrientation Vertical
hasDescription true
hasIcon true
⤷ IconSlot (slot) 2 items · 92 × 92, filled by a Placeholder circle in the published variants
⤷ ActionSlot (slot) 2 items · vertical flow, 320 × 156, 24px padding, 8px gap — the two buttons stack
Content box 320 × 214 · vertical, 24px sides, 24px top and none at the bottom, gap 16, top centre
Action order Filled on top, outlined beneath
Versions 2
Colors
Surface #FFFFFF
#title #0A2757
#description #6780A9
IconSlot placeholder #D7E0EF
Typography
#title Primary/Headlines/Section
#description Secondary/Default/Base
ActionSlot #label Primary/Label/Large
Layout
Height 370 — Hug
Width 320 — Fill
Radius 6
Padding H 0
Padding V 0
Gap 0
Alignment Center
Colors by Orientation

Eight roles, identical in both orientations — the action area changes shape, not colour. Four belong to this component; the other four are the appearance/* set, which is the Button’s own variable mode arriving through ⤷ ActionSlot rather than anything the modal sets. The 92 × 92 circle in the icon slot is a placeholder: real usage swaps it, and the illustration brings its own colour.

Role Token Value
Surface bg/color-bg-main #FFFFFF
#title text/color-text #0A2757
#description text/color-text-weaker #6780A9
IconSlot placeholder border/color-border #D7E0EF
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
Horizontal
Icon
Put the title here
Add description here.
Add description here.
Label
Label
Properties
hasDescription
hasIcon
Properties
ActionOrientation Horizontal
hasDescription true
hasIcon true
⤷ IconSlot (slot) 2 items · 92 × 92, filled by a Placeholder circle in the published variants
⤷ ActionSlot (slot) 2 items · horizontal flow, 320 × 98, 24px padding, 8px gap — the two buttons sit side by side
Content box 320 × 214 · vertical, 24px sides, 24px top and none at the bottom, gap 16, top centre
Action order Outlined left, filled right
Versions 2
Colors
Surface #FFFFFF
#title #0A2757
#description #6780A9
IconSlot placeholder #D7E0EF
Typography
#title Primary/Headlines/Section
#description Secondary/Default/Base
ActionSlot #label Primary/Label/Large
Layout
Height 312 — Hug
Width 320 — Fill
Radius 6
Padding H 0
Padding V 0
Gap 0
Alignment Center

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: this component, <a href="/components/modal-transaction-receipt">Modal - Transaction Receipt</a> and <a href="/components/modal-transaction-receipt-entry">its 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. title and description have no rows because #title and #description are text layers rather than component properties — a designer sets them by overriding the layer. Each boolean keeps its own parameter beside the slot it gates rather than the slot being inferred from a nil, so every Figma property traces 1:1 for Code Connect. 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 — Vertical, Horizontal actionOrientation: EBActionOrientation actionOrientation: EBActionOrientation
hasDescription — true, false hasDescription: Bool = true hasDescription: Boolean = true
hasIcon — true, false hasIcon: Bool = true hasIcon: Boolean = true
⤷ IconSlot (slot) @ViewBuilder icon: () -> Icon — shown when hasIcon icon: @Composable (() -> Unit)? — shown when hasIcon
⤷ ActionSlot (slot) @ViewBuilder actions: () -> Actions actions: @Composable () -> Unit
EBModal(
    title: "Delete this recipient?",
    description: "You can add them again at any time.",
    actionOrientation: .vertical
)
EBModal(
    title = "Delete this recipient?",
    description = "You can add them again at any time.",
    actionOrientation = EBActionOrientation.Vertical
)
EBModal(
    title: "Delete this recipient?",
    description: "You can add them again at any time.",
    actionOrientation: .horizontal
)
EBModal(
    title = "Delete this recipient?",
    description = "You can add them again at any time.",
    actionOrientation = EBActionOrientation.Horizontal
)
RequirementiOSAndroid
Dialog announced on present .accessibilityAddTraits(.isModal), focus moves to #title Dialog with Modifier.semantics { paneTitle = title }
Focus is trapped while open .accessibilityFocused held inside the dialog Dialog traps focus by default
Illustration is decorative .accessibilityHidden(true) unless it carries meaning contentDescription = null unless it carries meaning
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
Dismissal has a non-visual path Escape gesture maps to the secondary action Back press maps to the secondary action

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

Switch hasIcon off when there is no illustration to show.

Don't

Don't leave the slot on and empty — it reserves 108px of nothing.

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.

Do

Use Modal - Transaction Receipt for receipts.

Don't

Don't rebuild a receipt inside this component; that split is what the restructure was for.

ID Criterion Status Notes
C1 Layer Structure & Naming Ready Container, Content, #title and #description all follow the family convention. The spacer rectangles from the first assessment are gone, and the Container wrapper is confirmed as holding the auto layout.
C2 Variant & Property Naming Ready ActionOrientation closed its space this pass. hasIcon follows the boolean prefix convention and stays off the variant name.
C3 Token Coverage Ready Every colour is bound and named. Four belong to this component — bg/color-bg-main, text/color-text, text/color-text-weaker and border/color-border on the 92 × 92 placeholder. The other four are the appearance/* set, the Button’s own variable mode arriving through ⤷ ActionSlot. All three text layers resolve, and Modal is the one component in this family that uses both faces: Primary/Headlines/Section for the title, Secondary/Default/Base for the description.
C4 Native Mappability Ready Maps cleanly to a dialog with two view-builder slots. The whole family shares one action pattern — two Button_New instances in a ⤷ ActionSlot with 24px padding and an 8px gap, flipping between 156 tall stacked and 98 side by side — so the same actionOrientation parameter serves this component and Modal - Transaction Receipt alike. The one gap is that the two strings are text layers rather than properties; see C7.
C5 Interaction State Coverage Ready Button states come from Button_New. hasIcon covers the with and without cases. A loading state belongs to the Button family rather than here.
C6 Asset & Icon Quality Ready The hardcoded placeholder circle is now a real slot with a Placeholder instance, and the raster copy icon left with the receipt layouts.

1 component set × 2 ActionOrientation values = 2 variants. hasDescription and hasIcon are boolean component properties, so they switch the description and the illustration without multiplying the set, and both slots take their content by swap rather than by variant.

ActionOrientationSizeAction areaButtonsNode
Vertical320 × 370320 × 1562 × 272 × 50, filled on top5879:41275
Horizontal320 × 312320 × 982 × 132 × 50, filled on the right5879:41276
Style and Code tabs rebuilt to the content guides — node 5879:41278
One card became two. ActionOrientation is the driving property, so Vertical and Horizontal each get a card, in this panel’s order — which runs the opposite way to Modal - Transaction Receipt’s, so the two pages list their cards differently on purpose.
Docs
hasDescription was undocumented. hasIcon was already on the page; the second boolean was not, though the Figma panel carries both defaulting to True. Nothing changed in Figma — get_node_info cannot read property definitions.
C2 resolved
Layout is read from two panels rather than derived. The Container gives the seven canonical keys — 370 or 312 tall and hugging, 320 and filling, radius 6, no padding, no gap, centred. The Content box has its own: 320 × 214, vertical, 24 at the sides and top with none at the bottom, gap 16, top centre. That sits in Properties, since it describes a child rather than the component.
Docs
Modal uses both faces, and now says so. #title is Primary/Headlines/Section and #description is Secondary/Default/Base — Proxima Soft over BarkAda. The action label is Primary/Label/Large. The page had carried font specs and a "names pending Dev Mode read" IOU.
C3 resolved
All eight colours are named, and half of them are not this component’s. Four belong here: bg/color-bg-main, text/color-text, text/color-text-weaker, and border/color-border on the 92 × 92 placeholder — read by exporting the icon slot as a vector, which returns the circle with its fill. The other four are the appearance/* set, the Button’s own variable mode arriving through ⤷ ActionSlot.
C3 resolved
The preview drew its title in the wrong face. .eb-preview-mdl declared font-family: inherit, which resolves to the site’s --font-body — BarkAda. That made #description look right by accident and #title silently wrong. The root now names Proxima Soft and the description names BarkAda, so each layer states its own face. Modal is the component that proves this cannot be a find-and-replace: a blanket sweep to Proxima Soft would have broken its description.
Docs
Both previews are server-rendered. Each card’s static markup 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.*, completing the family on one artifact.
Docs
Two properties mapped to the same parameter. hasIcon and ⤷ IconSlot both read icon: Image?, which collapses the switch and the content it reveals into one thing and leaves the boolean with nothing of its own to trace to. The boolean now gates a @ViewBuilder icon, matching how the receipt handles hasReferenceNo and its strip.
Docs
Property Mapping missed a property and mapped two things that are not properties. hasDescription had no row, and #title and #description are text layers. Six rows became five, one per panel property, with both strings named in the description instead.
Docs
Two strings have nothing to bind to. Because #title and #description 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
The two tabs declared different APIs. The mapping had .ebActionOrientation(...) — a SwiftUI modifier — against its own Compose column’s parameter. Settled on the parameter, the third time in this family and for the same reason: it changes the layout of the action area, not the component’s appearance.
Docs
Usage Snippets had three subheadings for two values. The third, "No illustration (hasIcon=false)", was keyed to a boolean rather than to the driving property, and the first claimed a default the panel does not state. Two now, one per ActionOrientation value.
Docs
The variants table named a button that does not exist. It read "primary on top" and "primary on right"; primary is not a term this component’s API uses. Checked against the nodes — the outlined button sits left and the filled right — and reworded to filled, which is what the button actually is.
Docs
The family shares one action pattern, and C4 now records it. Two Button_New instances in a ⤷ ActionSlot with 24px padding and an 8px gap, flipping between 156 tall stacked and 98 side by side — identical in this component and in the receipt, which is what lets one actionOrientation parameter serve both. C3’s note also named no tokens before; it now carries all four.
Docs
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
Rebuilt on the 2026 Working File · node 5879:41278
Restructured as recommended — the receipt layouts were extracted into Modal - Transaction Receipt and Modal - Transaction Receipt Entry. What remains here is the general-purpose dialog, which is what the first assessment asked for.
C4 resolved
Duplicate scope with Overlay settled — Modal owns the card, Overlay owns the scrim. Neither builds the other.
C7 resolved
_space_* spacer rectangles removed and the hardcoded placeholder circle replaced with ⤷ IconSlot.
C1 resolved
Mixed-casing enums gone — transaction_v1, transaction_v2 and 1 - vertical collapsed into ActionOrientation = Vertical | Horizontal, renamed this pass to close the space it carried.
C2 resolved
hasIcon added, defaulting to true, with a Placeholder instance in the slot — the illustration can now be switched off instead of reserving 108px of empty space.
C5 resolved
Raster copy icon resolved — the shape_half and shape_full PNGs left with the receipt layouts, where the copy control is now a vector instance.
C6 resolved
BarkAda description confirmed — the first assessment left this open for design. BarkAda is the secondary face for longer copy at smaller sizes; Proxima Soft carries titles, labels and shorter or larger text.
C3 resolved
Buttons unified on Button_New across the family, so the label is addressable as #label in both modals.
C4 resolved
Node moved from 18507:71705 (Sticker Sheets v2) to 5879:41278 (2026 Working File).
Rebuilt
Initial Assessment · node 18507:71705
DS Health — 7 variants across 2 axes. Reusable and Composable flagged Warn due to transaction layouts being compressed into a general-purpose Modal. Documented
Baseline
Duplicate scope with Overlay — Modal and Overlay (47:329691) are maintained independently but overlap in intent. Family consolidation required. Open
C7
C1 — Layer structure — Opacity-0 _space_* spacer rectangles + hardcoded icon-placeholder circle. Open
C1
C2 — Enum naming — Mixed casing (transaction_v1 vs 1 - vertical). Sparse CTA matrix. Open
C2
C4 — Native mappability — Transaction variants are a different component wearing the Modal hat. Recommend extraction into EBTransactionReceipt. Open
C4
C5 — State coverage — No loading / destructive / copy-success states. Open
C5
C6 — Raster copy icon — shape_half / shape_full PNGs should be a vector icon instance. Open
C6
C7 — Code Connect — Blocked on restructure. Token coverage is the only Ready-status criterion. Open
C7
Typography note — Description copy uses BarkAda (secondary font). Confirm with design — otherwise covered by the standing custom-font action item. Info
Info