A 336-wide voucher tile with a 144px hero image, status row, title, price, and validity period.
Replaced by Voucher Card Horizontal
This component is consolidated into
Voucher Card Horizontal — the canonical sibling. Use Voucher Card with orientation: horizontal and the state axis instead. Once Figma usages migrate, this record will be deleted.Live Preview
Default (all props on)
Minimal — header + price + validity
Migration
Replaced byVoucher Card Horizontal
DS Health
Reusable
Fail
All text content is hardcoded placeholder. Consumers cannot set a title ("Grab Food"), price (PHP 100.00), validity, or badge label without detaching. A "reusable" voucher component that can only render one frozen sample string is not reusable.
Self-contained
Warn
The symbol carries its own layout, spacing, and token-bound colors via
main/vouchers/color/default/*. But it ships two stacked discount Badge instances ("10% off" + "35% off") at the same anchor, assuming one is invisible — nothing in the schema picks between them.Consistent
Fail
Parallel to Vertical Voucher and Voucher Card Horizontal — three components for one concept. Voucher Card Horizontal ships a proper
state axis (Default/Limited/Expiring/Used/Expired); Horizontal Voucher ships none. Property shape diverges across the family (6 booleans here vs 8 in Vertical vs 2 booleans + 4-state enum in Voucher Card Horizontal).Composable
Warn
Nests Badge instances for the badge row (composition works). But the hero image is a raster asset baked into the frame, not a Voucher Asset instance — consumers cannot swap partner imagery without detaching. And since all voucher content is locked placeholder, a parent screen cannot compose real voucher data.
Behavior
| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Hero image | N/A | N/A | asset boolean (raster frozen) | Currently a 336×144 raster with the "GrabFood" wordmark baked in. Should accept any Voucher Asset variant. |
| Discount amount | N/A | N/A | Two stacked Badges ("10% off" + "35% off") | Two Badge instances at the same anchor. Should be a single discount string property on the Voucher Asset, not two stacked layers. |
| Title | N/A | N/A | header boolean (string hardcoded) | "Grab Good" frozen in the symbol. Boolean only toggles visibility. |
| Description | N/A | N/A | description boolean (string hardcoded) | "This is the description of the voucher." frozen. |
| Price / original | N/A | N/A | amount boolean (strings hardcoded) | "PHP 100.00" and "PHP 150.00" frozen; one boolean toggles both. |
| Validity | N/A | N/A | validityPeriod boolean (string hardcoded) | "Validity: Dec 25 2022 - Jan 5 2023" frozen. |
| Status badges | N/A | N/A | badges boolean (row of 4 hardcoded) | Single row of 4 fixed badge labels ("Limited", "Expiring", "Hot", "Discounted"). Row-level visibility only. |
| State | N/A | N/A | Not modelled | Absent entirely. Voucher Card Horizontal has it; Horizontal Voucher does not. |
| Tap target | N/A | N/A | Not modelled | Vouchers are always tappable; current symbol has no pressed/disabled states. |
Resolved Issues
- Voucher content tokens exist. Background (
text/color-text-inverse= white card surface), title (main/vouchers/color/default/label-title), description (label-description), amount (label-amount), strikethrough amount (rendered as#90a8d0, matcheslabel-amount-original), and metadata (label-metadata) are all bound to the voucher component's variable collection. C3 · Token Coverage - Card elevation is tokenised. The card uses
app/shadow/shadow-low(0px/0px/4px,elevation/app/shadow-low/colorrgba(2,14,34,0.06)). No hardcoded shadow values on the parent frame. C3 · Token Coverage
Open Issues
- Three parallel components for one concept. Horizontal Voucher, Vertical Voucher (
5119:1635), and Voucher Card Horizontal (5119:1786) share the same anatomy — voucher image + title + description + price + validity + status badges — but ship as three separate components with divergent property shapes. This is a family-level consolidation, not a single-component fix. C4 · Native Mappability - No state axis. Voucher Card Horizontal ships Default / Limited / Expiring / Used / Expired as a proper state variant that drives background, label colors, and badge treatment. Horizontal Voucher has no state concept — a used or expired horizontal voucher cannot be rendered in greyed-out treatment. C5 · Interaction State Coverage
- All text content is hardcoded placeholder. Title "Grab Food", description "This is the description of the voucher.", price "PHP 100.00", original price "PHP 150.00", and validity "Validity: Dec 25 2022 - Jan 5 2023" are all frozen strings inside the symbol. Booleans toggle visibility but not content. Consumers cannot render a real voucher without detaching. C2 · Variant & Property Naming
- Two stacked discount Badges at the same anchor. The Voucher Asset image frame nests two Badge instances (
I5121:4534;6983:110671"10% off" andI5121:4534;6983:110685"35% off") both absolutely positioned at the top-right. Only one is ever intended to be visible, but no property selects between them — both render on top of each other in the raw symbol. C1 · Layer Structure & Naming - Discount amount is baked into the image frame. The "35% off" / "10% off" label is a hardcoded Badge text inside the Voucher Asset, not a property on the parent Horizontal Voucher. A voucher offering "50% off" or "BUY1 TAKE1" cannot be rendered. Should be a
discount: String?property on the image slot. C2 · Variant & Property Naming - Status badges are row-level, not array-level. The
badgesboolean toggles a single fixed row of 4 hardcoded Badge instances ("Limited" + "Expiring" + "Hot" + "Discounted") on or off. A real voucher with one "Limited" badge and nothing else cannot be rendered. Badges should be a composable array, not a fixed row. C2 · Variant & Property Naming - Hero image is a raster photograph with burned-in partner wordmark.
Paste Image Here/imgPasteImageHereis a 336×144 raster asset with the "GrabFood" wordmark burned into the pixels. Partner branding and image content are frozen. Should be an Image Slot that accepts any Voucher Asset variant (GrabFood, Globe, Smart, Shopee, etc.). C6 · Asset & Icon Quality - No native component maps to this shape. 6 booleans with hardcoded content do not map to any reasonable native API. A proper
EBVoucherCardtakes title, price, validity, badges array, and image as parameters — not six visibility toggles over frozen strings. Code Connect has no 1:1 target. C4 · Native Mappability - Code Connect cannot link a 6-boolean symbol with frozen strings. Even if a mapping existed, swapping the "title" string, the hero image, or the badge labels would require detaching the component. Linkability requires real string/array properties and an image Slot first. C7 · Code Connect Linkability
Design Recommendations
- Merge the three voucher cards into a single Voucher Card component. Horizontal Voucher + Vertical Voucher + Voucher Card Horizontal become one component with
orientation: vertical | horizontal(swaps the layout axis) andstate: default | limited | expiring | used | expired(borrowed from Voucher Card Horizontal — the canonical sibling since it already ships the state axis). Target shape: 2 orientations × 5 states = 10 variants instead of three separate components with divergent schemas. Family - Promote every text string to a property. Add
title: String,description: String,price: String,originalPrice: String?,validity: String?. Retire theheader/amount/description/validityPeriodbooleans — visibility falls out of whether the string is empty. Property - Replace the stacked discount Badges with one
discountstring on the image slot. Drop the duplicated "10% off" and "35% off" Badge instances inside the Voucher Asset frame. Exposediscount: String?on the voucher-image Slot so any discount value ("10% off", "50% off", "BUY1 TAKE1") can be rendered without editing the symbol. Property - Adopt a Figma Slot for the hero image. Replace the raster
Paste Image Herefill with a Slot that accepts a Voucher Asset instance (or any partner illustration component). The discount overlay and the raster wordmark both move into the swapped-in asset, not into the parent Voucher. Slot - Replace the fixed 4-badge row with a composable badges array. Drop the single
badgesboolean. Expose a badges Slot that accepts 0..n Badge instances and wraps when it runs out of width. Consumers choose which badges apply ("Limited" alone, "Hot" + "Discounted", "New" + "Featured", etc.). Slot - Add the state axis missing from Horizontal Voucher. Used and Expired vouchers render in greyed-out treatment with muted labels and a dimmed hero image — a pattern Voucher Card Horizontal already ships. Port the same 5-state treatment to the unified Voucher Card. State
- Remove the dead
10% offBadge layer. The image frame carries bothI5121:4534;6983:110671("10% off") andI5121:4534;6983:110685("35% off") at identical coordinates; after thediscountproperty above is introduced, only one Badge instance should remain, with its text bound to the new property. Composition - Document that Voucher Card is the tap target. Vouchers are always tappable entry points to the voucher detail screen. The unified component should ship a pressed/focused state on the card frame; the handoff is an
onTapclosure, not an internal CTA button. Docs
Styles
Horizontal Voucher
DES DEV
Legacy 336-wide voucher tile. Same hex values + token paths as the canonical <a href="/components/voucher-card-horizontal">Voucher Card Horizontal</a>; migrate to that component when you can.
Properties
Orientation horizontal
Width 336
Colors
Surface bg #FFFFFF
Title #0A2757
Description #445C85
Amount #005CE5
Original price #90A8D0
Validity #6780A9
Layout
Width 336
Image area 336 × 144
Body padding 8 12 12 12
Corner radius 4
Border 1px solid #E6EAF2
Typography
Title Proxima Soft Bold · 16 / 20 · +0.25
Amount Proxima Soft Bold · 14 / 14 · +0.25
Original price Proxima Soft Semibold · 14 (strike)
Validity BarkAda Semibold · 10 / 15
Property Mapping
The current 6 booleans do not map cleanly to native. The table below shows the target shape after the family consolidation — each row captures what the proposed EBVoucherCard replaces from the current Horizontal Voucher.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| — | orientation | orientation: EBVoucherOrientation |
| — | state | state: EBVoucherState |
asset (boolean, raster frozen) | Image Slot | trailing closure |
| "10% off" + "35% off" Badges | discount on Voucher Asset | EBVoucherImageFrame(discount: "35% off") |
header (boolean, string frozen) | title (string) | title: String |
description (boolean, string frozen) | description (string) | description: String? |
amount (boolean, PHP 100 / PHP 150 frozen) | price + originalPrice | price: String, originalPrice: String? |
validityPeriod (boolean, string frozen) | validity (string) | validity: String? |
badges (boolean, row of 4 hardcoded) | badges Slot | badges: [EBBadge] |
| — | — | onTap: () -> Void |
Criteria Scorecard
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Requires Rework | Two discount Badge instances ("10% off" + "35% off") stacked at the same anchor inside the Voucher Asset frame. Text layers are unnamed. Paste Image Here is a generic placeholder layer name. |
| C2 | Variant & Property Naming | Requires Rework | 6 booleans where most should be strings (title, price, validity) or a Slot (badges, image). badges boolean toggles a fixed row of 4 hardcoded badges. All content is frozen placeholder. |
| C3 | Token Coverage | Ready | Title, description, amount, strikethrough amount, and metadata are all bound to main/vouchers/color/default/*. Card elevation uses app/shadow/shadow-low. Typography uses named text styles (Primary/Multi-line Label/Base, Primary/Label/Small, Secondary/Default/Caption, Secondary/Default/Fine). |
| C4 | Native Mappability | Requires Rework | Parallel to 2 other voucher components with divergent schemas. Native has one EBVoucherCard, not three. 6 booleans with frozen strings and a raster hero have no native analog. |
| C5 | Interaction State Coverage | Requires Rework | No state axis at all. Voucher Card Horizontal ships Default/Limited/Expiring/Used/Expired; Horizontal Voucher has none. No pressed/focused/disabled on the card frame either. |
| C6 | Asset & Icon Quality | Requires Rework | Hero image is a raster photograph with the "GrabFood" wordmark burned into the pixels. Discount amount is baked into stacked Badge instances, not a property. |
| C7 | Code Connect Linkability | Requires Rework | Cannot map. Frozen strings, row-level badge toggle, stacked discount badges, and raster hero do not have 1:1 native parameters. Linkability requires the family consolidation first. |
Variants Inventory (1 total)
Single symbol, no variant axes declared. All configurability is through 6 boolean property toggles on the lone instance.
| Node ID | Name | Dimensions | Property toggles |
|---|---|---|---|
5121:4533 | Horizontal Voucher | 336 × 265 (with all properties on) | amount, asset, badges, description, header, validityPeriod — all boolean, all default true |
1.0.0 — April 2026Major
Initial Assessment · node 5121:4533
Assessed with Consolidate verdict. Single symbol with no variants, 6 boolean toggles, hardcoded content, raster hero image, and two stacked discount Badges. Parallel to Vertical Voucher (
Design Decision5119:1635) and Voucher Card Horizontal (5119:1786). OpenProposed family-level merge. Collapse the 3 voucher components into one
Design DecisionVoucher Card with orientation + state axes, text slots (title, description, price, originalPrice, validity), a badges array, and a voucher image Slot. Target: 2 × 5 = 10 variants instead of 3 divergent components. Open