A 336×704 single-instance screen rendering merchant header, voucher content, and optional terms-and-conditions sections.
Product Layer — ship as a screen recipe, not a component
A 336×704 single-instance symbol with no variants is not a DS primitive — it is a screen. DS primitives are reusable across many contexts with meaningful variant axes; Voucher Details exists exactly once and only toggles which optional child subtree renders. The DS should ship the primitives it composes from (
EBLogo, EBBadge, EBAccordion, EBListItem) and publish this screen as a recipe in product documentation. The shared "amount + slashed original" row and the dashed strip divider are the only pieces worth extracting — and those belong in a product-layer VoucherAmountRow + TicketDivider, still outside the core DS.Live Preview
Single 336×704 symbol, no variants. Four optional-content booleans:
accordion, badge, slashedAmount, tCWithTextLink — all shown on here with their defaults.DS Health
Reusable
Fail
Not reusable — the component is the voucher-details screen. It has no abstraction; every text string ("Brand", "Voucher Title", "PHP 200.00", "Limited", "All branches", the 4 T&C rows) is baked in. Any consumer must detach to change anything.
Self-contained
Fail
Every meaningful piece — Logo, Badge, Accordion, List Item — is owned by another DS component. What's unique here (the amount row and strip divider) is custom layers, not tokens or logic. The component carries no DS-level behavior of its own.
Consistent
Fail
Schema is 4 booleans that flip child visibility — not states, not variants, not semantic props. Worse, two of them (
tCWithTextLink, accordion) render overlapping content (plain-text T&C vs accordion T&C) in parallel. No real voucher turns both on.Composable
Warn
The composition of primitives is clean — Logo 40px, Badge, Accordion, and List Item are all instance-swapped correctly and inherit their own tokens. That's exactly why this should be product code, not a DS atom.
Behavior
| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Container | N/A | N/A | Root frame | Screen-level scroll, not a component |
| Merchant logo | N/A | N/A | Instance of Logo 40px | Canonical Logo primitive |
| "Limited" pill | N/A | N/A | Instance of Badge | String is hardcoded today |
| Amount row | N/A | N/A | Local layer | Current amount + slashed original — product-layer concern |
| Strip divider | N/A | N/A | Raster image fill | Should be a stroke, not a raster asset |
| T&C accordion | N/A | N/A | Instance | Canonical Accordion with ForEach of EBListItem |
| "See full promo mechanics" | N/A | N/A | Inline colored span | Needs a real link primitive or product-owned LinkText |
Open Issues
- Screen masquerading as a DS component. A 336×704 single-instance symbol with four optional-content booleans is a screen, not a primitive. DS primitives are reusable across many contexts with meaningful variant axes; this exists exactly once and only toggles which optional child renders. C1 · Layer Structure & Naming
- Booleans are child-visibility switches, not states.
accordion,badge,slashedAmount,tCWithTextLinkeach mean "render this optional subtree." They are not semantic props (e.g.hasLimitedOffer,hasOriginalPrice) and not behavioral states. 2^4 = 16 possible combinations of which only ~4 are legitimate in real product screens. C2 · Variant & Property Naming - Overlapping T&C display paths.
tCWithTextLinkrenders a plain-text Terms & Conditions block with a "See full promo mechanics" link;accordionrenders a full Terms & Conditions Accordion with 4 list rows. Both describe the same information and can be toggled on simultaneously. Real screens pick one. C2 · Variant & Property Naming - Native handoff is a View/Screen, not a Component. SwiftUI would model this as
VoucherDetailsView— a scrollable parent that composesEBLogo,EBBadge, a product-owned amount row,EBAccordion, andEBListItem. There is no value in mapping the screen as a single-symbol Code Connect entry. C4 · Native Mappability - Missing interaction states. Voucher details is interactive on mobile — accordion expands/collapses, the "See full promo mechanics" link navigates, the voucher itself may have a primary "Use Voucher" CTA on the real screen. The symbol ships only one static frame and happens to use
expanded=yesfor the embedded accordion. C5 · Interaction State Coverage - Strip divider is a raster image fill. The dashed horizontal line between voucher content and description is rendered as an imported image (
imgStrip), not astroke-dasharrayor a vector pattern. It will not scale with density, re-color, or adapt to dark mode. C6 · Asset & Icon Quality - Not linkable via Code Connect. A 4-boolean screen symbol cannot map 1:1 to a meaningful native API. Linkability belongs at the primitive level (Logo, Badge, Accordion, ListItem) — each of which already has its own Code Connect track. C7 · Code Connect Linkability
Design Recommendations
- Retire from the sticker sheet; publish as a product-screen recipe. Move Voucher Details out of the DS file and into a product documentation page titled "Voucher Details Screen". The recipe shows how to compose
EBLogo,EBBadge, the amount row, the strip divider, andEBAccordionwith aForEachofEBListItem. The DS owns primitives; product owns screens. Docs - Extract the shared amount row into a product-layer component. Current amount + slashed original amount is a genuinely reusable product pattern (appears in every voucher variant and on other commerce screens). Lift it into a
VoucherAmountRow(current: "PHP 200.00", original: "PHP 180.00")living in the product library — not the core DS. Composition - Replace the raster strip divider with a stroke pattern. The dashed horizontal rule between voucher content and description is rendered as a raster image. Replace it with a stroke-based divider (
stroke-dasharrayin SVG,Canvason native) so it scales, retints, and adapts to dark mode without a new asset. Asset - Collapse the dual Terms & Conditions paths into one. The symbol ships both a plain-text T&C section and a full accordion T&C section as independent booleans. Pick one pattern for the product — recommendation: keep the accordion (collapsible saves vertical space on small screens) and drop
tCWithTextLinkentirely, moving the "See full promo mechanics" link into the accordion body or next to the CTA. Composition - Promote user-facing strings to properties (if this symbol survives as a product component). If the design team keeps Voucher Details as a product-layer component, hoist "Brand", "All branches", "Voucher Title", "PHP 200.00", "PHP 180.00", validity range, description, and the badge label into named text props so product teams do not detach to change copy. Property
- Native mapping lives at the primitive level. Document that Code Connect mappings for Voucher Details are not added at the screen level. Each composed primitive —
EBLogo,EBBadge,EBAccordion,EBListItem— carries its own mapping. The screen itself ships as a SwiftUIView/ Compose screen-level composable inside product code. Docs
Property Mapping
No 1:1 Code Connect mapping — the screen is not a primitive. Each composed primitive maps via its own component. The current Figma booleans correspond to product-level state on the Voucher model, not to component props.
| Figma Property | SwiftUI | Compose |
|---|---|---|
badge | voucher.limitedLabel: String? | Conditional EBBadge render |
slashedAmount | voucher.originalAmount: Money? | Conditional strikethrough text |
tCWithTextLink | — | Drop |
accordion | voucher.rules: [Rule] | EBAccordion + ForEach EBListItem |
Criteria Scorecard
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Not Applicable | Screen, not a component — layer-naming discipline applies per primitive, not here. |
| C2 | Variant & Property Naming | Not Applicable | Four boolean visibility switches, no variant axis. Not a schema worth normalising. |
| C3 | Token Coverage | Ready | Every color resolves to main/vouchers/*, main/badge/*, main/accordion/*, or main/list-item/* tokens from composed primitives. |
| C4 | Native Mappability | Not Applicable | Maps to a product-layer View/Screen, not a component. No 1:1 DS-to-native handoff. |
| C5 | Interaction State Coverage | Not Applicable | Interaction lives on primitives (Accordion expand/collapse, link tap). Screen-level state is product concern. |
| C6 | Asset & Icon Quality | Requires Rework | Strip divider is a raster image fill — should be a stroke pattern. |
| C7 | Code Connect Linkability | Not Applicable | Not linkable as a unit. Primitives carry their own mappings. |
Variants Inventory (1 total)
Single symbol, no variant axes. Four boolean toggles (accordion, badge, slashedAmount, tCWithTextLink) drive optional child subtrees but do not generate variants in the component set.
| Node ID | Dimensions | Default booleans |
|---|---|---|
5119:5368 | 336 × 704 | accordion=true, badge=true, slashedAmount=true, tCWithTextLink=true |
1.0.0 — April 2026Major
Initial Assessment · node 5119:5368
Assessed as Product Layer. 336×704 single-symbol screen composition with four optional-content booleans — not a DS primitive. Composes canonical
Design DecisionLogo 40px, Badge, Accordion, and List Item. OpenRecommendation: retire from the DS file. Ship as a product-screen recipe. Extract the shared amount row + strip divider into product-layer
Design DecisionVoucherAmountRow and TicketDivider. Replace the raster strip with a stroke pattern. Drop the tCWithTextLink path to eliminate overlap with the embedded accordion. Open