RestructureRequires Rework
Voucher Card Horizontal Component link

A 336×111 horizontal voucher card with text content on the left and a perforated image frame on the right; ships in four state variants (limited, expiring, used, expired).

Merge the voucher-card family, port the state axis to the unified component
Voucher Card Horizontal, Vertical Voucher (5119:1635), and Horizontal Voucher (5121:4533) are three parallel records of the same component. This one carries the canonical state coverage the others lack — state: limited | expiring | used | expired. The consolidation target is a single Voucher Card with orientation: vertical | horizontal × state (5 values if a default is added), text Slots for title / price / originalPrice / validity, a logo Slot for partner branding, and a composable badges array replacing the hardcoded state-to-badge mapping. Push the merge from here, not from the other siblings.
Live Preview
Properties
State
Badge
Original price
DS Health
Reusable
Fail
Title "Buy Load Globe Go90", price "PHP 50.00", original price "PHP 90.00", and validity string are all hardcoded inside the symbol. The partner logo is a raster GCash asset baked into the right frame. Any real voucher (a Globe load voucher, a GrabFood 50% off, a Shopee ₱100 off) cannot render without detaching.
Self-contained
Partial
Colors are token-bound (main/vouchers/color/default/* and main/vouchers/color/expired/*). Typography uses named text styles (Primary/Multi-line Label/Base, Primary/Label/Small, Secondary/Bold/Small Caption). The card does carry its own layout and state treatment — but the logo raster, the "GET VOUCHER" CTA text, and the validity date are all frozen.
Consistent
Warn
Correctly models state as a variant axis — the other two voucher-card siblings (Vertical Voucher, Horizontal Voucher) do not. But the three components ship as three separate records with divergent property shapes. Family-level inconsistency.
Composable
Warn
Nests a Badge instance (composition works). But the partner-image frame is a bespoke raster tree, not an instance-swap slot; and the content block has no text slots. A parent screen cannot compose real voucher data into this card.
Behavior
State iOS Android Figma Property Notes
State N/A N/A state enum (4) Drives bg, label colors, partner-image treatment, and corner badge. Add a default fifth state for vouchers without a status callout.
Title N/A N/A Hardcoded "Buy Load Globe Go90" No property; must be set via detach.
Price / original N/A N/A crossedValue boolean (strings hardcoded) "PHP 50.00" and "PHP 90.00" frozen; boolean only toggles visibility of the strikethrough.
Validity N/A N/A Hardcoded "Validity: Dec 25 2022 - Jan 5 2023" No property; must be set via detach.
Status badge N/A N/A badge boolean + state-derived text "Limited" / "Expiring" / "Used" / "Expired" are derived from state. Consumers cannot set their own badge text.
Partner logo N/A N/A Raster GCash asset No slot — logo is baked into the partner-image frame.
CTA N/A N/A "GET VOUCHER" rotated text (non-interactive) Card is the tap target; the rotated text is decorative, not a Button instance.
Tap state N/A N/A Not modelled No pressed/focused/disabled on the card frame.
Resolved Issues
  • State axis is modelled correctly. Unlike Vertical Voucher and Horizontal Voucher, this component ships a proper state variant driving background, label colors, partner-image tint, and badge style. The 4-value enum (limited / expiring / used / expired) is the canonical shape to port to the unified Voucher Card. C5 · Interaction State Coverage
  • Voucher color tokens are bound. Background (main/vouchers/color/default/bg, main/vouchers/color/expired/bg), title (default/label-title #0A2757, expired/label-title #445C85), amount (label-amount-horizontal #2340A9, expired/label-amount #6780A9), original amount (label-amount-original #90A8D0), and metadata (expired/label-metadata #6780A9) all resolve through the voucher component variable collection. C3 · Token Coverage
  • Badge instance is a DS component. The corner ribbon is a real Badge instance — styled via main/badge/information/heavy/background, main/badge/negative/heavy/background, or main/badge/muted/light/background depending on state. Composition works; only the label string is state-derived rather than user-settable. C7 · Code Connect Linkability
  • Shadow uses the app elevation token. app/shadow/shadow-low (0 0 4 0 #020e220f) is applied to the card frame. C3 · Token Coverage
Open Issues
  • Three parallel components for one concept. Voucher Card Horizontal, Vertical Voucher (5119:1635), and Horizontal Voucher (5121:4533) are three separate records of the same component. This is a family-level consolidation — the unified Voucher Card needs orientation: vertical | horizontal × state: default | limited | expiring | used | expired = 10 variants, not three divergent symbols. C4 · Native Mappability
  • All text content is hardcoded. Title "Buy Load Globe Go90", price "PHP 50.00", original price "PHP 90.00", and validity "Validity: Dec 25 2022 - Jan 5 2023" are frozen strings inside every variant. Booleans badge and crossedValue only toggle visibility — they do not accept content. Consumers cannot render a real voucher without detaching. C2 · Variant & Property Naming
  • Badge text is state-derived, not independently settable. The corner ribbon label flips between "Limited" / "Expiring" / "Used" / "Expired" based on the state enum. A consumer who wants to show "New" or "Featured" on a limited voucher cannot — state and badge label are conflated. Split into state (drives visual treatment) + badge (independent Slot/string). C2 · Variant & Property Naming
  • Partner logo is a raster GCash asset with no slot. imgLogoNoText, imgGCashLogosV2RgbIconBwWhiteTransparent, and imgVoucherImageV1 are raster image fills inside the 96×111 partner-image frame. Vouchers for Globe, Smart, GrabFood, or Shopee all render with the GCash logo. No logo Slot exists. C6 · Asset & Icon Quality
  • Duplicated partner-image subtrees per state. voucher (used by limited/expiring) and Voucher Image V1 (used by used/expired) are two complete parallel subtrees inside the same frame — differing only by background fill (bg/color-bg-primary vs bg/color-bg-overlay-weak). Should be a single subtree with state-driven tokens. C1 · Layer Structure & Naming
  • "GET VOUCHER" rotated text is not a Button. The CTA label is a rotated <p> inside the partner-image frame — not a Button instance, no pressed/focused/disabled state, no onTap handler semantics. If tapping the partner half is supposed to redeem the voucher, that needs to be an actual Button or the whole card needs to be the tap target. C5 · Interaction State Coverage
  • Perforated ticket edge is a raster mask. imgPerforate is a raster image used as a mask to produce the perforated dashed edge between the content block and partner frame. Should be a vector path or an SVG mask; at 1× / 2× / 3× the raster will alias. C6 · Asset & Icon Quality
  • No default (neutral) state. Every variant renders a corner badge. A voucher that is simply available (neither limited nor expiring) has no option to render without a badge beyond setting badge=false, which drops the callout but keeps the limited-state visual treatment. Add a default state for active-but-unflagged vouchers. C5 · Interaction State Coverage
  • Two-boolean + 4-enum surface cannot map 1:1 to native. A proper EBVoucherCard(orientation:, state:, title:, price:, originalPrice:, validity:, logo:, badge:, onTap:) shape has no 1:1 correspondence in the current component — title/price/originalPrice/validity/logo are all hardcoded. Code Connect linkability requires the family consolidation and property-ification first. C7 · Code Connect Linkability
Design Recommendations
  • Merge the three voucher cards into a single Voucher Card component. Vertical Voucher + Horizontal Voucher + Voucher Card Horizontal collapse into one component with orientation: vertical | horizontal × state: default | limited | expiring | used | expired = 10 variants. Port this component's state axis to the unified schema; port Vertical Voucher's content-block structure; drop Horizontal Voucher entirely. Family
  • Promote every text string to a property. Add title: String, price: String, originalPrice: String?, validity: String?. Retire the crossedValue boolean — visibility falls out of whether originalPrice is set. Keep the text-style bindings intact. Property
  • Split state (visual treatment) from badge (label). Keep state as the 5-value enum that drives bg / label colors / partner-image treatment. Expose badge: EBBadge? as an independent Slot so consumers can pick any badge style and text ("New", "Featured", "Limited", custom). The current state-to-badge-text mapping becomes the default badge when none is supplied. Property
  • Adopt a Figma Slot for the partner logo. Replace the raster GCash asset with a 64×64 logo Slot inside the partner-image frame. Consumers instance-swap partner brand marks (Globe, Smart, GrabFood, Shopee, etc.) without detaching. Keep the perforated ticket shape and overlay treatment in the frame itself. Slot
  • Collapse the two partner-image subtrees into one. voucher (limited/expiring) and Voucher Image V1 (used/expired) are duplicate layer trees differing only by background token. A single subtree gated by state-driven fills (main/vouchers/color/{state}/partner-bg) removes the duplication. Composition
  • Add a default state. Current states are all "flagged" — active-but-unflagged vouchers have no clean render. Add state: default with the active (non-greyed) treatment and no corner badge by default. State
  • Replace the perforated raster mask with a vector path. The perforated ticket edge should be an SVG path or a vector mask — not a raster image. Same treatment as recommended for Voucher Asset. Asset
  • Make the card the tap target; remove the rotated "GET VOUCHER" text as a faux-button. The entire card is the semantic action ("redeem / open voucher details"). Document the handoff as onTap; keep "GET VOUCHER" only as a decorative label if product still wants it visible, or drop it entirely. Docs
  • Add pressed / focused / disabled to the card frame. Vouchers are always tappable — the unified Voucher Card needs a pressed state variant (e.g. opacity 0.8 or scale 0.98), a focused state for keyboard navigation, and a disabled treatment for unavailable vouchers distinct from expired. State
  • Rename the duplicated Badge layers per state. All four badge layers are named Badge with no variant-qualifying name. After the family merge, there should be a single badge-slot layer; until then, name them badge-limited / badge-expiring / badge-used / badge-expired for clarity. Rename
Styles
Default — active voucher
DES DEV

Active voucher card layout — image left, title + amounts + metadata right. Default state used in voucher catalogs.

Buy Load Globe Go90
PHP 50.00
PHP 90.00
Validity: Dec 25 2022 - Jan 5 2023
GET VOUCHER
Limited
state=limited
Properties
State
Original price
Properties
Status limited
Has original price Yes
Has metadata row Yes
Colors
Surface bg #FFFFFF
Title color #0A2757
Amount color #2340A9
Original amount color #90A8D0
Partner bg #005CE5
Badge bg #2340A9
Shadow color #020E220F (~6%)
Shadow token app/shadow/shadow-low
Layout
Width 336
Min height 144
Image area 144 × 144
Body padding 12
Gap 8
Corner radius 6
Shadow blur 4
Typography
Title style Primary/Multi-line Label/Base
Title font Proxima Soft · Bold 16 · LH 20 · Track 0.25
Amount style Primary/Label/Small
Amount font Proxima Soft · Bold 14 · LH 14
Original amount style Primary/Label/Light/Small (semibold)
Metadata style Secondary/Bold/Small Caption (BarkAda · 10)
Default — Colors

Active voucher with full-color title, brand-blue amount, strike-through original price, and a soft drop shadow.

Role Token Default
Surface bg main/vouchers/color/default/bg #FFFFFF
Title main/vouchers/color/default/label-title #0A2757
Amount main/vouchers/color/label-amount-horizontal #2340A9
Original amount main/vouchers/color/default/label-amount-original #90A8D0
Shadow shadow/color-shadow-soft #020E22 @ 6%
No original price — single amount
DES DEV

Slim variant when the voucher has no compared/strike-through price. Renders a single amount line.

Buy Load Globe Go90
PHP 50.00
Validity: Dec 25 2022 - Jan 5 2023
GET VOUCHER
Limited
state=limited
Properties
State
Original price
Properties
Status limited
Has original price No
Has metadata row Yes
Colors
Surface bg #FFFFFF
Title color #0A2757
Amount color #2340A9
Layout
Width 336
Min height 144
Image area 144 × 144
Body padding 12
Gap 8
Corner radius 6
Typography
Title style Primary/Multi-line Label/Base
Amount style Primary/Label/Small
Expired — Colors

Past-validity voucher with muted title to signal it is no longer redeemable.

Role Token Default
Surface bg main/vouchers/color/expired/bg #FFFFFF
Title main/vouchers/color/expired/label-title #445C85
Property Mapping

Current shape is 4 state variants + 2 booleans with hardcoded content. The table below shows the target shape after the family consolidation — each row captures what the proposed EBVoucherCard replaces.

Figma PropertySwiftUICompose
orientation orientation: EBVoucherOrientation
state (4 values) state (5 values) state: EBVoucherState
hardcoded "Buy Load Globe Go90" title (string) title: String
hardcoded "PHP 50.00" price (string) price: String
crossedValue (boolean, "PHP 90.00" frozen) originalPrice (string) originalPrice: String?
hardcoded "Validity: …" validity (string) validity: String?
raster GCash logo logo Slot trailing closure
badge (boolean, state-derived label) badge Slot (optional) badge: EBBadge?
"GET VOUCHER" rotated text — (remove or decorative) onTap: () -> Void
Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Requires Rework Two parallel partner-image subtrees (voucher vs Voucher Image V1) for active vs greyed states. Four badge layers all named Badge. Content block uses generic container / price names but text layers are unnamed.
C2 Variant & Property Naming Requires Rework State enum drives bg + label colors + badge style + badge text all at once. Title, price, original price, validity are frozen strings. crossedValue boolean should be a nullable originalPrice string.
C3 Token Coverage Ready All colors bound to main/vouchers/color/{default|expired}/* and main/badge/{information|negative|muted}/{heavy|light}/*. Typography uses named text styles. Shadow uses app/shadow/shadow-low.
C4 Native Mappability Requires Rework Parallel to 2 other voucher components with divergent schemas. Native is one EBVoucherCard, not three. Strings/logo need property-ification before any 1:1 mapping.
C5 Interaction State Coverage Requires Rework 4 state variants are good but no default, no pressed/focused/disabled on the card frame, and the "GET VOUCHER" CTA is a non-interactive rotated text label rather than a Button.
C6 Asset & Icon Quality Requires Rework Partner logo is raster (imgLogoNoText, GCash PNG). Perforated ticket edge uses a raster mask (imgPerforate). Should be vector / SVG path.
C7 Code Connect Linkability Requires Rework Cannot map with hardcoded strings and no logo slot. Linkability requires the family consolidation + property-ification first.
Variants Inventory (4 total)

Single axis: state. All 4 variants render at 336 × 111. Booleans badge (default true) and crossedValue (default true) apply uniformly across states.

Node IDVariantDimensionsBadge stylePartner-image treatment
5119:1787state=limited336 × 111information/heavy #2340A9, label "Limited"Full-color bg (bg/color-bg-primary #005CE5) + white GCash logo
5119:1807state=expiring336 × 111negative/heavy #D61B2C, label "Expiring"Full-color bg (bg/color-bg-primary #005CE5) + white GCash logo
5119:1827state=used336 × 111muted/light #C2C5CA, label "Used"Greyed overlay (bg/color-bg-overlay-weak rgba(2,14,34,0.24)), mix-blend-multiply
5119:1847state=expired336 × 111muted/light #C2C5CA, label "Expired"Greyed overlay (bg/color-bg-overlay-weak rgba(2,14,34,0.24)), mix-blend-multiply
1.0.0 — April 2026Major
Initial Assessment · node 5119:1786
Assessed with Restructure verdict. 4-state variant (limited / expiring / used / expired) is the canonical state axis for the voucher family — the only one of three parallel voucher cards that models state. Text content, partner logo, and perforated edge are hardcoded / raster. Open
Design Decision
Proposed family-level merge. Collapse this, Vertical Voucher (5119:1635), and Horizontal Voucher (5121:4533) into a single Voucher Card with orientation + state axes. Port this component's state coverage to the unified schema. Target: 2 × 5 = 10 variants instead of 3 divergent components. Open
Design Decision