East Blue v0.1.0
Component Assessment
Overview
Components
Accordion
Action List
Action List - with Counter
Action List - with Description
Ad
Ad Space
Alert
DM LM
Avatar
Avatar Group
Label Label
Badge
Banner
Bottom Sheet
Button
Callout
Generic Card
Generic Transaction Card
Carousel - Discount Card
Carousel - Item
Carousel Card
Chat Field
Checkbox
Chip
Countdown
Counter
Date Picker
Date Picker - Group
Date Picker - Item
Month and Year Picker - Item
Dropdown
Dropdown Item
Dropdown Item Group
Empty State
Amount Text Field
Input Field
Labeled Field
Recipient Field
Search Field
Select Field
Text Area
Upload File
View Only Field
Footer
Header
Header - Centered
Header - Transaction
Header - With Logo
Inline Message
Inline Text
List
List Item
List Item Asset
Menu Grid
Modal
Overlay
Progress Bar
Radio Button
Radio Button with Label
Service Item
Slider
Stepper - Bullet
Stepper - Circular
Stepper - Dash
Subtext Message
Table
Table - Scheduling
Table - Transaction
Tab Item
Tabs
Title Header
Title Bar
Toast
Toast - With Button
Segmented Control - Group
Toggle
Toggle - Segmented Control
Toggle - With Label
Onboarding - Tooltip
Tooltip Blurred and Transparent
Tooltip V2
Visual Popup
35%
Horizontal Voucher
Terms & Conditions Accordion
35%
Vertical Voucher
35%
Voucher Asset
Voucher Card Horizontal
Voucher Details
Icons Styles
ConsolidateNeeds Refinement
Carousel - Discount Card Component link

A discount-themed card variant inside a horizontally scrolling carousel — hero image, percent-off badge, and label.

Consolidate — fold into Carousel Card with <code>variant=discount</code>
Same 140-wide frame, same banner + text block composition, same skeleton axis. The only net-new capabilities are (a) a trailing-corner violator tag and (b) a two-line label slot with a peso-value line below. Both belong as optional slots on a unified Carousel Card rather than a separate component. Today's 3 variants collapse into Carousel Card props: variant: default | with-icon | discount, violator?: string, isLoading: bool.
In Context

Discount Card appears in horizontally-scrolling voucher rails — GDeals, Voucher Pocket, "For You" promotions. Violator tag calls out freshness (New, Ending Soon, Limited).

Live Preview

Add label here
Add label here

PHP 200.00

Content
label line 1
label line 2
value
violator
Properties
type
DS Health
Reusable
Warn
Perforate edge is baked into the banner image — ties the card to voucher contexts. A generic carousel card should accept any banner and only show the perforate when it's actually a voucher.
Self-contained
Warn
Banner image, perforate edge, and placeholder asset are raster PNGs stitched via mask layers. Violator text is hardcoded "New". No token governance over banner/violator colors per state.
Consistent
Fail
Anatomy duplicates Carousel Card (140-wide banner + text + skeleton). Shipping as a separate component splits a single concept across two files with independent token collections.
Composable
Partial
Violator placement is an absolute-positioned overlay — works, but should be a named slot. Hidden _space_12 layer acts as a 12 px spacer via an invisible rectangle rather than a gap token.
Behavior
State iOS Android Figma Property Notes
Default Yes Yes type=default Banner + perforate + label + value. No violator.
With violator Yes Yes type=with violator Same layout with a blue tag anchored to the banner's top-right corner. Text is hardcoded "New".
Skeleton (loading) Yes Yes type=skeleton loader Flat 140 × 152 placeholder fill for the banner; rounded rectangles for title (27 h) and amount (10 h × 101 w).
Pressed N/A N/A Not built Cards tap through to voucher detail — needs a pressed state (subtle dim or scale) for feedback.
Open Issues
  • Duplicates Carousel Card's anatomy. Same 140-wide frame, same banner + text + skeleton composition. Ships as a second component with its own variants and tokens instead of a variant=discount on the shared card. C1 · Layer Structure & Naming
  • type conflates layout and state. default and with violator are layout variants; skeleton loader is a loading state. Packing them on one enum forces mutually-exclusive combinations that shouldn't be — a violator card can also be loading. C2 · Variant & Property Naming
  • Violator label hardcoded. The "New" string is baked into the variant — consumers can't show "Ending Soon", "Limited", or localized copy without detaching. C2 · Variant & Property Naming
  • Perforated voucher edge baked into the banner image. The serrated bottom is part of a raster PNG, not a vector overlay. Ties every "discount card" to voucher visuals even when the use case is a plain promo card. C4 · Native Mappability
  • Banner is a raster PNG with mask layers. replace-this-asset + mask intersect + overflow-clip is fragile on native (iOS AsyncImage / Compose AsyncImage don't need any of that). Also blocks the image from being sized/cropped consistently. C6 · Asset & Icon Quality
  • _space_12 invisible rectangle used as a spacer. A 10.305 px tall #0500ff rectangle with opacity:0 sits between label and value as a spacing hack. Should be a gap / space-* token on the auto-layout. C1 · Layer Structure & Naming
  • No pressed state. Card is tappable (opens voucher detail) but no pressed/active appearance is modeled. C5 · Interaction State Coverage
  • Code Connect mappings not registered. Blocked until the consolidation into Carousel Card is decided. C7 · Code Connect Linkability
Design Recommendations
  • Consolidate into Carousel Card with variant=discount. The entire Carousel family (Carousel Card, Carousel - Discount Card, Carousel - Item, Carousel Item - Center, Carousel Item - Side) shares a 140-wide frame and banner + text + skeleton composition. Merge the three "card" siblings into one Carousel Card with variant: default | with-icon | discount. Preserves every existing layout; eliminates redundant components. Family
  • Split type into independent props. On the consolidated Carousel Card: variant: default | with-icon | discount (layout), violator?: string (optional slot — any text, any variant), isLoading: bool (orthogonal state). 3 × 2 × 2 visual cases from 3 clean props instead of conflated enums. Property
  • Make the violator a named slot. Adopt Figma Slots for the top-right corner overlay. Accepts Badge instance or custom text — maps to @ViewBuilder (SwiftUI) / @Composable slot (Compose) via Code Connect. Slot
  • Replace the perforate edge with a vector overlay. Today it's baked into the banner raster. Extract as a vector SVG rendered on top of the banner when variant=discount. Token-bindable fill + crisp at any scale. Asset
  • Remove the _space_12 placeholder rectangle. Use gap: 12 on the content auto-layout frame instead. Invisible elements used as spacers are a C1 anti-pattern — they clutter the layer tree and break native handoff. Property
  • Rename the value slot to match Figma's token. The peso amount binds to main/carousel/color/value but renders as a standalone text. Expose as amount: String on the proposed Carousel Card so Code Connect can target it directly. Rename
  • Add a pressed state on the consolidated card. Subtle scale (0.98) or overlay tint when tapped. One state that covers all three variants on the merged component. State
  • Banner should accept an Image instance, not a mask layer. Replace the Asset Placeholder + replace-this-asset + mask stack with a single image-fill slot on the banner frame. Cleaner handoff to AsyncImage on both platforms. Slot
Types
Default
DES DEV

Voucher card with perforated banner image, two-line label, and peso-value line.

Add label here
Add label here

PHP 200.00

Properties
Type default
Variant Discount card
Layout image-left + label-right
Colors
Label #0A2757
Discount #2340A9
Surface #FFFFFF
Inverse text #FFFFFF
Active dot #005CE5
Layout
Width × Height 140 × 223.48px
Banner size 140 × 152px
Content padding 6.87 / 10.305px
Corner radius 4px
Drop shadow 0 1 3 0 rgba(232,238,242,.79)
Perforate edge bottom 43.8 · raster PNG
Typography
Label style Primary/Multi-line Label/Small
Label font Proxima Soft Bold · 14 / 16 · +0.25
Value style Primary/Label/Fine
Value font Proxima Soft Bold · 12 / 12 · +0.5
EBDiscountCard(discount: item)
    .ebHeroImage(item.image)
    .ebPercentBadge(item.percent)
Default — Colors

Discount carousel card with label + brand-blue value pair on a white surface.

Role Token Default
Label carousel/color/label #0A2757
Discount carousel/color/value #2340A9
Surface bg/color-bg-main #FFFFFF
Inverse text text/color-text-inverse #FFFFFF
Active dot bg/color-bg-primary #005CE5
With violator
DES DEV

Adds a blue violator tag anchored to the banner's top-right corner. Text is hardcoded "New" today — should be a parameterized slot.

New

Add label here
Add label here

PHP 200.00

Properties
Type with-violator
Has violator Yes
Discount label 50% OFF
Colors
Surface bg #FFFFFF
Violator bg #D81E1E
Violator label #FFFFFF
Title #0A2757
Layout
Width 180px
Height 220px
Image area 180 × 110px
Body padding 12px
Violator height 20px
Violator padding (H) 8px
Corner radius 12px
Typography
Violator style Caption/Bold
Title style Body/Medium · Bold
EBDiscountCard(
    title: "Cinema voucher",
    image: Image("cinema"),
    violator: "50% OFF"
)
With Violator — Colors

Same default surface as Card 1, plus a red violator chip overlaying the image area.

Role Token Default
Surface bg main/discount-card/bg #FFFFFF
Violator bg main/discount-card/violator/bg #D81E1E
Violator label main/discount-card/violator/label #FFFFFF
Title main/discount-card/title #0A2757
Skeleton loader
DES DEV

Loading pattern: flat banner fill, rounded title rectangle, shorter amount rectangle. Centered column (differs from the left-aligned default).

Properties
Type skeleton
State Loading
Has content No
Colors
Skeleton bg #EEF2F9
Surface bg #FFFFFF
Layout
Width 180px
Height 220px
Image placeholder 180 × 110px
Title bar 120 × 12px
Subtitle bar 80 × 8px
Typography
— No text in skeleton state
EBDiscountCard(isLoading: true)
Skeleton — Colors

Loading state — every content slot becomes a rounded grey rectangle.

Role Token Default
Skeleton bg main/skeleton/bg #EEF2F9
Surface bg main/discount-card/bg #FFFFFF
Property Mapping
Figma PropertySwiftUICompose
type=default variant: discount .ebVariant(.discount)
type=with violator violator?: String (slot) violator: String? / violatorSlot: (()->Badge)?
type=skeleton loader isLoading: Bool loading: Bool
(hardcoded 2-line label) label: String (2-line auto-wrap) label: String
(hardcoded "PHP 200.00") amount: String amount: String
(mask + raster asset) banner: Image (slot) banner: Image
(baked perforate PNG) (auto-rendered vector when variant=discount) —
(_space_12 invisible rect) (auto-layout gap token) —
(not modeled) onTap?: () -> Void onTap: (() -> Void)?
Accessibility
RequirementiOSAndroid
Card as a button Wrap in Button; accessibilityLabel combines violator + label + amount. Modifier.clickable { onTap() }.semantics(mergeDescendants = true) on the card.
Combined announcement "New, 2% off GCrypto Bitcoin purchase, PHP 200.00" Same reading order via TalkBack.
Loading state Announce "Loading voucher" once on mount; suppress placeholder reads. contentDescription = "Loading voucher" on the skeleton container.
Min touch target 223 pt card height ≫ 44 pt ✓ 223 dp ≫ 48 dp ✓
Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Needs Refinement _space_12 invisible rectangle acts as a spacer; Asset Placeholder + replace-this-asset leak authoring affordances.
C2 Variant & Property Naming Needs Refinement type conflates layout and loading state; violator text hardcoded.
C3 Token Coverage Ready All colors bound to main/carousel/color/*, bg/*, text/*. Typography via Primary/Multi-line Label/Small + Primary/Label/Fine.
C4 Native Mappability Needs Refinement Perforate edge baked into the banner raster; mask-intersect image composition doesn't translate cleanly to AsyncImage.
C5 Interaction State Coverage Needs Refinement Default + skeleton built. Missing pressed for a tappable card.
C6 Asset & Icon Quality Needs Refinement Banner + perforate ship as raster PNGs.
C7 Code Connect Linkability Not Mapped Blocked until consolidation into Carousel Card is decided.
Variants Inventory (3 total)

type (3) = 3 variants. Single-axis enum conflates layout (default, with violator) with loading state (skeleton loader) — should split into variant + violator? + isLoading on consolidation.

typeNodeDimensionsNotes
default18543:2762140 × 223.48Banner + label + value. Left-aligned.
with violator18543:2770140 × 223.48Adds blue "New" tag top-right of banner.
skeleton loader18543:2782140 × 211Flat banner fill + 2 rounded rect placeholders. Centered column.
1.0.0 — April 2026Major
Initial Assessment · node 18543:2761
Verdict: Consolidate — Fold into Carousel Card with variant=discount + violator? slot + isLoading. Anatomy duplicates Carousel Card. Open
Family
C1 — _space_12 invisible spacer — Replace with gap: 12 on auto-layout. Open
C1
C2 — type conflates layout + state — Split into variant, violator?, isLoading on consolidated Carousel Card. Parameterize violator text. Open
C2
C4 — Perforate edge baked into banner raster — Extract as vector overlay. Open
C4
C5 — Missing pressed state — Tappable card with no press feedback. Open
C5
C6 — Raster banner + perforate — Replace with image slot + vector overlay. Open
C6
C7 — Code Connect — Blocked until consolidation decision. Open
C7
On this page
Are you looking for Components?
esc