RestructureRequires Rework
Modal Component link

A centered overlay surface used for blocking confirmations and dialogs — header, body, and primary/secondary actions.

Restructure — duplicates Overlay scope and compresses two unrelated layouts
A single Modal component is trying to serve both a general-purpose dialog (default / with icon) and a specialised transaction-receipt layout (transaction_v1 / v2). These are not "variants" of the same thing — they have different information architectures, different tokens, and different native mappings. On top of that, Modal overlaps in name and scope with the existing Overlay component (47:329691), which currently ships the scrim only. Consolidate the family: one canonical Modal that owns the surface + scrim, and a separate Transaction Receipt Card for the receipt layout.

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

Properties
type
cta
scrim
Reusable
Warn
General-dialog variants (default / with icon) are reusable across many screens, but transaction_v1 / v2 are specific to the receipt / order-summary use case and shouldn't live inside a generic Modal.
Self-contained
Partial
Modal owns its bg, border, shadow, and label tokens, but relies on an external Overlay to dim the page. The relationship between the two components is not annotated anywhere.
Consistent
Warn
Enum casing is mixed within the same property: transaction_v1 / transaction_v2 use snake_case, 1 - vertical / 2 - horizontal use space-dashed-space. Also duplicates scope with the Overlay component.
Composable
Warn
Transaction variants hard-code inner transaction rows and a reference-number slot — consumers can't swap content. Should be split into a Modal shell + a composable Transaction Receipt child.
State iOS Android Figma Property Notes
Present / dismiss Yes Yes Not annotated Scale-in + fade animation is implied by pattern but not documented on the component.
Tap-outside dismiss Yes Yes Not annotated Overlay owns the tap-region. Contract should be documented (dismissible vs. modal).
CTA resolution Yes Yes Via Button child 1 / 2-horizontal / 2-vertical layouts maped by cta property.
Copy to clipboard (transaction) Yes Yes Icon only, no state Copy icon is raster, with no pressed / success feedback state defined.
Focus trap / a11y Yes Yes Implicit Focus should be trapped inside the modal while open; restore to trigger on close.
  • Duplicate scope with Overlay component. A separate Overlay record (node 47:329691) already owns the scrim primitive; this Modal should compose it, not re-declare the modal surface. Today the two are maintained independently and there's no annotation describing which is canonical. C7 · Code Connect Linkability
  • Two unrelated layouts compressed into one component. type=default and type=with icon are general-purpose dialog shapes, while type=transaction_v1 and type=transaction_v2 are transaction-receipt layouts with their own inner rows and reference-number slot. These are different components masquerading as variants. C4 · Native Mappability
  • Mixed enum casing within a single property. type uses default, with icon (space), transaction_v1, transaction_v2 (snake_case). cta uses 1, 1 - vertical, 2 - horizontal, 2 - vertical (spaces around dashes). Neither is consistent with the rest of the DS. C2 · Variant & Property Naming
  • Opacity-0 spacer frames instead of gap. Inner layers named _space_16, _space_12 with opacity:0 are used to create vertical rhythm. These are non-semantic and don't translate to native auto-layout. Use gap on the parent instead. C1 · Layer Structure & Naming
  • Raster copy-to-clipboard icon. The transaction variants use PNG assets shape_half / shape_full for the copy icon. Should be a vector icon instance bound to main/modal-popup/color/icon-copy. C6 · Asset & Icon Quality
  • Icon-placeholder slot is a grey circle, not an instance swap. The with icon variants render a raw #C2C6CF circle (icon-placeholder). Consumers can't swap in a real icon without detaching. Should be a Slot backed by the Icon component. C1 · Layer Structure & Naming
  • No interaction states on the modal surface. The component ships only a default state — no pressed / dragging state for the CTA group, no loading state for async actions, and no entrance/exit transition annotation. C5 · Interaction State Coverage
  • Missing CTA combos. Transaction variants only ship with cta=1; "with icon" only ships with vertical CTAs. The cta axis is not complete across all type values, so designers resort to detaching when they need another arrangement. C2 · Variant & Property Naming
  • No Code Connect mapping. Blocked on restructure — once the transaction layout is extracted and the enum values are cleaned up, mapping is trivial. C7 · Code Connect Linkability
  • Consolidate with Overlay — one canonical Modal family. The current split (Overlay = scrim only, Modal = surface + scrim baked in) duplicates intent. Rename and re-partition into: Overlay (scrim primitive, already assessed) + Modal (surface composition that references Overlay) + TransactionReceipt (the transaction_v1/v2 layout pulled out as its own card). Document which component owns which concern. Family
  • Extract transaction_v1 / transaction_v2 as a separate component. Move the receipt layout into a new Transaction Receipt component (likely a composition of List + Reference Number + Copy action), and drop the transaction_* values from Modal's type enum. Modal keeps only general-dialog variants. Composition
  • Normalise enum casing. Pick one convention for all multi-word values. Recommendation: single lowercase words separated by dashes — default, with-icon, cta-single, cta-single-vertical, cta-double-horizontal, cta-double-vertical. Align with Button and other DS components. Rename
  • Replace opacity-0 spacer frames with auto-layout gap. Remove the _space_16 / _space_12 invisible rectangles and set gap on the parent auto-layout frames (using space/space-16 and space/space-12 tokens). Native translators can then emit proper spacing parameters. Property
  • Convert icon-placeholder into a Figma Slot. Add a named icon slot to the with icon variants backed by the DS Icon component, so designers can instance-swap without detaching. Default to a neutral status icon. Slot
  • Replace raster copy icon with a vector instance. Swap shape_half / shape_full PNGs for the DS vector Copy icon and bind colour to main/modal-popup/color/icon-copy. While there, add a pressed / copied success state. Asset
  • Complete the CTA matrix. Ship every type × cta combination (or constrain the schema so unsupported combos aren't implied). Currently default is missing 1-vertical, with icon is missing horizontal pairs, and transactions only ship with cta=1. Either fill the gaps or reshape the enum. Property
  • Add loading and destructive states. Modals routinely host async confirmations — add a state=loading variant (CTA replaced with spinner) and surface destructive-action styling via a boolean or via the child Button's existing isError prop. State
  • Annotate the dismiss contract. Document on the component: entrance / exit animation, focus trap, restore-focus-on-close, ESC-to-dismiss, tap-outside-dismiss. Developers currently have to infer these from adjacent patterns. Docs
  • Title + description copy should come from the DS text styles. Title is bound to Primary/Headlines/Section and description to Secondary/Default/Base (BarkAda). Confirm the secondary-font description is intentional — flag as the standing custom-font action item if not. Docs
Styles
Default
DES DEV

The general-purpose dialog. Title + description + single CTA on a white card. Use for confirmations, errors, and neutral informational prompts.

Properties
Type
CTA
Properties
Name Modal
type default
cta 1
Title slot Text · Primary/Headlines/Section
Description slot Text · Secondary/Default/Base
Colors
Surface #FFFFFF
Subtle surface #F6F9FD
Border #E5EBF4
Title #0A2757
Description #6780A9
Accent icon #005CE5
Primary CTA bg #005CE5
Primary CTA label #FFFFFF
Secondary CTA #005CE5
Layout
Width 320
Height 212
Padding 24 / 32 top · 24 sides
Corner radius 6
Border none (shadow only)
CTA group padding py 24
Typography
Title style Primary/Headlines/Section
Title font Proxima Soft · Bold
Title size 22 / 26
Description style Secondary/Default/Base
Description font BarkAda · Medium
Description size 14 / 20
Alignment center
Default — Colors

White surface card with title, description, and primary CTA over a dimmed scrim.

Role Token Default
Surface bg main/modal/surface #FFFFFF
Title label text/primary/headline #0A2757
Description text/primary/body/secondary #6780A9
Scrim (overlay) main/overlay/scrim #020E22 @ 56%
With Icon
DES DEV

Dialog that leads with a 92×92 icon to set tone — success, warning, or info. CTAs stack vertically (1 or 2).

Properties
Type
CTA
Properties
type with icon
cta 1 - vertical
Icon slot 92 × 92 placeholder circle (should be Slot)
Title slot Text · Primary/Headlines/Section
Description slot Text · Secondary/Default/Base
Colors
Surface #FFFFFF
Subtle surface #F6F9FD
Border #E5EBF4
Title #0A2757
Description #6780A9
Accent icon #005CE5
Primary CTA bg #005CE5
Primary CTA label #FFFFFF
Secondary CTA #005CE5
Layout
Width 320
Height (cta=1-v) 312
Height (cta=2-v) 370
Icon container 92 × 92
Icon radius ~72.5 (circle)
Gap icon → title 16
Gap title → desc 16
CTA gap 8 (vertical)
Typography
Title style Primary/Headlines/Section
Title font Proxima Soft · Bold · 22 / 26
Description style Secondary/Default/Base
Description font BarkAda · Medium · 14 / 20
Alignment center
With Icon — Colors

White surface with a leading status icon. Icon color inherits from the placed Icon instance (no own token).

Role Token Default
Surface bg main/modal/surface #FFFFFF
Title label text/primary/headline #0A2757
Description text/primary/body/secondary #6780A9
Scrim (overlay) main/overlay/scrim #020E22 @ 56%
Transaction (v1 · v2)
DES DEV

Receipt-style dialog used for order, transfer, and subscription summaries. <code>v1</code> stacks label + value per row; <code>v2</code> is horizontal. Both include a reference-number row with copy-to-clipboard. <strong>Recommended for extraction into its own <code>TransactionReceipt</code> component.</strong>

Properties
Type
CTA
Properties
type transaction_v1
cta 1
Detail rows 3 per variant, stacked (v1) or inline (v2)
Reference row Label + value + copy icon
Copy icon shape_half
Colors
Surface #FFFFFF
Subtle surface #F6F9FD
Border #E5EBF4
Title #0A2757
Description #6780A9
Accent icon #005CE5
Primary CTA bg #005CE5
Primary CTA label #FFFFFF
Secondary CTA #005CE5
Layout
Width 320
Height (v1) 398
Height (v2) 404
Content padding 24 all sides
Reference row padding 16 top · 8 bottom · 24 sides
CTA padding 8 top · 24 bottom · 24 sides
Row gap (v1) 12
Row gap (v2) 12
Copy icon 24 × 24
Typography
Title style Primary/Headlines/Section
Title font Proxima Soft · Bold · 22 / 26
Section text Primary/Multi-line Label/Light/Base · 16 / 20
Row label / value Primary/Label/Light/Small · 14 / 14
Multi-line row (v1) Primary/Multi-line Label/Light/Small · 14 / 16
Alignment left (v1/v2 body), center (title)
Transaction — Colors

Two-zone surface: a white content card sits on top of a light-blue receipt-offset shelf that holds the reference number row.

Role Token Default
Outer surface bg main/modal/transaction/shelf #F6F9FD
Inner content bg main/modal/transaction/content #FFFFFF
Inner content border main/modal/transaction/border #E5EBF4
Title label text/primary/headline #0A2757
Description text/primary/body/secondary #6780A9
Scrim (overlay) main/overlay/scrim #020E22 @ 56%
Figma PropertySwiftUICompose
type = default EBModal(title:, description:) EBModal(title, description)
type = with icon EBModal(icon:, title:, description:) EBModal(icon = { … }, title, description)
type = transaction_v1 / v2 Extract → EBTransactionReceipt(layout: .stacked / .inline) Extract → EBTransactionReceipt(layout = Stacked / Inline)
cta = 1 { EBButton(…) } (single trailing closure) content: { EBButton(…) }
cta = 1 - vertical Implicit — single button is always full-width Implicit — single button is always full-width
cta = 2 - horizontal { EBButton(…); EBOutlinedButton(…) } + .ctaLayout(.horizontal) ctaLayout = CtaLayout.Horizontal
cta = 2 - vertical .ctaLayout(.vertical) ctaLayout = CtaLayout.Vertical
(proposed) Dismissible .interactiveDismissDisabled(!dismissible) DialogProperties(dismissOnClickOutside = dismissible)
(proposed) Loading state .ebLoading(isLoading) isLoading = true
RequirementiOSAndroid
Modal trait Apply .accessibilityAddTraits(.isModal) — VoiceOver trap focus inside. Use Dialog / AlertDialog — TalkBack treats content as modal by default.
Focus management Focus moves to modal on present; restores to trigger on dismiss. Focus enters dialog content on show; restored to trigger element on dismiss.
Title announcement Bind the title Text as the accessibilityHeading so it's read first. Use Modifier.semantics { heading() } on the title; set paneTitle on the surface.
Dismiss gesture ESC / tap-outside / swipe-down should all route through one dismiss handler. Back gesture + tap-outside configured via DialogProperties(dismissOnBackPress, dismissOnClickOutside).
Destructive action Use role: .destructive on the CTA so VoiceOver announces destructive intent. Use destructive colour palette; set contentDescription on CTA explicitly.
Copy to clipboard Announce "Copied" via UIAccessibility.post(.announcement, …). Announce via view.announceForAccessibility("Copied").
Tap target (copy icon) Wrap 24×24 icon in a ≥44×44 tappable area. Wrap 24×24 icon in a ≥48×48 dp tappable area.
ID Criterion Status Notes
C1 Layer Structure & Naming Needs Refinement Opacity-0 _space_* spacer rectangles used instead of gap. Icon-placeholder is a raw circle node, not a named icon slot.
C2 Variant & Property Naming Needs Refinement Mixed casing: transaction_v1 (snake) vs 1 - vertical (space-dash-space). CTA matrix is sparse across type values.
C3 Token Coverage Ready bg, label, label-primary, border, bg-subtle, icon-copy all bound to main/modal-popup/color/*. Shadow uses Shadow/Depth 0.
C4 Native Mappability Requires Rework General-dialog variants map to .sheet / Dialog; transaction variants do not — they need a dedicated Receipt component. Not a single native primitive.
C5 Interaction State Coverage Needs Refinement Only default state shipped. No loading, no destructive variant, no copy-success feedback, no present/dismiss transition annotation.
C6 Asset & Icon Quality Needs Refinement Copy icon is raster PNG (shape_half, shape_full). Icon-placeholder slot is a hardcoded grey circle instead of a vector icon instance.
C7 Code Connect Linkability Not Mapped Blocked on restructure — duplicate scope with Overlay + embedded transaction layout must be resolved before mapping.

A type × cta matrix would yield 4 × 4 = 16, but only 7 combinations are shipped. The rest are gaps.

TypeCTANodeDimensionsNotes
default118507:71792320 × 212Title + description + single CTA.
default2 - horizontal18507:71799320 × 212Outlined secondary + filled primary, side-by-side.
default2 - vertical18507:71807320 × 270Stacked CTAs, both full-width.
with icon1 - vertical18507:71783320 × 31292×92 icon placeholder + title + desc + single CTA.
with icon2 - vertical18507:71773320 × 37092×92 icon + title + desc + two stacked CTAs.
transaction_v1118507:71706320 × 398Receipt layout — rows stacked (label above value). Reference row with copy icon.
transaction_v2118507:71732320 × 404Receipt layout — rows inline (label left, value right). Reference row with copy icon. Outer surface uses bg-subtle.
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 iconshape_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