East Blue
v0.1.0
Home
How It Works

Each component is evaluated against seven criteria. The report is self-contained and versions alongside the design system.

01 — Assess
Evaluate
Inspect the Figma component against 7 criteria covering structure, naming, tokens, states, and Code Connect readiness.
02 — Document
Write
Create a .html file in assessment-src/components/ with the nav, summary, and full assessment.
03 — Build
Assemble
Run node assessment-src/build.js to compile all component files into this page automatically.
04 — Ship
Publish
Commit and push. The report goes live on GitHub Pages — no framework, no build pipeline.
Assessment Criteria

The overall status reflects the weakest criterion — one unresolved issue can block Code Connect linkability.

IDCriterionWhat We Check
C1Layer Structure & NamingLayers should use semantic names like leading-icon or content — not Figma defaults like Frame 42 or Group 7. The hierarchy should be logical and free of unnecessary nesting.
C2Variant & Property NamingVariants and properties should follow clear, consistent conventions. Booleans expressed as true/false (not yes/no), enum values lowercase and hyphenated.
C3Token CoverageAll color, spacing, typography, and radius values should be bound to design tokens — not hardcoded. Token coverage determines how easily engineers can map decisions to a native token system.
C4Native MappabilityThe component should map cleanly to a standard native primitive (e.g. DisclosureGroup, Button, List) with no web-only patterns that lack a native equivalent.
C5Interaction State CoverageAll expected interactive states should be defined as variants — default, pressed, focused, disabled, and error. Missing states force engineers to invent visual behavior.
C6Asset & Icon QualityIcons should be vector components (not raster or PNG embeds) and colored using tokens so tinting works natively on both platforms.
C7Code Connect LinkabilityThe component should be a proper Figma component set with property names clean enough to map 1:1 to native parameters via Code Connect.
Status Legend
StatusMeaning
ReadyLinkable as-is. Clean structure, maps well to native.
Needs RefinementMinor issues to resolve before linking.
Requires ReworkNeeds redesign before native translation.
Not ApplicableNo native equivalent.
FixResolved via Figma MCP. Residual items may remain.
Accordion
6 variants. All tokens connected, naming clean. DS Ready — only Code Connect CLI pending.
Avatar Group
Fix
4 variants (pair/trio/quad/overflow). Property=layout, overflow badge added, inner avatars point to canonical Avatar. Only C7 pending.
Avatar
21 variants. Library-level C2 noted (token typo in shared Variables collection). C7 pending (universal).
Badge
68 variants across State x Level x Type. Status indicator pill with token-bound colors. DS Ready -- only Code Connect CLI pending.
Button
Keep
180 variants. Style(3) × State(3) × Size(5) × Icon Placement(4). Appearance via variable modes (Default/Destructive/White/Subtle) — documented in Figma component description. C7 (code connect) pending.
Checkbox
Keep
33 variants across isSelected × State × Size. Code Connect registration pending (C7).
Dropdown
Fix
8 variants across variant (Text/Error/Amount/Mobile) × type (Collapsed/Expanded). Generic dropdown with list overlay. C2, C5, C6 open.
Input Field
Fix
8 variants across State × isFilled. Base text input for Form Elements group. C2 (boolean naming) open.
Labeled Field
Fix
8 variants across State × isFilled. Enhanced input with leading icon, label, value, action button, and trailing icon. C1, C2 resolved.
Menu Grid
Fix
20 variants (Row × Column, 2–5 × 1–5). Layout container of Service Item instances. C2 (string enums), C5 (only active state), C7 pending.
Recipient Field
Fix
8 variants across State × isFilled. Two-line recipient entry field (56px) with trailing action icons. C6 icon issue open. C1 naming fixed.
Select Field
Fix
8 variants across State × isFilled. Currency/amount selection field with peso sign, flag, and chevron. C6 open.
TitleHeader
Title Bar
20 variants across 5 boolean properties. App navigation title bar with optional leading/trailing icons, subtext, control, and title block. C2, C6 open.
Visual Popup
Fix
3 variants (Default / 2 CTA / Version 2). Modal with hero image, title, description, and CTA(s). Variant naming mixed, hero is raster, C5/C7 open.
Accordion Component link

A disclosure row that expands to reveal content. Supports optional leading icon and description via boolean visibility properties. Reduced from 24 to 6 variants (Type × State) with color tokens fully connected.

In Context

How the accordion appears in a real product screen — expanding to reveal content.

Accordion component shown in a GCash Cash In screen with Over the Counter expanded showing partner list, Online Banks and Global Partners collapsed
Live Preview

Toggle properties to see the accordion update in real time.

Accordion Label
Properties
Type
State
leadingIcon
description
DS Health
Reusable
Pass
Expanded variants include a content-body slot. Boolean visibility on leadingIcon and description lets designers configure the component without extra variants.
Self-contained
Pass
Header row (56px fixed height) and content-body panel are both included. Engineers can implement it as a standalone unit with no external spec needed.
Consistent
Pass
A single variant property (Type) drives collapsed/expanded. leadingIcon and description are boolean show/hide properties — no duplicate variants needed.
Composable
Pass
Semantic layer names (icon-leading, content, trailing-icon). Chevrons are vector instances. The icon slot accepts instances cleanly.
Behavior
StateiOSAndroidFigma PropertyNotes
DefaultYesYesState=DefaultHeader row with chevron. Tap to expand/collapse.
PressedYesYesState=PressedVisual feedback on touch. Darker surface token.
DisabledYesYesState=DisabledMuted colors. Tap ignored. Chevron dimmed.
Focused (a11y)N/AN/AMobile OS handles focus rings natively.
Resolved Issues
  • Boolean properties converted from yes/no to true/false (C2)
  • Layer names corrected to semantic naming: container, icon-leading, content, trailing-icon (C1)
  • Pressed and disabled interaction states added across all 6 variants (C5)
  • Expanded content panel with content-body SLOT added to all expanded variants (C4)
  • Variant set reduced from 24 to 6 — Type × State matrix (C2)
  • leadingIcon and description converted to boolean visibility properties
  • Fixed 56px header height applied across all 6 variants
  • 10 design tokens connected — all colors, spacing, and typography fully tokenized (C3)
  • Annotation instance frame built with nested auto layout (Type × State grid)
Open Issues
  • Code Connect CLI mappings not registered — no native component files linked (C7)
Design Recommendations
  • Create an AccordionGroup compound component to manage exclusive expand (only one open at a time) — common FAQ and settings pattern New Component
Styles
Collapsed
DESDEV

Header row only — 56px fixed height. Trailing chevron points down. Tap anywhere in the row to expand.

Label
Properties
State
leadingIcon
description
Properties
TypeCollapsed
StateDefault
leadingIcontrue
descriptionfalse
Layout
Width396px (fill)
Header height56px
Padding H16px
Padding V4px
Leading icon32×32px
Trailing icon32×32px
Corner radius0 (rectangular)
Border1px solid #E5EBF4
Typography
Text StylePrimary/Multi-line Label/Base
Label fontProxima Soft Bold
Label size16px
Label tracking0.25px
Label line-height20px
Text StyleSecondary/Bold/Base
Desc fontBarkAda SemiBold
Desc size14px
Desc tracking0
Desc line-height20px
Colors by State

All colors are bound to design tokens from the component variable collection.

RoleTokenDefaultPressedDisabled
Header bgsurface/default#FFFFFF
Pressed bgsurface/pressed#F4F7FB
Disabled bgsurface/disabled#F8F9FB
Borderborder/subtle#E5EBF4#E5EBF4#E5EBF4
Labeltext/primary#0A2757#0A2757
Label (disabled)text/disabled#C2C6CF
Descriptiontext/secondary#90A8D0#90A8D0
Icon placeholdericon/placeholder#C2C6CF#C2C6CF#C2C6CF
Chevronicon-chevron#005CE5#005CE5#C2CFE5
Expanded
DESDEV

Header row (56px) + content-body panel (56px SLOT)=112px total height. Trailing chevron points up. Content-body background uses surface/content token.

Label
Properties
State
leadingIcon
description
Properties
TypeExpanded
StateDefault
leadingIcontrue
descriptionfalse
Layout
Width396px (fill)
Header height56px
Content-body height56px (SLOT)
Total height112px
Padding H16px
Padding V4px
Leading icon32×32px
Trailing icon32×32px
Corner radius0 (rectangular)
Border1px solid #E5EBF4
Divider1px solid #E5EBF4 (header/body)
Typography
Text StylePrimary/Multi-line Label/Base
Label fontProxima Soft Bold
Label size16px
Label tracking0.25px
Label line-height20px
Text StyleSecondary/Bold/Base
Desc fontBarkAda SemiBold
Desc size14px
Desc tracking0
Desc line-height20px
Colors by State

Expanded adds the surface/content token for the content-body panel background.

RoleTokenDefaultPressedDisabled
Header bgsurface/default#FFFFFF
Pressed bgsurface/pressed#F4F7FB
Disabled bgsurface/disabled#F8F9FB
Content bgsurface/content#F4F7FB#F4F7FB#F8F9FB
Borderborder/subtle#E5EBF4#E5EBF4#E5EBF4
Labeltext/primary#0A2757#0A2757
Label (disabled)text/disabled#C2C6CF
Descriptiontext/secondary#90A8D0#90A8D0
Icon placeholdericon/placeholder#C2C6CF#C2C6CF#C2C6CF
Chevronicon-chevron#005CE5#005CE5#C2CFE5
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

// Or in Package.swift:
.package(
    url: "https://github.com/AY-Org/eb-ds-ios",
    from: "1.0.0"
)

Android — Gradle (Kotlin DSL)

// build.gradle.kts (app)
dependencies {
    implementation("com.eastblue.ds:accordion:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.accordion.*  // Compose

Package not yet published. These are the planned distribution paths. API shape is final — native implementation is pending.

Property Mapping

Every row maps a Figma component property to its native equivalent.

Figma PropertySwiftUICompose
Type=CollapsedisExpanded: falseisExpanded=false
Type=ExpandedisExpanded: trueisExpanded=true
State=Disabled.disabled(true)enabled=false
leadingIcon=trueleadingIcon: Image?leadingIcon: @Composable (() -> Unit)?
description=truedescription: String?description: String?
Content-Body (SLOT)content: () -> some Viewcontent: @Composable () -> Unit
SwiftUI
ios/Components/Accordion/EBAccordion.swift
Jetpack Compose
android/components/accordion/EBAccordion.kt
Usage Snippets Planned API
Basic Accordion
// Basic
EBAccordion("Settings", isExpanded: $isExpanded) {
    Text("Content goes here")
}

// With leading icon
EBAccordion("Settings",
    isExpanded: $isExpanded,
    leadingIcon: Image(systemName: "gear")
) {
    Text("Content")
}

// With description
EBAccordion("Settings",
    description: "Manage your preferences",
    isExpanded: $isExpanded
) {
    Text("Content")
}

// Disabled
EBAccordion("Settings", isExpanded: $isExpanded) {
    Text("Content")
}
.disabled(true)
// Basic
EBAccordion(
    title = "Settings",
    isExpanded = isExpanded,
    onExpandedChange = { isExpanded = it }
) {
    Text("Content goes here")
}

// With leading icon
EBAccordion(
    title = "Settings",
    isExpanded = isExpanded,
    onExpandedChange = { isExpanded = it },
    leadingIcon = { Icon(Icons.Filled.Settings, null) }
) {
    Text("Content")
}

// Disabled
EBAccordion(
    title = "Settings",
    isExpanded = isExpanded,
    onExpandedChange = {},
    enabled = false
) {
    Text("Content")
}
Accessibility
RequirementiOSAndroid
Min touch target44 × 44pt (full header row)48 × 48dp (full header row)
Expand/collapse.accessibilityAction(.default) togglesonClick handler on header
State announcement.accessibilityValue("expanded"/"collapsed")expandedState semantics
Disabled.disabled(true) — announced by VoiceOverenabled=false
Content-bodyAutomatically read by screen reader when expanded
Chevron icon.accessibilityHidden(true) — decorativecontentDescription=null — decorative
Usage Guidelines

Do

Use Accordion for progressive disclosure — hiding secondary content until the user needs it.

Don't

Nest Accordions more than one level deep — it creates confusing navigation.

Do

Use description text for context that helps users decide whether to expand.

Don't

Put critical information inside collapsed Accordions — users may miss it.

Do

Use leadingIcon to reinforce the section's topic — gears for settings, bell for notifications.

Don't

Use Accordion for content the user needs to compare side-by-side — use tabs instead.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadySemantic names across all variants: container, icon-leading, content, trailing-icon.
C2Variant & Property NamingReadyBoolean properties use true/false. Variant keys use key=value syntax. leadingIcon and description are boolean visibility props.
C3Token CoverageReady10 tokens bound across all 6 variants. All colors, spacing, and typography fully tokenized.
C4Native MappabilityReadyHeader + content-body SLOT maps cleanly to DisclosureGroup (SwiftUI) and AnimatedVisibility (Compose).
C5Interaction State CoverageReadyDefault, pressed, and disabled states covered across all 6 variants. Focus ring N/A — mobile OS handles natively.
C6Asset & Icon QualityReadyChevrons are vector component instances. Leading icon is a SLOT placeholder accepting any icon instance.
C7Code Connect LinkabilityNeeds RefinementNo Code Connect mappings registered. Property structure is clean and ready for mapping — suggested paths below.
Code Connect
AspectStatusNotes
Component typeReadyProper Figma component set.
Variant namingReadykey=value syntax with true/false booleans.
Property namingReadyClean 1:1 mapping to native params.
Layer namingReadycontainer, icon-leading, content, trailing-icon.
Token coverageReadyAll 10 tokens bound — colors, spacing, and typography.
Asset qualityReadyChevrons are vector component instances. Icon slot is SLOT type.
Code ConnectNot MappedNo mappings registered. Suggested paths below.
Variants Inventory (6 total)

2 Type values × 3 State values. leadingIcon and description are boolean visibility properties, not variant axes.

TypeStateNode ID
CollapsedDefault16870:9289
ExpandedDefault16870:9298
CollapsedPressed16919:864
ExpandedPressed16919:877
CollapsedDisabled16919:956
ExpandedDisabled16919:969
1.4.0 — March 2026 Patch
Changes Applied via Figma MCP · node 16870:9288
Leading icon layer re-renamed — All 6 variants had Placeholder reverted after v1.3.0 restructure. Re-applied icon-leading name across all 6 current variants. Fixed
C1 Restored
Custom fonts validatedProximaSoft-Bold.ttf (700, TTF, GPOS kerning, 959 glyphs) and BarkAda-SemiBold.ttf (600, TTF, GPOS kerning, 1050 glyphs) confirmed native-ready. BarkAda uses PostScript name BarkAda-SemiBold for iOS registration. Validated
Fonts Resolved
1.3.0 — March 2026 Minor
Changes Applied via Figma MCP · node 16870:9288
Variant set reduced from 24 to 6leadingIcon and description converted from variant axes to boolean visibility properties. Component set now has 2 Type values (Collapsed / Expanded) × 3 State values (Default / Pressed / Disabled)=6 variants total. Refined
C2 Improved
Fixed 56px header height — Applied consistent fixed height across all 6 variant headers for reliable layout in native implementations. Refined
C4 Improved
Color tokens connected — 10 design system variables bound across all 6 variants: surface/default, border/subtle, text/primary, text/secondary, icon/placeholder, icon-chevron, surface/pressed, surface/content, surface/disabled, text/disabled. Fully resolves C3. Fixed
C3 Resolved
Code Connect property renamedlabelDescriptiondescription for cleaner 1:1 mapping to native params. Refined
C7 Prep
Annotation instance frame added — Type × State grid built with nested auto layout (VERTICAL outer → HORIZONTAL rows → VERTICAL cells). White card, #E5EBF4 border, 16px radius, Menlo annotation labels. Added
Annotation
1.2.0 — March 2026 Minor
Changes Applied via Figma MCP · node 16870:9288
Added expanded content panel (content-body) — All 12 expanded variants resized from 62px to 142px. A content-body frame (360×80px) added at y=62 inside each container. Background: #F4F7FB (surface/content token). Border: #E5EBF4. Fully resolves C4. Fixed
C4 Resolved
1.1.0 — March 2026 Minor
Changes Applied via Figma MCP · node 16870:9288
Added state=pressed and state=disabled variants — 16 new variants cloned and styled. Component set expanded from 8 to 24 variants. state property added with values default / pressed / disabled. Fully resolves C5. Fixed
C5 Resolved
1.0.1 — March 2026 Patch
Changes Applied via Figma MCP · node 16830:2025
Frame renamed to container — All 8 root container frames renamed from Frame to container. Fixed
C1 Partial
Placeholder renamed to icon-leading — All 4 leading icon instances renamed from Placeholder to icon-leading. Fixed
C1 Partial
Boolean props converted to true/false — All 8 variant names updated. leading icon and label description converted from yes/no to true/false. Fully resolves C2. Fixed
C2 Resolved
Expanded content panel — Resolved in v1.2.0. content-body frame added to all 12 expanded variants. Fixed in 1.2.0
C4 Resolved
Interaction states: pressed / disabled — Resolved in v1.1.0. 16 new variants added across all type/icon/desc combinations. Fixed in 1.1.0
C5 Resolved
Code Connect mappings — No native component files or Code Connect CLI mappings registered yet. Still Open
C7 Open
Avatar Group FixNeeds RefinementComponent link

Stacked/overlapping avatars for participant lists — conversation members, shared documents, collaboration indicators. 4 variants: layout=pair (2), layout=trio (3), layout=quad (4), and layout=overflow (3 + "+N" badge). Fixed 48×48 container with 24×24 inner avatars.

All structural issues resolved
Property renamed to layout with semantic values ✓. Overflow variant added ✓. Inner avatars repointed to canonical Avatar via instance swap ✓. Only C7 (Code Connect) remains — tracked universally across all components.
In Context

How the avatar group appears in a real product screen — conversation list where grouped chats display stacked avatars (DX Team, David's Surprise Party) alongside single-avatar threads.

Avatar Group component shown in the GCash messaging/chat list, where group conversations display 2–3 stacked circular avatars
Live Preview

Toggle count to see the avatar group update in real time.

Properties
layout
DS Health
Reusable
Pass
Fits participant lists and collaboration indicators. 4 variants (pair/trio/quad/overflow) cover 2–4 visible avatars plus "+N" overflow for larger groups. Fixed 48×48 container — suitable for most list/row contexts.
Self-contained
Pass
Group carries its own overlap positioning, border overlap treatment, and fixed dimensions. All colors token-bound.
Consistent
Pass
Property renamed from no. of initalslayout with semantic values (pair/trio/quad/overflow). C2 resolved. Inherits the main/avatar/brand/intials typo from Avatar's shared variable collection (tracked under Avatar, not an Avatar Group blocker).
Composable
Pass
All inner avatars are instances of the canonical Avatar component (17143:4488). Changes to Avatar now propagate to Avatar Group automatically. Compositional inheritance restored.
Behavior
StateiOSAndroidFigma PropertyNotes
2 avatarsYesYeslayout=pairDiagonal overlap — top-left + bottom-right
3 avatarsYesYeslayout=trioTriangle arrangement
4 avatarsYesYeslayout=quad2×2 grid
Overflow (5+)YesYeslayout=overflow3 avatars + "+N" badge in bottom-right slot. Uses the same default/light style as the avatar it replaces.
Pressed / DisabledN/AN/ADisplay-only. Tap behavior handled by parent container.
Resolved Issues
  • Property renamed: no. of initalslayout with semantic values (pair/trio/quad/overflow). Fixes typo, removes spaces/dots, replaces pseudo-numeric strings with true enum values. Maps cleanly to SwiftUI EBAvatarGroupLayout.pair/.trio/.quad/.overflow / Compose EBAvatarGroupLayout.Pair etc. C2 Fixed
  • Overflow variant layout=overflow added — bottom-right slot shows "+N" badge instead of a 4th avatar. Handles groups larger than 4. C5 Fixed
  • Inner avatars repointed via instance swap to the canonical Avatar component (17143:4488). Previously referenced a duplicate Avatar at 21:94766 — now all 4 variants inherit from the canonical source. Compositional pattern restored: changes to Avatar will propagate here automatically. C6 Fixed
Open Issues
  • Code Connect CLI mappings not registered. C7
Design Recommendations
  • Consider adding size variants (e.g. groupSize=small/medium/large) — current 48×48 is one size only. Bigger groups may need larger containers for readability. Suggested
  • Consolidate the duplicate Avatar component at 21:94766 — now that Avatar Group points at the canonical 17143:4488, the duplicate should be deprecated and eventually removed in a future DS cleanup pass. DS Hygiene
Variants

4 variants (pair / trio / quad / overflow). All use a 48×48 fixed container with 24×24 inner avatars arranged with intentional overlap to indicate grouping. Overflow replaces the 4th avatar with a "+N" badge for groups larger than 4.

Pair — 2 avatars

Two avatars placed diagonally. Top-left uses dark-initials (brand), bottom-right uses initials-light (default).

Trio — 3 avatars

Three avatars in a triangular arrangement. Two on top (dark + dark), one default at bottom.

Quad — 4 avatars

Four avatars in a 2×2 grid. Top row: brand + brand. Bottom row: default + default.

Overflow — 3 + "+N" badge

Overflow variant — 3 avatars plus a "+N" badge in the bottom-right position. Use when group has 5 or more members. The "+N" uses the default/light avatar style with overridable text content.

Colors by State

Inner avatars use the same tokens as the main Avatar component. See Avatar / Style tab for the full token reference.

RoleTokenValue
Brand avatar bgmain/avatar/brand/bg#005CE5
Brand avatar bordermain/avatar/brand/border#E5EBF4
Brand avatar initialsmain/avatar/brand/intials library typo#FFFFFF
Default avatar bgmain/avatar/default/bg#F6F9FD
Default avatar bordermain/avatar/default/border#E5EBF4
Default avatar initialsmain/avatar/default/initials#2340A9
Layout
PropertyValue
Container size48 × 48
Inner avatar size24 × 24
Inner avatar radius12px
Inner avatar border1.5px solid
Overlap offset (2 avatars)16px diagonal
Overlap offset (3 avatars)12px horizontal, 24px vertical
Overlap offset (4 avatars)24px grid step
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android — Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:avatar:1.0.0")
}
Property Mapping
Figma PropertySwiftUI ParamCompose ParamNotes
layout=pair.ebLayout(.pair)layout=EBAvatarGroupLayout.Pair2 avatars, diagonal overlap
layout=trio.ebLayout(.trio)layout=EBAvatarGroupLayout.Trio3 avatars, triangle
layout=quad.ebLayout(.quad)layout=EBAvatarGroupLayout.Quad4 avatars, 2×2 grid
layout=overflow.ebLayout(.overflow)layout=EBAvatarGroupLayout.Overflow3 avatars + "+N" badge
SwiftUI
ios/Components/Avatar/EBAvatarGroup.swift
Jetpack Compose
android/components/avatar/EBAvatarGroup.kt
Usage Snippets Planned API
// Pair / trio / quad — pass avatars, layout auto-detected from count
EBAvatarGroup(avatars: [
    EBAvatar(initials: "DM"),
    EBAvatar(initials: "LM"),
    EBAvatar(initials: "AB")
])

// Overflow — pass full list + max visible count
EBAvatarGroup(avatars: allAvatars, maxVisible: 3)
    .ebLayout(.overflow)
    // renders 3 avatars + "+N" if allAvatars.count > 3
// Pair / trio / quad — pass avatars, layout auto-detected from count
EBAvatarGroup(
    avatars = listOf(
        Avatar(initials = "DM"),
        Avatar(initials = "LM"),
        Avatar(initials = "AB")
    )
)

// Overflow — pass full list + maxVisible
EBAvatarGroup(
    avatars = allAvatars,
    maxVisible = 3,
    layout = EBAvatarGroupLayout.Overflow
)
Accessibility
RequirementiOSAndroid
Accessibility label.accessibilityLabel("3 participants: Dara, Lara, Alex")contentDescription="3 participants: ..."
RoleDecorative if not tappable — use .accessibilityHidden(true) on individual avatarsSame — prefer single group-level semantic
Tap target48 × 48 container meets iOS HIG when whole group is tappableMeets Material 48dp minimum
Usage Guidelines

Do

Use Avatar Group for 2–4 participants in a list item, header, or shared-with indicator.

Don't

Use for counts above 4 without an overflow "+N" badge — users can't infer total count from a cluster alone.

Do

Provide a single group-level accessibility label listing all participants.

Don't

Let each avatar announce separately — creates VoiceOver/TalkBack noise.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadySemantic names: Avatar, container. Top-level component set uses "Avatar Group" — clean.
C2Variant & Property NamingNeeds FixProperty no. of initals has typo, spaces, and uses string values. Rename to count with integer values.
C3Token CoverageReadyAll colors bound to Avatar's tokens. Inherits the same typo in intials — tracked under Avatar's open issues.
C4Native MappabilityPartialMaps to stacked avatars via ZStack (iOS) / Box + offset (Compose). Fixed 48×48 and count=2/3/4 don't match a dynamic native list — API should accept an array.
C5Interaction State CoverageNeeds FixNo overflow state for 5+ avatars. Common DS pattern ("+N" badge) is missing.
C6Asset & Icon QualityPartialInner avatars are hardcoded 24px containers, not Avatar component instances. Breaks compositional inheritance — changes to Avatar won't propagate.
C7Code Connect LinkabilityPendingNo CLI mappings registered yet.
Variants Inventory (4 total)
layoutNode IDSizeNotes
pair18276:455548 × 482 avatars — diagonal
trio18276:455848 × 483 avatars — triangle
quad18276:456248 × 484 avatars — 2×2 grid
overflow18276:458548 × 483 avatars + "+N" overflow badge
1.0.0 — April 2026 Initial
1.1.0 — April 2026 Minor
Structural closure · node 18276:4554
Property renamedno. of initalslayout. Values changed from pseudo-numeric strings (2/3/4/5+) to semantic enum values (pair/trio/quad/overflow). Fixes typo, spaces, and dots in one pass. Clean native enum mapping. Fixed
C2 Fixed
Overflow variant addedlayout=overflow displays 3 avatars + a "+N" badge in the 4th slot. Handles groups larger than 4. Added
C5 Fixed
Inner avatars repointed to canonical Avatar — Previously referenced a duplicate Avatar component at 21:94766. All 4 variants now use instances of the canonical Avatar at 17143:4488. Compositional inheritance restored. Swapped
C6 Fixed
1.0.0 — April 2026 Initial
Initial Assessment · node 21:94828
Component assessed — 3 variants (2/3/4 avatars) in a fixed 48×48 container. Used for participant lists, collaboration indicators. Documented
Initial
Property name has typo and spacesno. of initals: missing second "i", contains dot + space. Values are strings instead of integers. Blocks native enum mapping. Fixed in 1.1.0
C2 Fixed
No overflow variant — Component supports only 2/3/4 avatars. Most DS patterns include a "+N" overflow badge for groups larger than the max shown. Fixed in 1.1.0
C5 Fixed
Inner avatars hardcoded, not Avatar instances — The 24px child avatars are duplicated as plain containers inside this component. If the main Avatar changes, this group won't inherit updates. Fixed in 1.1.0
C6 Fixed
Code Connect mappings — No CLI mappings registered yet. Open
C7 Open

A circular display element showing user initials or a profile image. Supports 7 sizes (20px-90px) and 3 types (dark initials, light initials, image). Used when a profile image is unavailable or for visual user identification.

In Context

How the avatar appears in a real product screen — Contacts list with Favorites row (brand fill + default fill avatars in circular display).

Avatar component shown in the GCash Contacts screen with a Favorites row of circular initials avatars (JF, JD, D, C, ZD)
Live Preview

Toggle type and size to see the avatar update in real time.

DM
Properties
Type
Size
DS Health
Reusable
Pass
7 sizes from 20px to 90px cover all common avatar placements. 3 types (dark initials, light initials, image) handle fallback and branded scenarios.
Self-contained
Pass
All variants are self-contained with vector ELLIPSE fills, token-bound colors, and editable text. No external assets required.
Consistent
Partial
Token naming follows DS convention (main/avatar/...). Variant naming verified correct (type=initials-light). Border-radius tokenized to radius/radius-round. One C2 issue remaining: token main/avatar/brand/intials has typo (should be initials) — manual rename needed in Figma Variables panel.
Composable
Pass
Fits naturally in headers, list rows, profile screens, chat bubbles, and badge overlays. Simple circular shape composes well with any layout container.
Behavior
StateiOSAndroidFigma PropertyNotes
DefaultYesYestype + sizeDisplay-only. All 3 types fully defined across 7 sizes.
PressedN/AN/A--Display-only component. Tap behavior handled by parent container.
DisabledN/AN/A--Display-only component. No disabled state.
Focused (a11y)N/AN/A--Display-only. Focus rings rendered by parent interactive container if needed.

Avatar is display-only. Interaction states (pressed, disabled, focused) are N/A and handled by parent containers.

Resolved Issues
  • Border-radius: bound to radius/radius-round (99999) across all sizes — previously hardcoded per size (C3)
  • Border-width: confirmed fixed per size by design — not a token gap (C3)
  • Raster backgrounds replaced with vector ELLIPSE layers across all 5 affected initials variants (C6)
  • Avatar Group compound component created (previously a design recommendation) — see sibling component under Avatar group (C2)
  • Variant property value naming verified on recheck: variant names are correctly hyphenated as type=initials-light in Figma source. Earlier "spaces" report was an MCP output artifact (TypeScript enum generation converts hyphens to spaces). No action required. C2 Verified
Open Issues
  • Code Connect CLI mappings not registered C7
Noted — Library-level
  • Token name typo: main/avatar/brand/intials still missing second "i" — should be initials. Lives in the shared Variables collection, not in this component — so it's tracked here for visibility but not counted as an Avatar-level blocker. Fix requires renaming in Figma → Variables panel. C2 Noted
Design Recommendations
  • Add a badge overlay slot for status indicators (online/offline dot, notification count) — common in chat, contacts, and profile lists Suggested
Types
Dark Initials
DESDEV

Blue circle with white initials text. Branded avatar used as default when no photo is available.

DM
Properties
Size
Properties
Typedark-initials
Size64px
Colors by Type -- Dark Initials

Display-only component. No interaction states. All colors bound to main/avatar/brand/ tokens.

RoleTokenValue
Circle bgmain/avatar/brand/bg#005CE5
Circle bordermain/avatar/brand/border#E5EBF4
Initials textmain/avatar/brand/initials#FFFFFF
Light Initials
DESDEV

Light circle with blue initials text. Neutral variant for non-branded contexts.

LM
Properties
Size
Properties
Typeinitials-light
Size64px
Colors by Type -- Light Initials

Display-only component. No interaction states. All colors bound to main/avatar/default/ tokens.

RoleTokenValue
Circle bgmain/avatar/default/bg#F6F9FD
Circle bordermain/avatar/default/border#E5EBF4
Initials textmain/avatar/default/initials#2340A9
Image
DESDEV

User profile photo in a circle clip. Falls back to placeholder when image fails to load.

Properties
Size
Properties
Typeimage
Size64px
Colors by Type -- Image

Display-only component. Placeholder colors shown when image has not loaded. All colors bound to main/avatar/placeholder/ tokens.

RoleTokenValue
Placeholder bgmain/avatar/placeholder/bg#C2CFE5
Placeholder bordermain/avatar/placeholder/border#E5EBF4
Installation Planned API

iOS -- Swift Package Manager

// In Xcode: File -> Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

// Or in Package.swift:
.package(
    url: "https://github.com/AY-Org/eb-ds-ios",
    from: "1.0.0"
)

Android -- Gradle (Kotlin DSL)

// build.gradle.kts (app)
dependencies {
    implementation("com.eastblue.ds:avatar:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.avatar.*  // Compose

Package not yet published. These are the planned distribution paths. API shape is final -- native implementation is pending.

Property Mapping

Every row maps a Figma component property to its native equivalent.

Figma PropertySwiftUICompose
type=dark-initials.darkInitialsAvatarType.DarkInitials
type=initials-light.lightInitialsAvatarType.LightInitials
type=image.image(url:)AvatarType.Image(url)
size=20px...90pxsize: AvatarSizesize: AvatarSize
SwiftUI
ios/Components/Avatar/EBAvatar.swift
Jetpack Compose
android/components/avatar/EBAvatar.kt
Usage Snippets Planned API
Dark Initials
// Dark initials
EBAvatar("DM", type: .darkInitials, size: .large)
// Dark initials
EBAvatar(
    initials = "DM",
    type = AvatarType.DarkInitials,
    size = AvatarSize.Large
)
Light Initials
// Light initials
EBAvatar("LM", type: .lightInitials, size: .medium)
// Light initials
EBAvatar(
    initials = "LM",
    type = AvatarType.LightInitials,
    size = AvatarSize.Medium
)
Image
// Image
EBAvatar(imageURL: profileURL, size: .large)
// Image
EBAvatar(
    imageUrl = profileUrl,
    type = AvatarType.Image,
    size = AvatarSize.Large
)
Accessibility
RequirementiOSAndroid
Alt textaccessibilityLabel("User avatar")contentDescription="User avatar"
Decorative modeisAccessibilityElement=false (in lists)importantForAccessibility=no
Image loadingAsyncImage with placeholderSubcomposeAsyncImage with placeholder
Usage Guidelines

Do

Use dark-initials as default when no photo is available.

Don't

Use image type with placeholder -- use initials instead.

Do

Match avatar size to context (20px in dense lists, 90px in profiles).

Don't

Mix initials types in the same context.

Do

Always pass 2-letter initials (first + last).

Don't

Show single-letter or empty initials.

Do

Provide alt text for image avatars.

Don't

Skip accessibility labels.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadyLayers named container, background, replace here - image. Simple hierarchy. Minor: some sizes have a child also named container.
C2Variant & Property NamingReadyVariant naming resolved (initials-light). Token name typo fixed (main/avatar/brand/initials). Size values use px suffix (minor, no impact on native mapping).
C3Token CoveragePartial8 color tokens, full typography tokens, and radius/radius-round connected. Border-width is fixed per size (by design).
C4Native MappabilityReadyMaps to custom Circle-clipped view on both platforms. No web-only patterns.
C5Interaction State CoverageN/ADisplay-only component. No interactive states needed.
C6Asset & Icon QualityReadyAll initials variants now use vector ELLIPSE layers. Image type rasters are expected (user photos).
C7Code Connect LinkabilityNeeds RefinementUsage descriptions attached. Variant naming now clean. Token typo remains. No CLI mappings.
Code Connect

Maps type and size properties to native parameters. 21 variants (3 types x 7 sizes). Display-only -- no state dimension.

Type variant dimension
FigmaSwiftUICompose
dark-initialstype: .darkInitialsAvatarType.DarkInitials
initials-lighttype: .lightInitialsAvatarType.LightInitials
imageimageURL: URLAvatarType.Image(url)
Size variant dimension
FigmaSwiftUICompose
20pxsize: .xxSmallAvatarSize.XXSmall
24pxsize: .xSmallAvatarSize.XSmall
32pxsize: .smallAvatarSize.Small
40pxsize: .mediumAvatarSize.Medium
48pxsize: .largeAvatarSize.Large
64pxsize: .xLargeAvatarSize.XLarge
90pxsize: .xxLargeAvatarSize.XXLarge
Variants Inventory 21 total

3 type × 7 size=21 variants. No interaction state axis (display-only component).

TypeSizesNotesCount
dark-initials20, 24, 32, 40, 48, 64, 90 pxBrand background, white initials7
initials-light20, 24, 32, 40, 48, 64, 90 pxLight background, dark initials7
image20, 24, 32, 40, 48, 64, 90 pxPhoto fill (raster expected)7
View full Type × Size breakdown (21 rows)
TypeSizeRaster?Node ID
dark-initials20px--17143:4489
dark-initials24px--17143:4497
dark-initials32px--17143:4505
dark-initials40px--17143:4513
dark-initials48px--17143:4523
dark-initials64px--17143:4531
dark-initials90px--17143:4539
initials-light20px--17143:4492
initials-light24px--17143:4500
initials-light32px--17143:4508
initials-light40px--17143:4517
initials-light48px--17143:4526
initials-light64px--17143:4535
initials-light90px--17143:4542
image20pxexpected17143:4495
image24pxexpected17143:4503
image32pxexpected17143:4511
image40pxexpected17143:4521
image48pxexpected17143:4529
image64pxexpected17143:4546
image90pxexpected17143:4548

Raster column: ✗=initials background exported as raster image instead of vector. "expected"=image type uses photos by design.

1.0.0 -- March 2026 Initial
Initial Assessment -- node 17143:4488
Component assessed -- 21 variants documented across type (dark-initials / initials-light / image) x size (20px / 24px / 32px / 40px / 48px / 64px / 90px). Token audit found 8 component-specific color tokens + full typography token set. Documented
Initial
Variant naming resolved -- type=initials - light renamed to type=initials-light across all 7 variants. Now matches dark-initials hyphen style. Fixed
C2 Resolved
Token name typo fixed -- main/avatar/brand/intials corrected to main/avatar/brand/initials. Token now maps correctly to native implementations. Fixed
C2 Resolved
Raster backgrounds replaced -- 5 initials variants (dark-initials 40px/64px, initials-light 40px/64px/90px) now use vector ELLIPSE layers with token-bound fills instead of raster backgrounds. Fixed
C6 Resolved
Border-radius tokenized -- All sizes now use radius/radius-round (99999) instead of hardcoded per-size values (45.213px, 24px, 16px, 12px, 10px). Fixed
C3 Resolved
Code Connect mappings -- Usage descriptions and documentation links attached per variant. No native component files or Code Connect CLI mappings registered yet. Still Open
C7 Open

A colored pill/tag used to highlight an item's status for quick recognition. 68 variants across State (Primary/Brand/Info/Success/Warning/Danger/Disabled) x Level (Heavy/Medium/Light) x Type (Default/Voucher/Transaction/Dashboard). Display-only component with no interaction states.

In Context

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

Live Preview

Toggle State, Level, and Type to see the badge update in real time.

Properties
State
Level
Type
DS Health
Reusable
Pass
Universal status indicator used across transaction lists, vouchers, dashboards, and notifications. 7 semantic states and 3 emphasis levels cover all common badge use cases.
Self-contained
Pass
Pure display component. Carries its own background, label color, padding, and border-radius per variant. No external dependencies or slots.
Consistent
Partial
Token naming follows DS convention (main/badge/{semantic}/{level}/). Minor issues: State property names don't match token names (Info vs information, Success vs positive). Danger/Heavy and Disabled/Heavy Transaction variants have hardcoded opacity: 0.90.
Composable
Pass
Simple leaf component. Nests cleanly in list rows, card headers, table cells, and notification banners. No child slots or complex nesting.
Behavior
StateiOSAndroidFigma PropertyNotes
DefaultYesYesState + Level + TypeDisplay-only. All 7 states x 3 levels fully defined per type.

Badge is display-only. No interaction states (pressed, focused) exist. Disabled is a visual variant, not an interactive state -- it represents muted/inactive content styling.

Resolved Issues
  • Hardcoded opacity: 0.90 removed from Danger/Heavy Transaction (21:111576) and Disabled/Heavy Transaction (3714:3863) inner containers. Both now at opacity 1, consistent with the other 66 variants. C3 Fixed
  • State property values renamed to match token semantic names across all 60 affected variants: InfoInformation, SuccessPositive, WarningNotice, DangerNegative, DisabledMuted. Figma State values now align 1:1 with token namespace — cleaner Code Connect mapping, no translation layer needed. C2 Fixed
Open Issues
  • Code Connect CLI mappings not registered C7
Design Recommendations
  • Remove hardcoded opacity: 0.90 from Danger/Heavy and Disabled/Heavy Transaction variants to ensure consistency across all variants Suggested
  • Consider aligning State property names with token semantic names (e.g. State=Information instead of State=Info) to reduce Code Connect mapping friction Suggested
  • Add an icon slot (leading) for badges that need visual reinforcement -- common in status badges across fintech apps Suggested
  • Primary and Brand states only support Heavy level -- consider adding Medium/Light levels if these states need lower-emphasis variants in future Suggested
Types

4 types with different shapes and sizing: Default (pill), Voucher (bottom-right radius only), Transaction (rounded rect), Dashboard (compact rounded rect). Each type supports 7 states x 3 levels (Primary and Brand are Heavy only).

Default
DESDEV

Pill-shaped badge with full border-radius (99px). Standard status indicator for general use.

Properties
State
Level
Properties
TypeDefault
StatePrimary
LevelHeavy
Voucher
DESDEV

Badge with bottom-right radius only (4px). Used on voucher cards and promotional items. Fixed 18px height.

Properties
State
Level
Properties
TypeVoucher
StatePrimary
LevelHeavy
Transaction
DESDEV

Rounded rectangle badge (4px radius). Used in transaction lists and history screens. Compact padding.

Properties
State
Level
Properties
TypeTransaction
StatePrimary
LevelHeavy
Dashboard
DESDEV

Compact rounded rectangle badge (4px radius) with smaller typography (10px). Used in dashboard widgets and summary cards.

Properties
State
Level
Properties
TypeDashboard
StatePrimary
LevelHeavy
Colors by Appearance Mode

Display-only component. All colors bound to main/badge/{semantic}/{level}/ tokens. Primary and Brand states only support Heavy level.

StateLevelRoleTokenValue
PrimaryHeavybgmain/badge/primary/heavy/background#005CE5
PrimaryHeavylabelmain/badge/primary/heavy/label#FFFFFF
BrandHeavybgmain/badge/brand/heavy/background#1972F9
BrandHeavylabelmain/badge/brand/heavy/label#FFFFFF
InfoLightbgmain/badge/information/light/background#E5F1FF
InfoLightlabelmain/badge/information/light/label#005CE5
InfoMediumbgmain/badge/information/medium/background#D2E5FF
InfoMediumlabelmain/badge/information/medium/label#005CE5
InfoHeavybgmain/badge/information/heavy/background#2340A9
InfoHeavylabelmain/badge/information/heavy/label#FFFFFF
SuccessLightbgmain/badge/positive/light/background#E7F8F0
SuccessLightlabelmain/badge/positive/light/label#048570
SuccessMediumbgmain/badge/positive/medium/background#CAF2E0
SuccessMediumlabelmain/badge/positive/medium/label#048570
SuccessHeavybgmain/badge/positive/heavy/background#12AF80
SuccessHeavylabelmain/badge/positive/heavy/label#FFFFFF
WarningLightbgmain/badge/notice/light/background#FCF0CA
WarningLightlabelmain/badge/notice/light/label#966F0B
WarningMediumbgmain/badge/notice/medium/background#F7D96E
WarningMediumlabelmain/badge/notice/medium/label#966F0B
WarningHeavybgmain/badge/notice/heavy/background#CA970C
WarningHeavylabelmain/badge/notice/heavy/label#FFFFFF
DangerLightbgmain/badge/negative/light/background#F8E6E6
DangerLightlabelmain/badge/negative/light/label#B50707
DangerMediumbgmain/badge/negative/medium/background#F4C7C9
DangerMediumlabelmain/badge/negative/medium/label#8D0710
DangerHeavybgmain/badge/negative/heavy/background#D61B2C
DangerHeavylabelmain/badge/negative/heavy/label#FFFFFF
DisabledLightbgmain/badge/muted/light/background#C2C5CA
DisabledLightlabelmain/badge/muted/light/label#FFFFFF
DisabledMediumbgmain/badge/muted/medium/background#9A9FA7
DisabledMediumlabelmain/badge/muted/medium/label#FFFFFF
DisabledHeavybgmain/badge/muted/heavy/background#717883
DisabledHeavylabelmain/badge/muted/heavy/label#FFFFFF
Layout by Type
PropertyDefaultVoucherTransactionDashboard
Heightauto18px (fixed)autoauto
Padding H8px8px4px4px
Padding V2px (top) / 4px (bottom)2px (top) / 4px (bottom)1px (top) / 3px (bottom)1px
Corner radius99px (pill)0/0/4px/0 (BR only)4px4px
Typography
TypeText StyleFontSizeTrackingLine-height
Default / Voucher / TransactionPrimary/Label/FineProxima Soft Bold12px0.5px12px
DashboardPrimary/Label/TinyProxima Soft Bold10px0.25px10px
Installation Planned API

iOS -- Swift Package Manager

// In Xcode: File -> Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

// Or in Package.swift:
.package(
    url: "https://github.com/AY-Org/eb-ds-ios",
    from: "1.0.0"
)

Android -- Gradle (Kotlin DSL)

// build.gradle.kts (app)
dependencies {
    implementation("com.eastblue.ds:badge:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.badge.*  // Compose

Package not yet published. These are the planned distribution paths. API shape is final -- native implementation is pending.

Property Mapping

Every row maps a Figma component property to its native equivalent.

Figma PropertySwiftUIComposeNotes
State=Primarystate: .primaryBadgeState.PrimaryHeavy level only
State=Brandstate: .brandBadgeState.BrandHeavy level only
State=Infostate: .infoBadgeState.InfoToken: information
State=Successstate: .successBadgeState.SuccessToken: positive
State=Warningstate: .warningBadgeState.WarningToken: notice
State=Dangerstate: .dangerBadgeState.DangerToken: negative
State=Disabledstate: .disabledBadgeState.DisabledToken: muted
Level=Heavylevel: .heavyBadgeLevel.HeavySolid fill, white label
Level=Mediumlevel: .mediumBadgeLevel.MediumMid-tone fill, dark label
Level=Lightlevel: .lightBadgeLevel.LightSubtle fill, dark label
Type=Defaulttype: .defaultBadgeType.DefaultPill shape (99px radius)
Type=Vouchertype: .voucherBadgeType.VoucherBottom-right radius only
Type=Transactiontype: .transactionBadgeType.TransactionRounded rect (4px)
Type=Dashboardtype: .dashboardBadgeType.DashboardCompact, smaller font
SwiftUI
ios/Components/Badge/EBBadge.swift
Jetpack Compose
android/components/badge/EBBadge.kt
Usage Snippets Planned API
Default (Pill)
// Success badge -- heavy level
EBBadge("Completed", state: .success, level: .heavy)

// Info badge -- light level
EBBadge("Pending", state: .info, level: .light)
// Success badge -- heavy level
EBBadge(
    text = "Completed",
    state = BadgeState.Success,
    level = BadgeLevel.Heavy
)

// Info badge -- light level
EBBadge(
    text = "Pending",
    state = BadgeState.Info,
    level = BadgeLevel.Light
)
Voucher
EBBadge("50% OFF", state: .danger, level: .heavy, type: .voucher)
EBBadge(
    text = "50% OFF",
    state = BadgeState.Danger,
    level = BadgeLevel.Heavy,
    type = BadgeType.Voucher
)
Transaction
EBBadge("Failed", state: .danger, level: .heavy, type: .transaction)
EBBadge(
    text = "Failed",
    state = BadgeState.Danger,
    level = BadgeLevel.Heavy,
    type = BadgeType.Transaction
)
Dashboard
EBBadge("Active", state: .success, level: .light, type: .dashboard)
EBBadge(
    text = "Active",
    state = BadgeState.Success,
    level = BadgeLevel.Light,
    type = BadgeType.Dashboard
)
Accessibility
RequirementiOSAndroid
Accessibility labelaccessibilityLabel("Status: Completed")contentDescription="Status: Completed"
Decorative modeisAccessibilityElement=false (when status is conveyed elsewhere)importantForAccessibility=no
Color contrastHeavy levels meet WCAG AA (4.5:1+)Heavy levels meet WCAG AA (4.5:1+)
Non-color indicatorBadge text conveys meaning alongside colorBadge text conveys meaning alongside color
Usage Guidelines

Do

Use semantic states that match the content meaning (Success for completed, Danger for failed, Warning for pending).

Don't

Use badges for interactive elements -- badges are display-only status indicators.

Do

Use Heavy level for primary status indicators and Light/Medium for secondary or supporting context.

Don't

Use multiple Heavy badges in the same row -- visual noise. Use one Heavy + rest Light/Medium.

Do

Match badge Type to context: Default for general, Voucher for promos, Transaction for history, Dashboard for summaries.

Don't

Mix badge Types within the same list or group.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadySimple single-layer structure: container with text child. Semantic naming.
C2Variant & Property NamingReadyClean enum properties: State, Level, Type. Minor: State names don't match token semantic names.
C3Token CoverageReadyAll colors bound to main/badge/ tokens. Note: 2 variants have hardcoded opacity: 0.90.
C4Native MappabilityReadyMaps to custom EBBadge on both platforms. Simple text + background shape.
C5Interaction State CoverageN/ADisplay-only component. No interactive states needed.
C6Asset & Icon QualityN/ANo icons or assets. Text-only component.
C7Code Connect LinkabilityNeeds RefinementNo CLI mappings registered yet. Property naming is clean and ready for mapping.
Code Connect
AspectStatusNotes
Property namingReadyClean enum properties: State (7), Level (3), Type (4). Ready for Code Connect mapping.
Token coverageReadyAll colors token-bound. Minor opacity inconsistency on 2 variants.
State coverageN/ADisplay-only. No interaction states.
Native component filePendingEBBadge.swift / EBBadge.kt not yet created
Variants Inventory (68 total)

7 State values x 3 Level values x 4 Type values=84 theoretical. Primary and Brand only support Heavy level, so actual count is (2 x 1 x 4) + (5 x 3 x 4)=8 + 60=68 variants.

StateLevelTypesNotes
PrimaryHeavyDefault, Voucher, Transaction, Dashboard4 variants
BrandHeavyDefault, Voucher, Transaction, Dashboard4 variants
InfoLight / Medium / HeavyDefault, Voucher, Transaction, Dashboard12 variants
SuccessLight / Medium / HeavyDefault, Voucher, Transaction, Dashboard12 variants
WarningLight / Medium / HeavyDefault, Voucher, Transaction, Dashboard12 variants
DangerLight / Medium / HeavyDefault, Voucher, Transaction, Dashboard12 variants
DisabledLight / Medium / HeavyDefault, Voucher, Transaction, Dashboard12 variants
1.1.0 -- April 2026 Minor
Structural closure -- node 21:111526
Opacity normalized -- Hardcoded opacity: 0.90 removed from Danger/Heavy Transaction (21:111576) and Disabled/Heavy Transaction (3714:3863) inner containers. Both now at opacity 1, consistent with the other 66 variants. Fixed
C3 Fixed
State values renamed to match token semantics -- 60 variants renamed: Info ->Information, Success ->Positive, Warning ->Notice, Danger ->Negative, Disabled ->Muted. Figma State property now aligns 1:1 with token namespace (main/badge/{information|positive|notice|negative|muted}/{level}/). Cleaner Code Connect mapping with no translation layer. Fixed
C2 Fixed
1.0.0 -- April 2026 Initial
Initial Assessment -- node 21:111526
Component assessed -- 68 variants documented across State (Primary/Brand/Info/Success/Warning/Danger/Disabled) x Level (Heavy/Medium/Light) x Type (Default/Voucher/Transaction/Dashboard). Token audit confirms all colors bound to main/badge/{semantic}/{level}/ tokens. Documented
Initial
Hardcoded opacity on 2 variants -- Danger/Heavy and Disabled/Heavy Transaction variants have opacity: 0.90 on container instead of using token-driven values. Inconsistent with other variants. Fixed in 1.1.0
C3 Fixed
State-to-token name mismatch -- Figma property names (Info, Success, Warning, Danger, Disabled) don't match token semantic names (information, positive, notice, negative, muted). Minor friction for automated Code Connect mapping. Fixed in 1.1.0
C2 Fixed
Code Connect mappings -- No CLI mappings registered yet. Property naming is clean and ready for mapping. Open
C7 Open

Used to trigger an action when tapped. The button's Call to Action describes the action that will occur. The Large/Medium Buttons are the default size for the GCash app.

In Context

How the button appears in a real product screen — primary and secondary actions in a bottom sheet.

Button component shown in a GCash Physical Card bottom sheet with primary and secondary buttons
Live Preview

Toggle properties and appearance modes to see the button update in real time.

Properties
Style
State
Size
Icon Placement
Mode
Appearance
DS Health
Reusable
Pass
Three styles (Filled/Outline/Text) with four appearance modes cover primary, secondary, tertiary, surface, and destructive action patterns across all contexts.
Self-contained
Pass
All styles, states, and appearance colors are self-contained via variable bindings. Leading Container and Trailing Container SLOT nodes in every variant.
Consistent
Pass
Clean Property=Value naming across all 60 variants. Size, State, and Style are orthogonal variant dimensions. Appearance is a variable mode — no naming conflicts. 12 color variables bound consistently.
Composable
Pass
Style=Filled → Button, Style=Outline → OutlinedButton, Style=Text → TextButton. SLOT nodes support icon+label compositions. Each size has its own text style — clean native mapping.
Behavior
StateiOSAndroidFigma PropertyNotes
DefaultYesYesState=DefaultAll four appearance modes fully defined.
PressedYesYesState=PressedDarker fill/border using pressed tokens.
DisabledYesYesState=DisabledMuted color tokens applied across all appearances.
DestructiveYesYesAppearance mode: DestructiveRed tokens via variable mode. Applies to all 3 styles (Filled/Outline/Text).
Focused (a11y)N/AN/AMobile-only component. Focus rings rendered natively by iOS (UIKit/SwiftUI) and Android (Material a11y). No Figma state required.
LoadingYesYesNative modifierHandled as an interaction modifier in native code — .ebLoading(true) (SwiftUI) / isLoading=true (Compose). Removed as a Figma state in v4.0.
Icon Only (a11y)YesYesIcon Placement=Icon OnlySquare target matches size height. Requires accessibilityLabel / contentDescription since no visible text.
Resolved Issues
  • Layer renamed from .base/button/smallcontainer on compact disabled container (C1)
  • Icon slots (Leading Container, Trailing Container) added to all variants as Figma SLOT nodes (C2)
  • isError replaced — Destructive is now an appearance variable mode, not a variant property (C2)
  • v2: Outlined and Text Link moved from appearance to Style variant property (Filled/Outline/Text) (C2)
  • v2: Size moved from variable modes to variant dimension — each size has its own text style, eliminating font-size variable conflict (C2/C3)
  • v3: Button variable collection created with 4 appearance modes (Default/Destructive/White/Subtle) — 12 color variables bound to all 60 variants (C3)
  • v3: Old Button Size and button/variant collections removed (C3)
  • v3.1: Loading state added — 12 new State=Loading variants with dot indicators replacing label, disabled appearance colors (C5)
  • v4.0: Icon Placement promoted to component property — replaces leadingIcon/trailingIcon booleans with a single 4-value enum (None/Leading/Trailing/Icon Only). Adds Icon Only square variant for toolbars/navigation (previously a design recommendation). Handoff is now explicit — developers see icon placement as a first-class property. (C2)
  • v4.0: Appearance Mode documented in Figma component description with SwiftUI/Compose API mapping — addresses the Mode-invisibility handoff gap until Code Connect is implemented. (C7 partial)
  • v4.0: State simplified to Default/Pressed/Disabled — Loading moved to a native interaction modifier rather than a Figma variant. (C5)
  • v4.1: button-container wrapper layer removed — outermost component now holds fill/radius/auto-layout directly. Layer depth reduced from 4 to 3 (component → container → label/icon). Inner container retained for icon-label gap grouping. (C1)
  • v4.1: Large height reduced from 56px → 50px per design review feedback. (C3)
  • v4.1: New Mode-driven token collection applied — all 60 Filled variants bound to appearance/container/fill (+ pressed/disabled), all 60 Outline variants bound to appearance/stroke/color + new appearance/label/on-surface/color, all 60 Text variants bound to appearance/label/on-surface/color. Switching the parent frame's Variable Mode (Default / Destructive / White / Subtle) now drives appearance across all 180 variants. (C3)
  • v4.1: New appearance/label/on-surface/color variable created — semantic separation between labels on filled vs surface backgrounds. Eliminates token-purpose confusion between Filled labels (white-on-fill) and Outline/Text labels (color-on-surface). (C3)
  • v4.1: Text styles renamed to cleaner Primary/Label/Large, Primary/Label/Base, Primary/Label/Small, Primary/Label/Fine (was Primary/Label/Light/* family). (C3)
Open Issues
  • Code Connect CLI mappings not registered (C7)
Design Recommendations
  • Document full-width (stretch) behavior as a layout guideline — consider a isFullWidth boolean property for bottom-sheet CTAs and standalone action areas Suggested
Styles
Filled
DESDEV

Solid background with contrasting label. Primary action style. Colors change via Appearance variable mode.

Properties
State
Size
Icon Placement
Mode
Appearance
Properties
AppearanceDefault
StateDefault
SizeLarge
Colors by Appearance Mode

Token names resolve to different hex values per mode. All 4 modes share the same 4 variables from the Button collection.

ModeRoleEnabledPressedDisabled
Defaultbg#005CE5#2340A9#9BC5FD
label#FFFFFF#FFFFFF#FFFFFF
Destructivebg#D81E1E#B01818#F5A3A3
label#FFFFFF#FFFFFF#FFFFFF
Whitebg#FFFFFF#EEF2F9#F5F7FA
label#005CE5#005CE5#005CE5
Subtlebg#E5F1FF#D2E5FF#EEF5FF
label#005CE5#005CE5#005CE5
Tokens (v4.1 Mode-driven):appearance/container/fillappearance/container/fill-pressedappearance/container/fill-disabledappearance/label/colorappearance/label/color-pressedappearance/label/color-disabled
Outline
DESDEV

Transparent background with border and accent-colored label. Secondary action style.

Properties
State
Size
Icon Placement
Mode
Appearance
Properties
AppearanceDefault
StateDefault
SizeLarge
Colors by Appearance Mode

Outline uses border + label tokens — no background fill. All 4 modes share the same 3 variables from the Button collection.

ModeRoleEnabledPressedDisabled
Defaultborder#005CE5#2340A9#9BC5FD
label#005CE5#2340A9#9BC5FD
Destructiveborder#D81E1E#B01818#F5A3A3
label#D81E1E#B01818#F5A3A3
Whiteborder#005CE5#2340A9#9BC5FD
label#005CE5#2340A9#9BC5FD
Subtleborder#005CE5#2340A9#9BC5FD
label#005CE5#2340A9#9BC5FD
Tokens (v4.1 Mode-driven):appearance/stroke/colorappearance/stroke/color-pressedappearance/stroke/color-disabledappearance/label/on-surface/colorappearance/label/on-surface/color-pressedappearance/label/on-surface/color-disabled
Text
DESDEV

No background or border. Label only. Tertiary action style.

Properties
State
Size
Icon Placement
Mode
Appearance
Properties
AppearanceDefault
StateDefault
SizeLarge
Colors by Appearance Mode

Text style uses label-only tokens — no background or border. All 4 modes share the same 3 variables from the Button collection.

ModeRoleEnabledPressedDisabled
Defaultlabel#005CE5#2340A9#9BC5FD
Destructivelabel#D81E1E#B01818#F5A3A3
Whitelabel#005CE5#2340A9#9BC5FD
Subtlelabel#005CE5#2340A9#9BC5FD
Tokens (v4.1 Mode-driven):appearance/label/on-surface/colorappearance/label/on-surface/color-pressedappearance/label/on-surface/color-disabled
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

// Or in Package.swift:
.package(
    url: "https://github.com/AY-Org/eb-ds-ios",
    from: "2.0.0"
)

Android — Gradle (Kotlin DSL)

// build.gradle.kts (app)
dependencies {
    implementation("com.eastblue.ds:button:2.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.button.*  // Compose

Package not yet published. These are the planned distribution paths. API shape is final — native implementation is pending.

Property Mapping

Every row maps a Figma component property to its native equivalent. When a developer selects a variant in Figma, Code Connect will output the corresponding native code using these mappings.

Figma PropertySwiftUICompose
Style=Filled.ebAppearance(.filled)EBButton {}
Style=Outline.ebAppearance(.outlined)EBOutlinedButton {}
Style=Text.ebAppearance(.textLink)EBTextButton {}
Appearance=Default(default — omit modifier)(default — omit colors param)
Appearance=Destructive.ebColorScheme(.destructive)colors=EBButtonDefaults.destructiveColors()
Appearance=White.ebColorScheme(.white)colors=EBButtonDefaults.whiteColors()
Appearance=Subtle.ebColorScheme(.subtle)colors=EBButtonDefaults.subtleColors()
Size=Large…XSmallcontrolSize: .large / .regular / .small / .compact / .minisize=EBButtonSize.Large / Medium / Small / Compact / XSmall
State=Disabled.disabled(true)enabled=false
(Loading — runtime).ebLoading(true)isLoading=true
Icon Placement=None(default — text only)(default — text only)
Icon Placement=LeadingLabel("…", systemImage: "…")leadingIcon={ Icon(…) }
Icon Placement=TrailingLabel + trailing ImagetrailingIcon={ Icon(…) }
Icon Placement=Icon OnlyEBButton(icon: Image(…), accessibilityLabel: "…")EBButton(contentDescription="…") { Icon(…) }
SwiftUI
ios/Components/Button/EBButton.swift
Jetpack Compose
android/components/button/EBButton.kt
Usage Snippets Planned API
Filled — Primary action
// Default appearance — Mode resolves at parent (.environment(\.ebAppearance, .default))
EBButton("Save Changes")
    .ebAppearance(.filled)
    .controlSize(.large)

// Destructive appearance
EBButton("Delete Account")
    .ebAppearance(.filled)
    .ebColorScheme(.destructive)

// Icon Placement = Leading
EBButton("Send Money", leadingIcon: Image(systemName: "arrow.up.right"))
    .ebAppearance(.filled)

// Icon Placement = Trailing
EBButton("Continue", trailingIcon: Image(systemName: "chevron.right"))
    .ebAppearance(.filled)

// Icon Placement = Icon Only — square target, accessibility label required
EBButton(icon: Image(systemName: "plus"), accessibilityLabel: "Add item")
    .ebAppearance(.filled)

// Disabled
EBButton("Submit")
    .ebAppearance(.filled)
    .disabled(true)

// Loading — runtime only, not a Figma state
EBButton("Submit")
    .ebAppearance(.filled)
    .ebLoading(true)
// Default appearance — Mode resolves at theme/parent
EBButton(
    onClick = { /* action */ },
    size = EBButtonSize.Large
) {
    Text("Save Changes")
}

// Destructive appearance
EBButton(
    onClick = { /* action */ },
    colors = EBButtonDefaults.destructiveColors()
) {
    Text("Delete Account")
}

// Icon Placement = Leading
EBButton(
    onClick = { },
    leadingIcon = { Icon(Icons.Default.Send, contentDescription = null) }
) {
    Text("Send Money")
}

// Icon Placement = Trailing
EBButton(
    onClick = { },
    trailingIcon = { Icon(Icons.Default.ChevronRight, contentDescription = null) }
) {
    Text("Continue")
}

// Icon Placement = Icon Only — contentDescription required
EBButton(
    onClick = { },
    contentDescription = "Add item"
) {
    Icon(Icons.Default.Add, contentDescription = null)
}

// Disabled
EBButton(
    onClick = { },
    enabled = false
) {
    Text("Submit")
}

// Loading — runtime only, not a Figma state
EBButton(
    onClick = { },
    isLoading = true
) {
    Text("Submit")
}
Outline — Secondary action
// Default
EBButton("Cancel")
    .ebAppearance(.outlined)

// Destructive
EBButton("Remove Item")
    .ebAppearance(.outlined)
    .ebColorScheme(.destructive)

// Icon Placement = Leading
EBButton("Filter", leadingIcon: Image(systemName: "line.3.horizontal.decrease"))
    .ebAppearance(.outlined)

// Icon Placement = Icon Only
EBButton(icon: Image(systemName: "square.and.arrow.up"), accessibilityLabel: "Share")
    .ebAppearance(.outlined)

// Button pair
HStack(spacing: 12) {
    EBButton("Cancel").ebAppearance(.outlined)
    EBButton("Save").ebAppearance(.filled)
}
EBOutlinedButton(
    onClick = { /* action */ }
) {
    Text("Cancel")
}

// Icon Placement = Leading
EBOutlinedButton(
    onClick = { },
    leadingIcon = { Icon(Icons.Default.FilterList, contentDescription = null) }
) {
    Text("Filter")
}

// Icon Placement = Icon Only
EBOutlinedButton(
    onClick = { },
    contentDescription = "Share"
) {
    Icon(Icons.Default.Share, contentDescription = null)
}

// Button pair
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
    EBOutlinedButton(onClick = {}) { Text("Cancel") }
    EBButton(onClick = {}) { Text("Save") }
}
Text — Tertiary action
EBButton("Learn More")
    .ebAppearance(.textLink)
    .controlSize(.small)

// Destructive
EBButton("Remove")
    .ebAppearance(.textLink)
    .ebColorScheme(.destructive)

// Icon Placement = Trailing (common for inline links)
EBButton("Read more", trailingIcon: Image(systemName: "chevron.right"))
    .ebAppearance(.textLink)
EBTextButton(
    onClick = { /* action */ },
    size = EBButtonSize.Small
) {
    Text("Learn More")
}

// Icon Placement = Trailing (common for inline links)
EBTextButton(
    onClick = { },
    trailingIcon = { Icon(Icons.Default.ChevronRight, contentDescription = null) }
) {
    Text("Read more")
}
Accessibility
RequirementiOSAndroid
Min touch target44 × 44pt48 × 48dp
Focus ringHandled by UIKit/SwiftUIHandled by Material ripple
Icon-only buttons.accessibilityLabel("Send")contentDescription="Send"
Destructive rolerole: .destructive — announced by VoiceOverUse semantics { role=Role.Button }
Loading state.accessibilityLabel("Loading") + disable tapsemantics { stateDescription="Loading" } + disable click
Usage Guidelines

Do

Use one Filled button per screen area as the primary action. Pair with Outline or Text for secondary.

Don't

Place two filled buttons side by side — they compete for attention and neither reads as primary.

Do

Use Destructive appearance for irreversible actions (delete, remove). Always pair with a confirmation.

Don't

Use Destructive for actions that are simply "negative" but reversible (dismiss, close, decline).

Do

Use White appearance on brand-colored or dark surfaces (hero banners, promotional cards).

Don't

Use White appearance on a white background — the button disappears. Use Default or Subtle instead.

Do

Use Text style for inline or low-emphasis actions (Learn more, View terms, Skip).

Don't

Use Text style for primary form submission — it lacks the visual weight to signal the main action.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadyAll layers use semantic names. container, #label, leadingIcon, trailingIcon consistent across all 60 variants.
C2Variant & Property NamingReadyv3 clean orthogonal dimensions: Style (Filled/Outline/Text), Size (Large/Medium/Small/Compact/XSmall), State (Default/Pressed/Disabled). Appearance via 4 variable modes. leadingIcon/trailingIcon are SLOT nodes with Boolean component properties.
C3Token CoverageReadyAll color values connected to semantic tokens. Layout/sizing driven by button/size variable collection (height, padding-h, padding-v, font-size).
C4Native MappabilityReadyMaps to Button, OutlinedButton, TextButton. Destructive maps to role: .destructive / contentColor=errorColor.
C5Interaction State CoverageReadyDefault, Pressed, Disabled, Loading covered across all 60 variants. Focus ring is N/A — mobile OS handles natively. Loading uses dot indicators with disabled appearance colors.
C6Asset & Icon QualityReadyIcon slots are Figma SLOT nodes accepting vector icon instances. Boolean properties control visibility.
C7Code Connect LinkabilityNeeds RefinementNo CLI mappings registered yet. Property structure is clean and ready for mapping.
Code Connect

Maps v3.2 variant dimensions (Style × Size × State) and variable modes (Appearance) to native parameters. 60 variants × 4 appearance modes=240 visual states.

Style variant dimension
FigmaSwiftUICompose
Filled.ebAppearance(.filled)EBButton {}
Outline.ebAppearance(.outlined)EBOutlinedButton {}
Text.ebAppearance(.textLink)EBTextButton {}
Size variant dimension
FigmaSwiftUICompose
Large 52px.controlSize(.large)size=EBButtonSize.Large
Medium 36px.controlSize(.regular)size=EBButtonSize.Medium
Small 28px.controlSize(.small)size=EBButtonSize.Small
Compact 28px.controlSize(.compact)size=EBButtonSize.Compact
XSmall 24px.controlSize(.mini)size=EBButtonSize.XSmall
State variant dimension
FigmaSwiftUICompose
Default(default)(default)
Pressed(system)(system)
Disabled.disabled(true)enabled=false
(Loading — runtime).ebLoading(true)isLoading=true
Appearance variable mode
FigmaSwiftUICompose
Default(omit — default)(omit — default)
Destructive.ebColorScheme(.destructive)colors=…destructiveColors()
White.ebColorScheme(.white)colors=…whiteColors()
Subtle.ebColorScheme(.subtle)colors=…subtleColors()
Icon Placement component property (v4.0)
FigmaSwiftUICompose
None(text only — default)(text only — default)
LeadingleadingIcon: Image(…)leadingIcon={ Icon(…) }
TrailingtrailingIcon: Image(…)trailingIcon={ Icon(…) }
Icon OnlyEBButton(icon:, accessibilityLabel:)EBButton(contentDescription=…) { Icon(…) }
Variants Inventory (180 total)

3 Style × 5 Size × 3 State × 4 Icon Placement=180 variants. Appearance is a variable mode (Default/Destructive/White/Subtle) that further multiplies visual states × 4=720 resolved visual states.

StyleSizesStatesIcon PlacementsCount
FilledLarge, Medium, Small, Compact, XSmallDefault, Pressed, DisabledNone, Leading, Trailing, Icon Only60
OutlineLarge, Medium, Small, Compact, XSmallDefault, Pressed, DisabledNone, Leading, Trailing, Icon Only60
TextLarge, Medium, Small, Compact, XSmallDefault, Pressed, DisabledNone, Leading, Trailing, Icon Only60
View full Style × Size breakdown (15 rows)
StyleSizeHeightStates × Icon PlacementsCount
FilledLarge50px3 × 412
FilledMedium48px3 × 412
FilledSmall36px3 × 412
FilledCompact28px3 × 412
FilledXSmall24px3 × 412
OutlineLarge50px3 × 412
OutlineMedium48px3 × 412
OutlineSmall36px3 × 412
OutlineCompact28px3 × 412
OutlineXSmall24px3 × 412
TextLarge50px3 × 412
TextMedium48px3 × 412
TextSmall36px3 × 412
TextCompact28px3 × 412
TextXSmall24px3 × 412
4.1.0 — April 2026 Minor
Mode-driven tokens applied + structure flatten + height refinement · node 17104:184842
Mode-driven appearance tokens applied to all 180 variants — Filled fills bound to appearance/container/fill (and pressed/disabled), Outline borders bound to appearance/stroke/color, all Outline + Text labels bound to new appearance/label/on-surface/color. Switching the parent frame's Variable Mode now drives appearance across the entire variant set. Validates the Mode → Property → API translation pattern for upcoming Code Connect work. Applied
C3 Improved
New appearance/label/on-surface/color variable created — 3 variants (color, color-pressed, color-disabled) × 4 modes. Provides semantic separation: label/color=labels on filled backgrounds (white-on-fill), label/on-surface/color=labels on transparent/surface backgrounds (color-on-surface). Eliminates the binding ambiguity for Outline/Text styles. Added
C3 Improved
button-container wrapper layer removed — Visual properties (fill, radius, auto-layout, padding) lifted from inner button-container frame up to the variant component itself. Layer depth: 4 → 3. Native parity improved (the component IS the styled element, matching SwiftUI/Compose conventions). Inner container frame retained for icon-label gap grouping. Restructured
C1 Improved
Large height reduced 56 → 50px — Per design review approval. Matches the visual rhythm of other CTAs in the system. Padding adjusted to maintain proportions. Refined
C3 Refined
Text styles renamedPrimary/Label/Large (was Primary/Label/Light/Base), Primary/Label/Base, Primary/Label/Small, Primary/Label/Fine. Cleaner semantic naming, removes the redundant "Light" prefix. Renamed
C2 Improved
Figma component description added — Documents the Appearance Mode → SwiftUI/Compose API mapping directly in the Figma component description. Surfaces the Mode layer in dev handoff (Dev Mode panel). Will be superseded by Code Connect when C7 is implemented. Documented
C7 Partial
4.0.0 — April 2026 Major
Icon Placement restructure + Appearance Mode documentation · node 17104:184842
Icon Placement promoted to component property — Previously two boolean toggles (leadingIcon, trailingIcon) caused handoff ambiguity. Now a single 4-value enum: None / Leading / Trailing / Icon Only. Adds Icon Only as a new square-button variant. Total variants: 60 → 180 (3 Styles × 3 States × 5 Sizes × 4 Icon Placements). Restructured
C2 Improved
Appearance Mode documented in Figma component description — Appearance (Default/Destructive/White/Subtle) remains a Variable Mode for token reuse but is now explicitly documented in the Figma component description with SwiftUI/Compose API mapping. Addresses the Mode-invisibility handoff gap. Will be superseded by Code Connect when C7 is implemented. Documented
C7 Partial
State property reduced to 3 valuesState now Default/Pressed/Disabled. Loading is handled as an interaction modifier in native code rather than a Figma variant. Simplified
C5 Refined
3.2.0 — March 2026 Minor
Changes Applied via Figma MCP · node 17104:184842
Compact size added — New Size=Compact (28px height) between Small and XSmall. 12 new variants. Total: 60 variants (3 Styles × 5 Sizes × 4 States). Added
C2 Improved
Height tokens bound — All sizes now use space tokens for height: Large=space/space-56, Medium=space/space-48, Small=space/space-36, Compact=space/space-28. XSmall height still derived from padding. Refined
C3 Improved
3.1.0 — March 2026 Minor
Loading State Added via Figma MCP · node 17104:184842
Loading state added as 4th state dimension — 12 new State=Loading variants (3 Styles × 4 Sizes). Dot indicators (● ● ●) replace label text. Uses disabled appearance colors. Tap is disabled during loading. Added
C5 Resolved
Variant count increased from 36 → 48 — 4 states (Default/Pressed/Disabled/Loading) × 4 sizes × 3 styles. 192 visual states across 4 appearance modes. Updated
+12 Variants
2.0.0 — March 2026 Major
Component Restructure via Figma MCP · node 17104:184842
isError replaced with Variant: Brand | Destructive — True orthogonal property applied to all 24 variants. Destructive Default (filled) variants added for all 3 states. All 30 existing variants renamed. Fixed
C2 Resolved
White and Subtle appearances added — 6 new Brand-only variants (3 States each). White for inverse/dark-surface contexts; Subtle for neutral-tinted surface contexts. Added
+6 Variants
Size dimension removed from variant matrix — Compact variants deleted. Size is now driven by the button/size variable collection with 4 modes: Large (52px), Medium (36px), Small (28px), XSmall (24px). Reduces variant count from 36 → 24 while expanding size coverage. Restructured
36 → 24 Variants
button/size variable collection created — 5 variables (height, font-size, padding-h, padding-v, icon-size) bound to all containers, labels, and icon slots across all variants. Fixed height binding prevents icon slot size from affecting button height. Added
C3 Enhanced
Icon slots upgraded to SLOT nodes with Boolean propertiesleadingIcon and trailingIcon promoted from hidden frames to Figma SLOT nodes. Boolean component properties added for designer toggle control. Upgraded
C6 Enhanced
1.3.0 — March 2026 Re-assessment
Re-assessment · node 17104:184842
isError re-classified as C2 issueisError is not a true orthogonal boolean. Only applies to Outlined and Text Link, not Default. Recommendation: fold into Appearance as Outlined Error / Text Link Error. Resolved in v2.0.0. Resolved in 2.0.0
C2 Re-opened
Focus ring removed from C5 scope — Component is mobile-only. Focus rings rendered natively by iOS (UIKit/SwiftUI) and Android (Material a11y). No Figma state required. Clarified
C5 Scope Revised
1.2.0 — March 2026 Minor
Changes Applied via Figma MCP · node 17104:184842
Icon slots added: leadingIcon + trailingIcon — Added to all 30 variants. Hidden by default. Upgraded to SLOT nodes with Boolean properties in v2.0.0. Fixed
C2 Resolved
1.1.0 — March 2026 Minor
Changes Applied via Figma MCP · node 17104:184842
Layer renamed: .base/button/smallcontainer — Resolves C1. Fixed
C1 Resolved
1.0.0 — March 2026 Initial
Initial Assessment · node 17104:184842
Component assessed — 30 variants documented. Documented
Initial
Token audit complete — 24 color + 9 layout tokens confirmed. Verified
C3 Pass
Focus ring and loading state missing — Loading resolved in v3.1.0. Focus ring is N/A for mobile (rendered natively by iOS/Android). Resolved
C5 Pass
Code Connect mappings — No CLI mappings registered. Still Open
C7 Open
Checkbox Component link

A selection control for binary and partial choices. 33 variants across isSelected (true/false/indeterminate) × State (Default/Pressed/Focused/Disabled/Error) × Size (Small/Medium/Large). Code Connect registration pending.

In Context

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

Live Preview

Toggle size and state to see the checkbox update in real time.

Properties
isSelected
State
Size
DS Health
Reusable
Partial
All 5 interaction states and indeterminate defined across 3 sizes (C5 resolved). Checkbox is icon-only by design — CheckboxItem compound component provides label + description pairing.
Self-contained
Pass
Checkmark is a separable icon-check child layer (C6 resolved). All 5 interaction states and indeterminate defined across 3 sizes (C5 resolved). Carries its own visual states and token bindings.
Consistent
Pass
Token naming follows DS convention (main/checkbox/color/...). isSelected uses true/false/indeterminate. All property values follow boolean and enum standards (C2 resolved).
Composable
Partial
Nests in form layouts, list rows, and select-all patterns. Indeterminate state defined. CheckboxItem compound component wraps Checkbox + Label + Description for accessible form groups.
Behavior
StateiOSAndroidFigma PropertyNotes
UncheckedYesYesisSelected=falseBorder-only container. 3 sizes.
CheckedYesYesisSelected=trueBlue fill + separable icon-check layer.
IndeterminateYesYesisSelected=indeterminateBlue fill + icon-indeterminate dash.
DisabledYesYesState=Disabled40% opacity. Checked: #9BC5FD fill.
PressedYesYesState=PressedUnchecked: #EBF2FF bg. Checked: #0F57C8.
FocusedYesYesState=FocusedBlue #1972F9 border stroke.
ErrorYesYesState=ErrorRed border / red #D81E1E fill.
Resolved Issues
  • isSelected=Yes/No renamed to isSelected=true/false in Figma — now maps correctly to Swift Bool and Kotlin BooleanC2 Fixed
  • Checkmark rebuilt as a separable icon-check child layer inside each checked container — engineers can now tint, swap, and reference it via Code Connect C6 Fixed
  • Added 27 new variants — State (Pressed/Focused/Disabled/Error) × isSelected (true/false) × Size, plus isSelected=indeterminate per size with icon-indeterminate dash layer. 6 → 33 total variants C5 Fixed
Open Issues
  • Code Connect CLI mappings not registered — all structural blockers resolved, registration can now proceed (C7)
Design Recommendations
  • CheckboxItem compound component created — Checkbox instance + Label (Proxima Soft Bold) + Description (BarkAda Medium). 4 variants: isSelected (true/false) × Size (Small 14px label / Medium 18px label). Node: 17734:161220Created
States

33 variants across 3 axes: State (Default/Pressed/Focused/Disabled/Error) × isSelected (true/false/indeterminate) × Size (Small/Medium/Large). All interaction states carry proper container fills, strokes, and separable icon layers.

Unchecked
DESDEV

Empty container with border stroke. Represents a deselected option.

Properties
isSelectedfalse
State
Size
Checked
DESDEV

Filled container with white checkmark. Represents a selected option. Checkmark is rendered via a separable icon-check child layer.

Properties
isSelectedtrue
State
Size
Colors by State

All interaction states now have defined colors. Token paths follow main/checkbox/color/{state}/{role} convention.

StateRoleTokenDEFAULTPRESSEDDISABLEDERROR
UncheckedBorderunselected/border#D7E0EF#1972F9#D7E0EF#D81E1E
UncheckedContainer bgunselected/bg#EBF2FF
CheckedContainer bgselected/bg#1972F9#0F57C8#9BC5FD#D81E1E
CheckedCheckmarkselected/icon-check#FFFFFF#FFFFFF#FFFFFF#FFFFFF
IndeterminateContainer bgindeterminate/bg#1972F9
IndeterminateDash iconindeterminate/icon#FFFFFF
FocusedBorderfocused/border#1972F9 (all isSelected values)
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

// Or in Package.swift:
.package(
    url: "https://github.com/AY-Org/eb-ds-ios",
    from: "2.0.0"
)

Android — Gradle (Kotlin DSL)

// build.gradle.kts (app)
dependencies {
    implementation("com.eastblue.ds:checkbox:2.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.checkbox.*  // Compose

Package not yet published. These are the planned distribution paths. API shape is final — native implementation is pending.

Property Mapping
Figma PropertySwiftUI ParamCompose ParamNotes
isSelectedisOn: Binding<Bool>checked: Booleantrue/false
isSelected=indeterminatetoggleIndeterminateTriStateCheckboxPartial selection
Size.controlSize()size=EBCheckboxSize.*Small 16px, Medium 20px, Large 24px
State=Disabled.disabled(true)enabled=false40% opacity
State=PressedinteractionSourceTouch feedback
State=Focused.focused()interactionSourceKeyboard/switch nav
State=Error.ebError(true)isError=trueForm validation
SwiftUI
ios/Components/Checkbox/EBCheckbox.swift
Jetpack Compose
android/components/checkbox/EBCheckbox.kt
Usage Snippets Planned API
Unchecked
// Unchecked (default state)
EBCheckbox(isOn: $isSelected)
    .controlSize(.regular)

// Small size
EBCheckbox(isOn: $isSelected)
    .controlSize(.mini)
// Unchecked (default state)
EBCheckbox(
    checked = false,
    onCheckedChange = { isSelected = it },
    size = EBCheckboxSize.Medium
)

// Small size
EBCheckbox(
    checked = false,
    onCheckedChange = { isSelected = it },
    size = EBCheckboxSize.Small
)
Checked
// Checked (selected state)
EBCheckbox(isOn: .constant(true))
    .controlSize(.regular)

// Bound to state
@State private var isChecked = true
EBCheckbox(isOn: $isChecked)
    .controlSize(.large)
// Checked (selected state)
EBCheckbox(
    checked = true,
    onCheckedChange = { isSelected = it },
    size = EBCheckboxSize.Medium
)

// Bound to state
var isChecked by remember { mutableStateOf(true) }
EBCheckbox(
    checked = isChecked,
    onCheckedChange = { isChecked = it }
)
Indeterminate
// Indeterminate (partial selection)
EBCheckbox(isOn: $isSelected)
    .controlSize(.regular)
    .toggleIndeterminate(true)
// Indeterminate (partial selection)
TriStateCheckbox(
    state = ToggleableState.Indeterminate,
    onClick = { /* cycle state */ },
    modifier = Modifier.size(EBCheckboxSize.Medium)
)
Disabled
// Disabled unchecked
EBCheckbox(isOn: $isSelected)
    .controlSize(.regular)
    .disabled(true)

// Disabled checked
EBCheckbox(isOn: .constant(true))
    .controlSize(.regular)
    .disabled(true)
// Disabled unchecked
EBCheckbox(
    checked = false,
    onCheckedChange = {},
    enabled = false,
    size = EBCheckboxSize.Medium
)

// Disabled checked
EBCheckbox(
    checked = true,
    onCheckedChange = {},
    enabled = false,
    size = EBCheckboxSize.Medium
)
Error
// Error state (form validation)
EBCheckbox(isOn: $isSelected)
    .controlSize(.regular)
    .ebError(true)
// Error state (form validation)
EBCheckbox(
    checked = false,
    onCheckedChange = { isSelected = it },
    isError = true,
    size = EBCheckboxSize.Medium
)
Accessibility
RequirementiOSAndroid
Minimum touch target44 x 44 pt48 x 48 dp
Accessibility label.accessibilityLabel("Accept terms")semantics { contentDescription="Accept terms" }
Checked state announcementVoiceOver reads "checked" / "unchecked" automatically via ToggleTalkBack reads state automatically via Checkbox semantics
IndeterminatetoggleIndeterminate reads "mixed"TriStateCheckbox reads "partially checked"

Component is icon-only. Wrapping containers must provide an accessible label. Use padding to meet minimum touch target sizes for the smaller variants.

Usage Guidelines

Do

Pair with a visible label adjacent to the checkbox. Checkboxes must always have associated text.

Don't

Use for a single binary toggle — use Switch/Toggle instead. Checkboxes are for multi-select scenarios.

Do

Use for multi-select scenarios — forms, filter lists, settings, and select-all patterns.

Don't

Use a standalone Checkbox without an accessible label. Pair with CheckboxItem or an adjacent text label for form use.

Do

Expand touch area via padding when using Small (16px) size — minimum touch target is 44pt / 48dp.

Don't

Omit an accessible label. Use .accessibilityLabel / contentDescription when no visible label is present.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadyRoot frame named container. Simple, semantic hierarchy. No generic layer names.
C2Variant & Property NamingReadyisSelected now uses true/false — corrected in Figma. Maps directly to Swift Bool / Kotlin Boolean. indeterminate property is a C5 concern (missing state variant).
C3Token CoverageReadyAll states have defined color values. Default, Pressed, Disabled, Error, Focused, and Indeterminate containers use distinct fills/strokes. Separable icon-check and icon-indeterminate layers present.
C4Native MappabilityReadyMaps to Toggle(.checkbox) / Checkbox. Indeterminate maps to TriStateCheckbox. Label pairing via CheckboxItem compound component.
C5Interaction State CoverageReadyAll 5 interaction states defined (Default, Pressed, Focused, Disabled, Error) × isSelected (true/false) × 3 sizes. Indeterminate added as isSelected=indeterminate with icon-indeterminate dash layer. 33 total variants.
C6Asset & Icon QualityReadyCheckmark rebuilt as a separable icon-check child vector layer inside each checked container. Can be tinted via selected/icon-check token and swapped natively.
C7Code Connect LinkabilityNeeds RefinementAll structural blockers resolved (C2, C5, C6). Ready for CLI mapping registration. No mappings registered yet.
Code Connect
AspectStatusNotes
Property namingReadyisSelected=true/false — maps directly to Swift Bool and Kotlin Boolean
Icon/asset qualityReadyicon-check is now a named, separable child layer — can be mapped to a native icon slot via Code Connect
State coverageReadyAll interaction states defined — Default, Pressed, Focused, Disabled, Error, plus Indeterminate
Usage descriptionsReadyAll 33 variants have usage descriptions attached in Figma
Native component filePendingEBCheckbox.swift / EBCheckbox.kt not yet created
Variants Inventory (33 total)

5 State × 3 isSelected × 3 Size=45 theoretical. isSelected=indeterminate only ships State=Default, so actual count is (5 × 2 × 3) + (1 × 1 × 3)=33 variants.

isSelectedStatesSizesCount
falseDefault, Pressed, Focused, Disabled, ErrorSmall, Medium, Large15
trueDefault, Pressed, Focused, Disabled, ErrorSmall, Medium, Large15
indeterminateDefault onlySmall, Medium, Large3
View full State × isSelected × Size breakdown (33 rows)
isSelectedStateSizeNode ID
falseDefaultSmall17143:2465
falsePressedSmall17733:968
falseFocusedSmall17733:971
falseDisabledSmall17733:974
falseErrorSmall17733:977
trueDefaultSmall17143:2468
truePressedSmall17733:980
trueFocusedSmall17733:984
trueDisabledSmall17733:988
trueErrorSmall17733:992
falseDefaultMedium17143:2471
falsePressedMedium17733:996
falseFocusedMedium17733:998
falseDisabledMedium17733:1000
falseErrorMedium17733:1002
trueDefaultMedium17143:2473
truePressedMedium17733:1004
trueFocusedMedium17733:1008
trueDisabledMedium17733:1012
trueErrorMedium17733:1016
falseDefaultLarge17143:2476
falsePressedLarge17733:1020
falseFocusedLarge17733:1022
falseDisabledLarge17733:1024
falseErrorLarge17733:1026
trueDefaultLarge17143:2478
truePressedLarge17733:1028
trueFocusedLarge17733:1032
trueDisabledLarge17733:1036
trueErrorLarge17733:1040
indeterminateDefaultSmall17733:1044
indeterminateDefaultMedium17733:1048
indeterminateDefaultLarge17733:1052
1.5.0 — March 2026 Minor
Compound Component + Cleanup · node 17734:161220
CheckboxItem compound component created — 4 variants: isSelected (true/false) × Size (Small/Medium). Each contains a real Checkbox instance + Label (Proxima Soft Bold, 14px/18px) + Description (BarkAda Medium, 12px). Wraps atomic Checkbox with label pairing for accessible form use. Created
New Component
Variant property order reordered — Naming changed from isSelected=X, Size=Y, State=Z to State=X, isSelected=Y, Size=Z. State is now the first property axis in the component set. Section renamed from "Claude Testing" to "Checkbox". Updated
Cleanup
1.4.0 — March 2026 Minor
C5 Fix · node 17143:2464
All interaction states added — Added 27 new variants covering State (Pressed/Focused/Disabled/Error) × isSelected (true/false) × Size (Small/Medium/Large). Variants 6 → 33. Colors: Pressed uses light blue fill + blue border (unchecked) / #0F57C8 (checked); Focused uses blue border; Disabled uses 40% opacity; Error uses red border / #D81E1E fill. Fixed
C5 Resolved
Indeterminate state added — Added isSelected=indeterminate variants for Small, Medium, and Large. Each contains a blue container frame with a named icon-indeterminate horizontal dash child layer. Maps to ToggleableState.Indeterminate (Android) and toggleIndeterminate (iOS). Fixed
C5 Indeterminate
1.3.0 — March 2026 Minor
C6 Fix · node 17143:2464
Checkmark rebuilt as separable vector layer — Deleted the flattened boolean-op containers from all 3 isSelected=true variants. Created clean blue container frames (4px radius) with a named icon-check child vector inside each. Engineers can now tint via selected/icon-check token and map to a native icon slot. Nodes: 17721:962 (Small), 17721:963 (Medium), 17721:964 (Large). Fixed
C6 Resolved
1.2.0 — March 2026 Minor
C2 Fix · node 17143:2464
Boolean property renamed in Figma — All 6 variants renamed from isSelected=Yes/No to isSelected=true/false. Now maps directly to Swift Bool and Kotlin Boolean for Code Connect. C2 criterion resolved. Fixed
C2 Resolved
1.1.0 — March 2026 Minor
Assessment Rebuild · node 17143:2464
Full 4-tab assessment built — Overview, Style, Code, and Changelog tabs. Interactive live preview with size and state controls. Spec cards for Unchecked and Checked appearances. Full criteria scorecard and Code Connect readiness table. Updated
Documentation
Property name corrected — Existing assessment incorrectly referenced isChecked. Figma metadata confirms the property is isSelected. All documentation updated. Fixed
C2 Note
1.0.0 — March 2026 Initial
Initial Assessment · node 17143:2464
Component assessed — 6 variants documented across isSelected (Yes/No) x Size (Small 16px / Medium 20px / Large 24px). Token audit found 7 variables defined. Documented
Initial
Flattened checkmark icon — Checked containers had no child layers. The white checkmark was a boolean operation baked into the container frame. Cannot be extracted, tinted, or swapped as a component instance. Hard C6 blocker. Fixed in v1.3.0
C6 Resolved
Missing interaction states — Only checked/unchecked defined. No disabled, pressed, focused, indeterminate, or error state variants. Checkboxes require all of these for production form use. Fixed in v1.4.0
C5 Resolved
Boolean property uses Yes/NoisSelected=Yes/No instead of true/false. Incompatible with Swift Bool and Kotlin Boolean for Code Connect mapping. Fixed in v1.2.0
C2 Resolved
Code Connect mappings — Usage descriptions attached per variant. Was blocked by C2 and C6 (both resolved). Pending C5 (missing states) before complete CLI mapping. No CLI mappings registered yet. Open
C7 Open
Dropdown FixNeeds RefinementComponent link

A generic dropdown component with a select-style trigger and an overlay item list. 8 variants across variant (Text/Error/Amount/Mobile) × type (Collapsed/Expanded). Text and Error variants use a standard label + select field trigger. Amount adds a peso sign prefix. Mobile bundles a country code selector with a phone number input field. Optional subtext slot for helper/error messages.

Fix required before handoff
Missing disabled and pressed states (C5). DropdownItem selected uses yes/no instead of true/false (C2). Amount variant Peso Sign uses BOOLEAN_OPERATION (C6).
In Context

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

Select Option
Live Preview

Toggle variant and type to see the dropdown update in real time.

Properties
variant
type
DS Health
Reusable
Partial
Text and Error variants are generic dropdown patterns usable across many flows. Amount (peso-specific) and Mobile (phone input) variants are product-specific to GCash and limit cross-context reuse.
Self-contained
Pass
Bundles trigger field, chevron affordance, dropdown list overlay with shadow, and subtext slot. All visual states (collapsed/expanded/error) are self-contained with proper token bindings.
Consistent
Partial
DropdownItem selected uses yes/no string instead of true/false boolean (C2). Property type is a generic name — could conflict with platform keywords. Layer dropdowncontainer inconsistent with kebab-case convention.
Composable
Partial
Text/Error/Amount variants compose well with form layouts. Mobile variant bundles too many concerns (dropdown + phone input) — should be separated into discrete components. Trigger field is not a swappable slot.
Behavior
StateiOSAndroidFigma PropertyNotes
Collapsed (Default)YesYestype=CollapsedGray #D7E0EF border, white bg. Chevron down.
Expanded (Active)YesYestype=ExpandedBlue #005CE5 border, chevron up. Dropdown list overlay with shadow.
ErrorYesYesvariant=ErrorRed border — weak #F4C7C9 (collapsed), strong #D61B2C (expanded).
DisabledNoNoNot defined. Required for form accessibility.
PressedNoNoNot defined. Touch feedback expected on mobile.
Open Issues
  • DropdownItem selected property uses yes/no string — should be true/false for Swift Bool / Kotlin Boolean mapping C2
  • No disabled state — form dropdowns must support non-interactive state for accessibility and conditional form flows C5
  • No pressed state — touch feedback expected on mobile platforms (iOS highlight, Android ripple) C5
  • Amount variant Peso Sign uses shape_full BOOLEAN_OPERATION — not a clean vector path C6
  • Code Connect CLI mappings not registered C7
Design Recommendations
  • Add a Disabled state to the variant matrix — required for form accessibility and conditional logic Suggested
  • Rename type property to isExpanded (true/false) — avoids platform keyword conflicts and aligns with boolean convention Suggested
  • Rename DropdownItem selected from yes/no to true/false for direct boolean mapping Suggested
  • Consider extracting Mobile variant into a separate CountryCodeDropdown component — it bundles too many concerns (dropdown + phone input) for a generic dropdown Suggested
  • Add a selected visual state for DropdownItem (checkmark or background highlight) to indicate current selection Suggested
Variants

8 variants across 2 axes: variant (Text/Error/Amount/Mobile) × type (Collapsed/Expanded). Text and Error share the same trigger structure. Amount adds a peso sign. Mobile adds a label row with info icon, plus a phone input field.

Text

Default text dropdown. Label header, select trigger with placeholder text and chevron, optional subtext. Used for general-purpose list selection.

Properties
variantText
type
Error

Error state dropdown with red border. Collapsed uses weak border (#F4C7C9), expanded uses strong border (#D61B2C). Subtext turns red for error messaging.

Properties
variantError
type
Amount

Amount selection with peso sign prefix. Same trigger structure as Text but with a currency indicator for monetary value selection.

Properties
variantAmount
type
Mobile

Country code dropdown with phone number input. Bundles a label row (with info icon), a select field for country code, and a Labeled Field for phone number entry. Product-specific to GCash mobile number flows.

Properties
variantMobile
type
Colors by State

Trigger field and dropdown list colors. Border color is the primary state indicator. Error variant uses distinct border tokens.

RoleTokenDEFAULTACTIVEERROR (collapsed)ERROR (expanded)
Trigger borderselected-field/color/{state}/border#D7E0EF#005CE5#F4C7C9#D61B2C
Trigger bgselected-field/color/{state}/bg#FFFFFF#FFFFFF#FFFFFF#FFFFFF
Placeholderselected-field/color/{state}/placeholder#90A8D0#90A8D0#90A8D0#90A8D0
Chevron iconselected-field/color/{state}/icon#005CE5#005CE5#005CE5#005CE5
Peso sign (Amount)selected-field/color/{state}/icon-currency#183462#183462
Header labelformgroup-header/color/label#0A2757#0A2757#0A2757#0A2757
Item labeldropdown-item/color/default/label#0A2757#0A2757
Item borderdropdown-item/color/default/border#E5EBF4#E5EBF4
Dropdown bgbg/color-bg-main#FFFFFF#FFFFFF
Subtexttext/color-text-weak#445C85#445C85
Error subtextborder/color-border-destructive#D61B2C#D61B2C
Layout
PropertyValue
Trigger height46px
Corner radius6px (radius-2)
Trigger padding6px top, 8px bottom, 12px horizontal
Chevron size32 × 32
Peso sign size (Amount)15 × 15
Item padding16px vertical, 12px left, 16px right
Dropdown corner radius6px
Dropdown shadow0 6px 12px rgba(2,14,34,0.16)
Header padding bottom8px
Typography
LayerText StyleFontSizeTrackingLine-height
Header labelPrimary/Label/Light/SmallProxima Soft Semibold14px0.25px14px
Trigger placeholderPrimary/Label/Light/SmallProxima Soft Semibold14px0.25px14px
Dropdown itemPrimary/Label/Light/LargeProxima Soft Semibold18px0.25px18px
SubtextBarkAda Semibold12px0px18px
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android — Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:dropdown:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.dropdown.*  // Compose

Package not yet published. These are the planned distribution paths.

Property Mapping
Figma PropertySwiftUI ParamCompose ParamNotes
variant=TextEBDropdown(label:items:)EBDropdown(label, items)Default text selection
variant=Error.ebError(true)isError=trueValidation failed state
variant=Amount.ebStyle(.amount)style=EBDropdownStyle.AmountShows peso sign prefix
variant=Mobile.ebStyle(.mobile)style=EBDropdownStyle.MobileCountry code + phone input
type=CollapsedDefault closed state (managed internally)
type=ExpandedisPresented: Binding<Bool>expanded: BooleanDropdown list visible
subtext (boolean)helperText: String?helperText: String?Optional helper/error text
SwiftUI
ios/Components/Dropdown/EBDropdown.swift
Jetpack Compose
android/components/dropdown/EBDropdown.kt
Usage Snippets Planned API
Text (Default)
EBDropdown("Category", selection: $category) {
    ForEach(categories) { item in
        Text(item.name)
    }
}
EBDropdown(
    label = "Category",
    items = categories,
    selectedItem = selected,
    onItemSelected = { selected = it }
)
Error
EBDropdown("Category", selection: $category) {
    ForEach(categories) { item in
        Text(item.name)
    }
}
.ebError(true)
.ebHelperText("Please select a category")
EBDropdown(
    label = "Category",
    items = categories,
    selectedItem = selected,
    onItemSelected = { selected = it },
    isError = true,
    helperText = "Please select a category"
)
Amount
EBDropdown("Amount", selection: $amount) {
    ForEach(amounts) { item in
        Text(item.formatted)
    }
}
.ebStyle(.amount)
EBDropdown(
    label = "Amount",
    items = amounts,
    selectedItem = selected,
    onItemSelected = { selected = it },
    style = EBDropdownStyle.Amount
)
Accessibility
RequirementiOSAndroid
Minimum touch target44 × 44 pt48 × 48 dp
Accessibility label.accessibilityLabel("Select category")contentDescription
Role.accessibilityAddTraits(.isButton)semantics { role=Role.DropdownList }
Expanded stateVoiceOver: "collapsed" / "expanded"TalkBack: announce expansion state
Item selection.accessibilityValue(selectedItem)semantics { stateDescription }
Usage Guidelines

Do

Use Dropdown for selecting from a predefined list of options. Label the trigger clearly so users know what they're selecting.

Don't

Use Dropdown for free-text entry — use Input Field instead. Dropdown is for constrained selection only.

Do

Show error state with helper text below the field explaining the validation issue.

Don't

Use the Mobile variant for generic dropdown needs — it bundles phone-specific UI that adds complexity without value.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadySemantic names: label, container, text-container, peso-sign, Chevron Up/Down. Minor: dropdowncontainer missing separator.
C2Variant & Property NamingPartialDropdownItem selected uses yes/no instead of true/false. type is a generic property name.
C3Token CoverageReadyAll colors bound to design tokens. Space, radius, typography, and elevation tokens all present.
C4Native MappabilityPartialText/Error/Amount map to Menu (iOS) / ExposedDropdownMenuBox (Android). Mobile variant needs custom composition.
C5Interaction State CoverageNeeds FixMissing disabled and pressed states. Only Collapsed, Expanded, and Error defined.
C6Asset & Icon QualityPartialChevrons are vector instances. Amount variant Peso Sign uses BOOLEAN_OPERATION (shape_full).
C7Code Connect LinkabilityPendingNo CLI mappings registered yet.
Code Connect
AspectStatusNotes
Property namingPartialDropdownItem selected needs boolean rename; type is generic
Asset qualityPartialPeso Sign BOOLEAN_OPERATION in Amount variant
State coverageBlockedMissing disabled/pressed states blocks complete mapping
Native component filePendingEBDropdown.swift / EBDropdown.kt not yet created
Variants Inventory (8 total)

4 variant values × 2 type values (Collapsed/Expanded). subtext boolean toggleable on all variants.

varianttypeNode ID
TextCollapsed23:199481
TextExpanded23:199486
ErrorCollapsed883:30631
ErrorExpanded883:30686
AmountCollapsed23:199491
AmountExpanded23:199496
MobileCollapsed23:199501
MobileExpanded6388:4818
1.0.0 — April 2026 Initial
Initial Assessment · node 23:199480
Component assessed — 8 variants documented across variant (Text/Error/Amount/Mobile) × type (Collapsed/Expanded). Generic dropdown with trigger field, chevron affordance, and overlay item list. Documented
Initial
DropdownItem selected uses yes/noselected=yes/no instead of true/false. Incompatible with Swift Bool and Kotlin Boolean for Code Connect mapping. Open
C2 Open
Missing disabled and pressed states — Only Collapsed, Expanded, and Error states defined. No disabled state for non-interactive forms, no pressed state for touch feedback. Open
C5 Open
Amount variant Peso Sign uses BOOLEAN_OPERATIONshape_full is a BOOLEAN_OPERATION, not a clean vector path. May render inconsistently on native platforms. Open
C6 Open
Code Connect mappings — No CLI mappings registered yet. Open
C7 Open
Input Field FixNeeds RefinementComponent link

A basic text input field with border stroke and placeholder text. 8 variants across State (Default/Active/Error/Disabled) × isFilled (Yes/No). Part of the Form Elements group — serves as the base primitive for Labeled Field, Select Field, and Recipient Field.

Fix required before handoff
isFilled uses Yes/No instead of true/false (C2). Property naming blocks direct Swift Bool / Kotlin Boolean mapping.
In Context

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

Live Preview

Toggle state and fill to see the input field update in real time.

Properties
State
isFilled
DS Health
Reusable
Partial
Works across any text input context. Single-line only — no multi-line or textarea variant. No size variants (fixed 46px height).
Self-contained
Pass
Carries its own border, fill, and text styles per state. All 4 interaction states defined. Disabled state has distinct background.
Consistent
Pass
C2 resolved — isFilled now uses true/false. C1 resolved — text layer renamed from #text-label to #label, consistent with sibling fields.
Composable
Pass
Serves as the base primitive for Labeled Field, Select Field, and Recipient Field. Nests cleanly in form layouts.
Behavior
StateiOSAndroidFigma PropertyNotes
DefaultYesYesState=DefaultGray #D7E0EF border, white bg.
Active (Focused)YesYesState=ActiveBlue #005CE5 border.
ErrorYesYesState=ErrorRed #D61B2C border.
DisabledYesYesState=Disabled#EEF2F9 bg, border hidden.
Resolved Issues
  • isFilled property renamed from Yes/No to true/false — now maps directly to Swift Bool / Kotlin BooleanC2 Fixed
  • Text layer renamed from #text-label to #label — now consistent with sibling fields C1 Fixed
Open Issues
  • Code Connect CLI mappings not registered C7
Design Recommendations
  • Continue standardizing text layer naming across all Form Elements — Input Field now uses #label; verify remaining fields follow suit Suggested
  • Consider adding a helperText slot below the field for validation messages and hints — currently handled externally Suggested
  • Add a leadingIcon and trailingIcon boolean slot to Input Field (Labeled Field already has these) for search inputs and clear buttons Suggested
States

8 variants across 2 axes: State (Default/Active/Error/Disabled) × isFilled (Yes/No). All share the same 366×46px container with 6px corner radius.

Default

Idle state with gray border. Text color depends on whether the field has a value.

Properties
StateDefault
isFilled
Active (Focused)

Focused state with blue border indicating active input.

Properties
StateActive
isFilled
Error

Validation error state with red border.

Properties
StateError
isFilled
Disabled

Non-interactive state with gray background and hidden border.

Properties
StateDisabled
isFilled
Colors by State

All states share the same container structure. Border color is the primary state indicator.

RoleTokenDEFAULTACTIVEERRORDISABLED
Borderfield/border#D7E0EF#005CE5#D61B2Chidden
Backgroundfield/bg#FFFFFF#FFFFFF#FFFFFF#EEF2F9
Text (filled)field/text/filled#0A2757#0A2757#0A2757#90A8D0
Text (empty)field/text/placeholder#90A8D0#90A8D0#90A8D0#C2CFE5
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android — Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:form-elements:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.form.*  // Compose

Package not yet published. These are the planned distribution paths.

Property Mapping
Figma PropertySwiftUI ParamCompose ParamNotes
isFilled (Yes/No)text: Binding<String>value: StringDerived from text content
State=DefaultDefault idle state
State=Active.focused()interactionSourceKeyboard active
State=Error.ebError(true)isError=trueValidation failed
State=Disabled.disabled(true)enabled=falseNon-interactive
SwiftUI
ios/Components/FormElements/EBInputField.swift
Jetpack Compose
android/components/form/EBInputField.kt
Usage Snippets Planned API
Default
EBInputField("Placeholder", text: $value)
EBInputField(
    value = text,
    onValueChange = { text = it },
    placeholder = "Placeholder"
)
Error
EBInputField("Placeholder", text: $value)
    .ebError(true)
EBInputField(
    value = text,
    onValueChange = { text = it },
    placeholder = "Placeholder",
    isError = true
)
Disabled
EBInputField("Placeholder", text: $value)
    .disabled(true)
EBInputField(
    value = text,
    onValueChange = { text = it },
    placeholder = "Placeholder",
    enabled = false
)
Accessibility
RequirementiOSAndroid
Minimum touch target44 x 44 pt48 x 48 dp
Accessibility label.accessibilityLabel("Input")contentDescription
Error announcementVoiceOver reads error via .accessibilityValueTalkBack reads error via semantics { error() }
Usage Guidelines

Do

Pair with a visible label above or inside the field. Use placeholder text to hint at expected input format.

Don't

Use placeholder text as the only label — it disappears on focus and fails accessibility.

Do

Show error state with a helper text message below the field explaining what needs to be corrected.

Don't

Use Input Field for selection — use Select Field instead. Input Field is for free-text entry only.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadyLayer renamed to #label, now consistent with sibling fields.
C2Variant & Property NamingReadyisFilled now uses true/false. Boolean naming resolved.
C3Token CoveragePartialColors appear correct but token binding not verified.
C4Native MappabilityReadyMaps to TextField (SwiftUI) / OutlinedTextField (Compose).
C5Interaction State CoverageReadyAll 4 states defined: Default, Active, Error, Disabled.
C6Asset & Icon QualityReadyNo icons in base Input Field — text only.
C7Code Connect LinkabilityPendingNo CLI mappings registered yet.
Code Connect
AspectStatusNotes
Property namingReadyisFilled uses true/false — maps directly to native booleans
State coverageReadyAll 4 states defined
Native component filePendingEBInputField.swift / EBInputField.kt not yet created
Variants Inventory (8 total)

4 State values × 2 isFilled values.

StateisFilledNode ID
Defaulttrue17758:3688
Defaultfalse17758:3691
Activetrue17758:3694
Activefalse17758:3697
Errortrue17758:3700
Errorfalse17758:3703
Disabledtrue17758:3706
Disabledfalse17758:3709
1.1.0 — March 2026 Minor
C1 + C2 Fixes · Layer naming and boolean naming resolved
isFilled property renamedisFilled=Yes/No updated to isFilled=true/false in Figma. Now maps directly to Swift Bool and Kotlin Boolean for Code Connect. Fixed
C2 Resolved
Text layer renamed#text-label renamed to #label in Figma. Now consistent with sibling fields (Labeled Field, Select Field, Recipient Field). Fixed
C1 Resolved
1.0.0 — March 2026 Initial
Initial Assessment · node 17758:3687
Component assessed — 8 variants documented across State (Default/Active/Error/Disabled) × isFilled (Yes/No). Part of Form Elements group. Documented
Initial
Boolean property uses Yes/NoisFilled=Yes/No instead of true/false. Incompatible with Swift Bool and Kotlin Boolean for Code Connect mapping. Open
C2 Open
Code Connect mappings — No CLI mappings registered yet. Blocked by C2 (property naming). Open
C7 Open
Labeled Field FixNeeds RefinementComponent link

An enhanced form field with leading icon, label + value text container, an XSmall action button, and trailing icon. 8 variants across State (Default/Active/Error/Disabled) × isFilled (true/false). Part of the Form Elements group — extends the base Input Field pattern with icon slots and an embedded action button.

Fix required before handoff
Trailing icon uses rectangle placeholder instead of swappable icon instance (C6). Code Connect mappings not yet registered (C7).
In Context

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

Live Preview

Toggle state and fill to see the labeled field update in real time.

Properties
State
isFilled
DS Health
Reusable
Partial
Works across form contexts requiring labeled inputs with icons. Single-line only — no multi-line variant. Fixed 46px height with no size options.
Self-contained
Pass
Carries its own border, fill, icon slots, and text styles per state. All 4 interaction states defined. Disabled state has distinct background and hidden border.
Consistent
Partial
Boolean naming and casing fixed (C2 resolved): isFilled now uses true/false and property renamed to State. Action button layer renamed to action-button (C1 resolved).
Composable
Partial
Nests in form layouts. However, trailing-icon uses a rectangle placeholder instead of a swappable icon instance (C6), limiting icon customization at the consumer level.
Behavior
StateiOSAndroidFigma PropertyNotes
DefaultYesYesState=DefaultGray #D7E0EF border, white bg.
Active (Focused)YesYesState=ActiveBlue #005CE5 border.
ErrorYesYesState=ErrorRed #D61B2C border.
DisabledYesYesState=Disabled#EEF2F9 bg, border hidden.
Resolved Issues
  • isFilled renamed from Yes/No to true/false — now maps directly to Swift Bool / Kotlin BooleanC2 Fixed
  • Property state renamed to State (capitalized) — consistent with sibling Form Elements fields C2 Fixed
  • Button - XSmall layer renamed to action-button — now a semantic slot name for flexible consumer customization C1 Fixed
  • Trailing icon uses shared Placeholder component instance — swappable by design. Internal RECTANGLE is the default visual, replaced by designers when consuming the component C6 Closed
Open Issues
  • Code Connect CLI mappings not registered C7
Design Recommendations
  • Replace Button - XSmall hardcoded instance with a named action slot so consumers can swap in different button variants or remove the action entirely Suggested
  • Replace icon-placeholder RECTANGLE in trailing-icon with a swappable icon instance — enables consumers to override the trailing icon via Figma instance swap Suggested
  • Consider adding a helperText slot below the field for validation messages — currently handled externally Suggested
States

8 variants across 2 axes: State (Default/Active/Error/Disabled) × isFilled (true/false). All share the same 46px height container with 6px corner radius. Each variant includes leading icon, text container (label + value), XSmall action button, and trailing icon.

Default

Idle state with gray border. Leading/trailing icon placeholders, label + value text container, and XSmall action button.

Properties
StateDefault
isFilled
Active (Focused)

Focused state with blue border indicating active input.

Properties
StateActive
isFilled
Error

Validation error state with red border.

Properties
StateError
isFilled
Disabled

Non-interactive state with gray background and hidden border.

Properties
StateDisabled
isFilled
Colors by State

All states share the same container structure. Border color is the primary state indicator. Text colors depend on isFilled (true/false).

RoleTokenDEFAULTACTIVEERRORDISABLED
Borderfield/border#D7E0EF#005CE5#D61B2Chidden
Backgroundfield/bg#FFFFFF#FFFFFF#FFFFFF#EEF2F9
Label (filled)field/text/label#0A2757#0A2757#0A2757#90A8D0
Value (filled)field/text/value#0A2757#0A2757#0A2757#C2CFE5
Value (empty)field/text/placeholder#90A8D0#90A8D0#90A8D0#C2CFE5
Layout
Height46px
Corner radius6px
Leading icon24 × 24
Trailing icon24 × 24
Action button60 × 24 (radius 99)
Typography
FontProxima Soft
WeightSemibold (600)
Size14px
Letter spacing0.25
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android — Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:form-elements:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.form.*  // Compose

Package not yet published. These are the planned distribution paths.

Property Mapping
Figma PropertySwiftUI ParamCompose ParamNotes
isFilled (true/false)text: Binding<String>value: StringDerived from text content
State=DefaultDefault idle state
State=Active.focused()interactionSourceKeyboard active
State=Error.ebError(true)isError=trueValidation failed
State=Disabled.disabled(true)enabled=falseNon-interactive
#label (TEXT)label: Stringlabel: StringField label text
#value (TEXT)text: Binding<String>value: StringField value text
leading-iconleadingIcon: Image?leadingIcon: @Composable?24×24 icon slot
trailing-icontrailingIcon: Image?trailingIcon: @Composable?24×24 icon slot (C6: rectangle placeholder)
action-buttonaction: EBFieldAction?action: @Composable?Semantic slot name (C1 resolved)
SwiftUI
ios/Components/FormElements/EBLabeledField.swift
Jetpack Compose
android/components/form/EBLabeledField.kt
Usage Snippets Planned API
Default
EBLabeledField("Label", text: $value)
    .leadingIcon(Image("icon-placeholder"))
    .trailingIcon(Image("chevron-right"))
EBLabeledField(
    value = text,
    onValueChange = { text = it },
    label = "Label",
    leadingIcon = { Icon(Icons.Default.Placeholder, null) },
    trailingIcon = { Icon(Icons.Default.ChevronRight, null) }
)
Error
EBLabeledField("Label", text: $value)
    .leadingIcon(Image("icon-placeholder"))
    .ebError(true)
EBLabeledField(
    value = text,
    onValueChange = { text = it },
    label = "Label",
    leadingIcon = { Icon(Icons.Default.Placeholder, null) },
    isError = true
)
Disabled
EBLabeledField("Label", text: $value)
    .leadingIcon(Image("icon-placeholder"))
    .disabled(true)
EBLabeledField(
    value = text,
    onValueChange = { text = it },
    label = "Label",
    leadingIcon = { Icon(Icons.Default.Placeholder, null) },
    enabled = false
)
Accessibility
RequirementiOSAndroid
Minimum touch target44 x 44 pt48 x 48 dp
Accessibility label.accessibilityLabel("Label")contentDescription
Error announcementVoiceOver reads error via .accessibilityValueTalkBack reads error via semantics { error() }
Action button label.accessibilityLabel("Action") on buttoncontentDescription on button
Usage Guidelines

Do

Use Labeled Field when the input needs a persistent label above the value, a leading icon for context, and an optional action button.

Don't

Use Labeled Field for simple text entry — use Input Field instead. Labeled Field is for complex form rows with icon context.

Do

Provide meaningful icons in the leading and trailing slots — they help users identify the field purpose at a glance.

Don't

Leave the icon placeholders as-is in production — always swap in a contextual icon or hide the slot.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadyLayer renamed to action-button, now a semantic slot name.
C2Variant & Property NamingReadyisFilled uses true/false. Property renamed to State (capitalized). Both fixes confirmed in Figma.
C3Token CoveragePartialColors appear correct but token binding not verified.
C4Native MappabilityReadyMaps to custom EBLabeledField on both platforms.
C5Interaction State CoverageReadyAll 4 states defined: Default, Active, Error, Disabled.
C6Asset & Icon QualityPartialtrailing-icon uses icon-placeholder RECTANGLE — not a swappable icon instance.
C7Code Connect LinkabilityPendingNo CLI mappings registered yet.
Code Connect
AspectStatusNotes
Property namingReadyisFilled=true/false and State (capitalized) — C2 fixed in Figma, ready for mapping
State coverageReadyAll 4 states defined
Icon slotsPartialleading-icon uses Placeholder instance (OK). trailing-icon uses RECTANGLE (blocked).
Action slotReadyRenamed to action-button — semantic slot name, ready for Code Connect mapping
Native component filePendingEBLabeledField.swift / EBLabeledField.kt not yet created
Variants Inventory (8 total)

4 State values × 2 isFilled values.

StateisFilledNode ID
Defaulttrue17758:3714
Defaultfalse17758:3723
Activetrue17758:3732
Activefalse17758:3741
Errortrue17758:3750
Errorfalse17758:3759
Disabledtrue17758:3768
Disabledfalse17758:3777
1.2.0 — March 2026 Minor
C1 Figma Fix · node 17758:3713
Action button layer renamedButton - XSmall renamed to action-button. Now uses a semantic slot name, enabling flexible consumer customization and clean Code Connect mapping. Fixed
C1 Fixed
1.1.0 — March 2026 Minor
C2 Figma Fix · node 17758:3713
Boolean property renamedisFilled values changed from Yes/No to true/false. Now maps directly to Swift Bool and Kotlin Boolean for Code Connect. Fixed
C2 Fixed
Property casing correctedstate renamed to State (capitalized) to align with sibling Form Elements fields (Input Field, etc.). Fixed
C2 Fixed
1.0.0 — March 2026 Initial
Initial Assessment · node 17758:3713
Component assessed — 8 variants documented across State (Default/Active/Error/Disabled) × isFilled (true/false). Part of Form Elements group. Enhanced input with leading icon, label/value text, action button, and trailing icon. Documented
Initial
Boolean property uses Yes/NoisFilled=Yes/No instead of true/false. Incompatible with Swift Bool and Kotlin Boolean for Code Connect mapping. Open
C2 Open
Lowercase property namestate uses lowercase, inconsistent with other Form Elements using State (capitalized). Open
C2 Open
Hardcoded button instanceButton - XSmall is not a named action slot, limiting consumer customization. Open
C1 Open
Trailing icon uses RECTANGLEicon-placeholder in trailing-icon is a RECTANGLE, not a swappable icon instance. Open
C6 Open
Code Connect mappings — No CLI mappings registered yet. Blocked by C2 (property naming) and C6 (icon quality). Open
C7 Open
Menu Grid FixNeeds RefinementComponent link

Dashboard service grid — a 336px-wide layout container that arranges Service Item instances (icon + label) in a configurable grid. 20 variants combine Row (2/3/4/5) and Column (1/2/3/4/5). Used for service shortcuts on dashboard surfaces (Send Money, Pay Bills, Cash In, etc.).

Open issues remain
Variant property values use pseudo-numeric strings ("by 4") instead of integers (C2). Service Item only ships an active color set — no pressed/disabled tokens (C5). Code Connect mappings not yet registered (C7).
In Context

Contexts are illustrative. Final screens will reference actual GCash patterns. Menu Grid sits on the dashboard as the primary service shortcut surface — typically Row=2, Column=4 (8 services) on the home screen.

GCash
Live Preview

Toggle Row × Column to see the grid recalculate. Each cell is a Service Item (icon + label).

Properties
Row
Column
DS Health
Reusable
Pass
Used on dashboard surfaces and any screen needing a uniform service shortcut grid. 20 row/column combinations cover most layout needs from a single column list to a 5×5 grid.
Self-contained
Pass
Container carries its own background, padding (8h / 10t / 6b), gap (4×4), and bottom radius (6). All values bound to design tokens.
Consistent
Warn
Variant property values are pseudo-numeric strings ("by 4") instead of clean integer enums. Should be rows: 4 / columns: 4 for native parity. C2
Composable
Pass
Cleanly composes Service Item instances (node 17787:1700). Service Item is a separate canonical component — icon and label are easily overridable per cell.
Behavior
StateiOSAndroidFigma PropertyNotes
DefaultYesYesRow × ColumnActive state only — uses dashboard/service-item/color/active/{icon,label}
PressedN/AN/ANo pressed token defined for Service Item. Native may need to derive from icon brand color. C5
DisabledN/AN/ANo disabled token defined. C5

Menu Grid is a layout container; tap behavior lives on each Service Item child.

Open Issues
  • Variant property values use pseudo-numeric strings — Row="by 4", Column="by 4". Should be plain integers (rows: 4, columns: 4) for clean native enum/int mapping. C2
  • Service Item only defines an active color set. No pressed or disabled color tokens — engineers must invent these on native. C5
  • Code Connect CLI mappings not registered. C7
Design Recommendations
  • Replace the 20-variant matrix with a single component exposing two integer properties (rows, columns) — variant explosion is a Figma-only construct; native uses a lazy grid that takes any int. Could collapse to ~1–2 variants. Restructure
  • Add pressed and disabled color tokens to dashboard/service-item/color/* so all states are documented at the token layer, not improvised in code. Token gap
  • Consider documenting Service Item as its own first-class DS component — currently it's a child of Menu Grid but is reused independently across other surfaces. DS Hygiene
Variants

20 variants formed by Row (2/3/4/5) × Column (1/2/3/4/5). All share the same 336px container width, 4×4 gap, and 8/10/6 padding. Cell count=Row × Column.

Row 2 × Column 4 — 8 services (most common)

The default dashboard layout — 2 rows × 4 columns=8 services. Used on the home dashboard for primary service shortcuts.

Row 4 × Column 4 — 16 services

Expanded grid for "All Services" sheets — 4 rows × 4 columns=16 services. Used on category pages or full-list views.

Row 5 × Column 5 — 25 services (max)

Maximum density — 5 rows × 5 columns=25 services. Use sparingly; label legibility tightens at this density.

Colors by State

Service Item ships only the active color set. Pressed and disabled are not yet defined at the token layer — see C5 in the Open Issues.

RoleTokenValue
Container bgbg/color-bg-main#FFFFFF
Service icondashboard/service-item/color/active/icon#005CE5
Service labeldashboard/service-item/color/active/label#072592
Border (weak)border/color-border-weak#E5EBF4
Layout
PropertyTokenValue
Container width336px (fixed)
Padding (horizontal)space/space-88px
Padding (top)space/space-1010px
Padding (bottom)space/space-66px
Cell gap (row & col)space/space-44px
Bottom radiusradius/radius-26px
Service Item min-width64px
Service Item icon container40 × 40
Icon paddingspace/space-44px
Icon → label gapspace/space-66px
Typography (Service Item Label)
PropertyValue
DS text stylePrimary/Label/Fine
FontProxima Soft
Weight700 (Bold)
Size12px
Line height12px
Tracking+0.5
Alignmentcenter
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android — Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:menu-grid:1.0.0")
}
Property Mapping
Figma PropertySwiftUI ParamCompose ParamNotes
Row="by N"rows: Introws: IntNative takes a plain integer; Figma string parsed to N
Column="by N"columns: Intcolumns: IntSame — integer parameter
Service Item (instance)items: [EBServiceItem]items: List<EBServiceItem>Each cell is a Service Item with icon + label
SwiftUI
ios/Components/MenuGrid/EBMenuGrid.swift
Jetpack Compose
android/components/menugrid/EBMenuGrid.kt
Usage Snippets Planned API
// Default dashboard grid — 2 rows × 4 columns
EBMenuGrid(columns: 4, items: services)

// Service Item child
EBServiceItem(
    icon: Image("send-money"),
    label: "Send Money",
    action: { /* tap */ }
)
// Default dashboard grid — 2 rows × 4 columns
EBMenuGrid(
    columns = 4,
    items = services
)

// Service Item child
EBServiceItem(
    icon = painterResource(R.drawable.send_money),
    label = "Send Money",
    onClick = { /* tap */ }
)
Accessibility
RequirementiOSAndroid
Tap targetService Item is 64+ px wide × ~64 px tall — meets HIG 44pt minimumMeets Material 48dp minimum
Accessibility label.accessibilityLabel(label) on each itemcontentDescription=label
Grid semanticsContainer exposes grid traits via LazyVGridLazyVerticalGrid announces row/column position
Disabled stateCurrently undefined — needs token + .disabled(true) handlingCurrently undefined — needs token + enabled=false
Usage Guidelines

Do

Use Row=2 × Column=4 for the primary dashboard surface — 8 services is the established home pattern.

Don't

Use Row=5 × Column=5 unless density is essential — 25 cells reduces label legibility.

Do

Pair Menu Grid with a section heading or container card so users understand the grouping.

Don't

Mix icon styles within a single grid — keep all Service Item icons in the same vector style.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadySemantic names: Service Item, icon-container, Label. No Frame/Group debris.
C2Variant & Property NamingNeeds FixProperty values are pseudo-numeric strings ("by 4") instead of integers. Native takes Int.
C3Token CoverageReadyAll colors, spacing, radii, and typography bound to tokens.
C4Native MappabilityPartialMaps to LazyVGrid / LazyVerticalGrid. The 20-variant matrix is Figma-only — native uses one component with two int props.
C5Interaction State CoverageNeeds FixOnly active token defined. No pressed or disabled color tokens for Service Item.
C6Asset & Icon QualityReadyIcon container is a vector instance, token-bound color.
C7Code Connect LinkabilityPendingNo CLI mappings registered yet.
Variants Inventory (20 total)

4 Row × 5 Column=20 variants. All variants share the same 336px container, 4×4 cell gap, and Service Item child.

RowColumnsCell rangeCount
by 2by 1, 2, 3, 4, 52 – 105
by 3by 1, 2, 3, 4, 53 – 155
by 4by 1, 2, 3, 4, 54 – 205
by 5by 1, 2, 3, 4, 55 – 255
View full Row × Column breakdown (20 rows)
RowColumnCellsNode ID
by 2by 1218320:14413
by 2by 2418320:14407
by 2by 3618320:14398
by 2by 4818320:14371
by 2by 51018320:14383
by 3by 1318320:14423
by 3by 2618320:14416
by 3by 3918320:14563
by 3by 41218320:14550
by 3by 51518320:14534
by 4by 1418320:14503
by 4by 2818320:14477
by 4by 31218320:14448
by 4by 41618320:14333
by 4by 52018320:14350
by 5by 1518320:14497
by 5by 21018320:14486
by 5by 31518320:14461
by 5by 42018320:14427
by 5by 52518320:14508
1.0.0 — April 2026 Initial
Initial Assessment · node 18320:14332
Component assessed — 20 variants formed by Row × Column (Row 2/3/4/5, Column 1/2/3/4/5). Layout container of Service Item children. 336px fixed width. Documented
Initial
Variant property values are strings, not integersRow="by 4", Column="by 4". Native takes Int; the string prefix forces parsing. Open
C2 Open
Service Item missing pressed/disabled tokens — Only dashboard/service-item/color/active/{icon,label} defined. Other states must be improvised. Open
C5 Open
Code Connect mappings — No CLI mappings registered yet. Open
C7 Open
Recipient Field FixNeeds RefinementComponent link

A GCash-specific two-line input field for recipient/contact entry in money transfer flows (Send Money, Pay Bills, etc.). 8 variants across State (Default/Active/Error/Disabled) × isFilled (true/false). Features a small label on top, value/placeholder below, and two trailing action icons. Notably taller than other Form Elements at 56px (vs 46px for Input Field, Labeled Field, etc.) with 6px corner radius.

Fix required before handoff
Both trailing icons are non-swappable rectangles (C6). This blocks direct native property mapping for icon slots.
In Context

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

Send Money
Live Preview

Toggle state and fill to see the recipient field update in real time. Note the 56px height (taller than the standard 46px form field).

Properties
State
isFilled
DS Health
Reusable
Partial
Specific to recipient/contact entry in money transfer flows (Send Money, Pay Bills). Not a general-purpose field — the two-line layout with trailing icon pair is domain-specific.
Self-contained
Pass
Carries its own border, fill, label, and text styles per state. All 4 interaction states defined. 56px height, 6px corner radius. Disabled state has distinct background.
Consistent
Partial
isFilled now uses true/false (C2 fixed). Value layer renamed to #value (C1 fixed) — now consistent with sibling fields.
Composable
Warn
Trailing icons are icon-placeholder RECTANGLEs (C6) — not swappable icon instances. Cannot compose different icon actions (phonebook, scan QR) without editing the component.
Behavior
StateiOSAndroidFigma PropertyNotes
DefaultYesYesState=DefaultGray #D7E0EF border, white bg. Label + placeholder/value visible.
Active (Focused)YesYesState=ActiveBlue #005CE5 border.
ErrorYesYesState=ErrorRed #D61B2C border.
DisabledYesYesState=Disabled#EEF2F9 bg, border hidden. Muted label and text.
Resolved Issues
  • isFilled property renamed from Yes/No to true/false — now maps directly to Swift Bool / Kotlin BooleanC2 Fixed
  • Text layer renamed from #text-placeholder to #value — now consistent with sibling fields (Input Field, Labeled Field) C1 Fixed
  • Both trailing icons use shared Placeholder component instances — swappable by design. Internal RECTANGLE is the default visual, replaced by designers when consuming the component C6 Closed
Open Issues
  • Code Connect CLI mappings not registered C7
Design Recommendations
  • Replace icon-placeholder RECTANGLEs with swappable icon component instances — enables phonebook, scan QR, or other action icons without editing the base component Suggested
  • Document the 56px height rationale — clarify in DS guidelines why Recipient Field is taller than the standard 46px form fields (two-line layout: label + value) Suggested
  • Consider adding an errorMessage slot below the field for inline validation text, consistent with other form elements Suggested
States

8 variants across 2 axes: State (Default/Active/Error/Disabled) × isFilled (true/false). All share the same container with 6px corner radius. Height is 56px — taller than other Form Elements (46px) to accommodate the two-line label + value layout.

Default

Idle state with gray border. Two-line layout: small label above, value/placeholder below. Two trailing icon placeholders.

Properties
StateDefault
isFilled
Active (Focused)

Focused state with blue border indicating active input.

Properties
StateActive
isFilled
Error

Validation error state with red border.

Properties
StateError
isFilled
Disabled

Non-interactive state with gray background and hidden border. Muted label and text colors.

Properties
StateDisabled
isFilled
Colors by State

All states share the same two-line container (56px height, 6px radius). Border color is the primary state indicator. Two trailing icon placeholders use a fixed fill across all states.

RoleTokenDEFAULTACTIVEERRORDISABLED
Borderfield/border#D7E0EF#005CE5#D61B2Chidden
Backgroundfield/bg#FFFFFF#FFFFFF#FFFFFF#EEF2F9
Label textfield/label#0A2757#0A2757#0A2757#90A8D0
Value (filled)field/text/filled#0A2757#0A2757#0A2757#90A8D0
Value (empty)field/text/placeholder#90A8D0#90A8D0#90A8D0#C2CFE5
Icon placeholderfield/icon#C2C6CF#C2C6CF#C2C6CF#C2C6CF
Layout
Height56px (vs 46px standard)
Corner radius6px
Border1px stroke
Icon group68 × 32px
Icon size32 × 32px each
Icon radius41px (circular)
Typography
#label (top line)
FontProxima Soft Semibold
Size12px
Tracking0.5
#text-placeholder (bottom line)
FontProxima Soft Semibold
Size14px
Tracking0.25
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android — Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:form-elements:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.form.*  // Compose

Package not yet published. These are the planned distribution paths.

Property Mapping
Figma PropertySwiftUI ParamCompose ParamNotes
isFilled (true/false)text: Binding<String>value: StringDerived from text content
#labellabel: Stringlabel: StringSmall top label (12px)
#text-placeholderplaceholder: Stringplaceholder: StringValue text or placeholder (14px)
icon-group (2x icons)trailingIcons: [Image]trailingIcons: @ComposableTwo action icon slots
State=DefaultDefault idle state
State=Active.focused()interactionSourceKeyboard active
State=Error.ebError(true)isError=trueValidation failed
State=Disabled.disabled(true)enabled=falseNon-interactive
SwiftUI
ios/Components/FormElements/EBRecipientField.swift
Jetpack Compose
android/components/form/EBRecipientField.kt
Usage Snippets Planned API
Default
EBRecipientField(
    label: "Mobile Number",
    text: $recipientNumber,
    placeholder: "Enter number or name",
    trailingIcons: [
        Image(systemName: "person.crop.circle"),
        Image(systemName: "qrcode.viewfinder")
    ]
)
EBRecipientField(
    label = "Mobile Number",
    value = recipientNumber,
    onValueChange = { recipientNumber = it },
    placeholder = "Enter number or name",
    trailingIcons = {
        IconButton(onClick = onContactsClick) {
            Icon(Icons.Default.Person, "Contacts")
        }
        IconButton(onClick = onScanClick) {
            Icon(Icons.Default.QrCode, "Scan QR")
        }
    }
)
Error
EBRecipientField(
    label: "Mobile Number",
    text: $recipientNumber,
    placeholder: "Enter number or name"
)
.ebError(true)
EBRecipientField(
    label = "Mobile Number",
    value = recipientNumber,
    onValueChange = { recipientNumber = it },
    placeholder = "Enter number or name",
    isError = true
)
Disabled
EBRecipientField(
    label: "Mobile Number",
    text: $recipientNumber,
    placeholder: "Enter number or name"
)
.disabled(true)
EBRecipientField(
    label = "Mobile Number",
    value = recipientNumber,
    onValueChange = { recipientNumber = it },
    placeholder = "Enter number or name",
    enabled = false
)
Accessibility
RequirementiOSAndroid
Minimum touch target44 x 44 pt (56px field exceeds)48 x 48 dp (56px field exceeds)
Accessibility label.accessibilityLabel("Recipient")contentDescription
Error announcementVoiceOver reads error via .accessibilityValueTalkBack reads error via semantics { error() }
Trailing icon labels.accessibilityLabel("Contacts") per iconcontentDescription per icon button
Usage Guidelines

Do

Use Recipient Field for contact/number entry in money transfer flows (Send Money, Pay Bills, Buy Load). The two-line layout with trailing icons is purpose-built for this context.

Don't

Use Recipient Field for general text input — use Input Field or Labeled Field instead. The 56px height and icon slots add unnecessary weight for simple text entry.

Do

Provide meaningful trailing icons (e.g. contacts picker, QR scanner) that match the field's purpose. Both slots should have distinct actions.

Don't

Leave icon placeholders as-is in production — they are design placeholders only. Always replace with real icon instances before handoff.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadyLayer renamed to #value, now consistent with sibling fields.
C2Variant & Property NamingReadyisFilled=true/false — correctly uses native boolean values.
C3Token CoveragePartialColors appear correct but token binding not verified. Icon placeholders use hardcoded #C2C6CF.
C4Native MappabilityReadyMaps to custom EBRecipientField (SwiftUI + Compose). Two-line layout with trailing icon slots.
C5Interaction State CoverageReadyAll 4 states defined: Default, Active, Error, Disabled.
C6Asset & Icon QualityNeeds FixBoth trailing icons are icon-placeholder RECTANGLEs — not swappable icon instances.
C7Code Connect LinkabilityPendingNo CLI mappings registered yet.
Code Connect
AspectStatusNotes
Property namingReadyisFilled=true/false — boolean values map directly to native types
Layer namingBlocked#text-placeholder needs rename to #value
Icon slotsBlockedRECTANGLE placeholders — need swappable icon instances
State coverageReadyAll 4 states defined
Native component filePendingEBRecipientField.swift / EBRecipientField.kt not yet created
Variants Inventory (8 total)

4 State values × 2 isFilled values (true/false).

StateisFilledNode ID
Defaulttrue17758:3868
Defaultfalse17758:3875
Activetrue17758:3882
Activefalse17758:3889
Errortrue17758:3896
Errorfalse17758:3903
Disabledtrue17758:3910
Disabledfalse17758:3917
1.2.0 — March 2026 Fix
C1 Resolved · node 17758:3867
#text-placeholder renamed to #value — Value text layer renamed from #text-placeholder to #value. Now consistent with sibling fields (Input Field, Labeled Field) for direct native property mapping. C1 Fixed
C1 Resolved
1.1.0 — March 2026 Fix
C2 Resolved · node 17758:3867
isFilled renamed to true/false — Figma property isFilled updated from Yes/No to true/false. Now maps directly to Swift Bool and Kotlin Boolean for Code Connect. C2 Fixed
C2 Resolved
1.0.0 — March 2026 Initial
Initial Assessment · node 17758:3867
Component assessed — 8 variants documented across State (Default/Active/Error/Disabled) × isFilled (true/false). GCash-specific two-line recipient field with trailing action icons. 56px height (vs 46px standard). Documented
Initial
Boolean property uses Yes/NoisFilled=Yes/No instead of true/false. Incompatible with Swift Bool and Kotlin Boolean for Code Connect mapping. Fixed in 1.1.0
C2 Fixed
Layer naming inconsistency — Value text layer is #text-placeholder instead of #value used by other Form Elements. Fixed in 1.2.0
C1 Fixed
Non-swappable icon placeholders — Both trailing icons are icon-placeholder RECTANGLEs, not component instances. Open
C6 Open
Code Connect mappings — No CLI mappings registered yet. Blocked by C1 and C6 issues. Open
C7 Open
Select Field FixNeeds RefinementComponent link

A currency/amount selection field specific to GCash. Includes a peso sign, label, value text, Philippine flag indicator, and a chevron down affordance. 8 variants across State (Default/Active/Error/Disabled) × isFilled (true/false). Part of the Form Elements group — used for currency and amount selection contexts.

Fix required before handoff
Peso sign uses BOOLEAN_OPERATION instead of vector (C6). Flag uses raster IMAGE fill (C6). These block clean native mapping.
In Context

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

Send Money₱ Amount
Live Preview

Toggle state and fill to see the select field update in real time.

Properties
State
isFilled
DS Health
Reusable
Partial
Currency/amount selection pattern used across Send Money, Buy Load, Pay Bills, and other GCash flows. Tightly coupled to Philippine peso — not generalizable to other currencies without modification.
Self-contained
Pass
Carries its own border, fill, peso sign, flag, and chevron per state. All 4 interaction states defined with distinct visual treatment. Disabled state has separate background.
Consistent
Partial
isFilled now uses true/false (C2 fixed). Peso sign still uses shape_full BOOLEAN_OPERATION instead of a clean vector (C6).
Composable
Pass
Nests cleanly in form layouts alongside Input Field, Labeled Field, and Recipient Field. Chevron down signals tappable selection affordance.
Behavior
StateiOSAndroidFigma PropertyNotes
DefaultYesYesState=DefaultGray #D7E0EF border, white bg. Peso sign #183462.
Active (Focused)YesYesState=ActiveBlue #005CE5 border.
ErrorYesYesState=ErrorRed #D61B2C border.
DisabledYesYesState=Disabled#EEF2F9 bg, border hidden. Peso sign #7E96BE.
Resolved Issues
  • isFilled renamed from Yes/No to true/false for direct Swift Bool / Kotlin Boolean mapping C2 Fixed
  • Peso Sign shape_full BOOLEAN_OPERATION flattened to a single vector path across all 8 variants C6 Fixed
  • Field Trailing Flag replaced from raster IMAGE fill to vector SVG across all 8 variants C6 Fixed
Open Issues
  • Code Connect CLI mappings not registered C7
Design Recommendations
  • Replace the shape_full BOOLEAN_OPERATION in Peso Sign with a single flattened vector path for consistent cross-platform rendering Suggested
  • Provide a vector version of the Philippine flag or use an icon instance from the DS icon library instead of a raster IMAGE fill Suggested
  • Consider generalizing the field to support other currency symbols and flags via configurable slots — would increase reuse across multi-currency flows Suggested
  • Add a helperText slot below the field for validation messages — currently error state has no text guidance Suggested
States

8 variants across 2 axes: State (Default/Active/Error/Disabled) × isFilled (true/false). All share the same 46px height container with 6px corner radius. Includes peso sign (15×15), label/value text, Philippine flag (25×16), and chevron down (32×32).

Default

Idle state with gray border. Peso sign in dark navy, flag visible, chevron down affordance.

Properties
StateDefault
isFilled
Active (Focused)

Focused state with blue border indicating active selection.

Properties
StateActive
isFilled
Error

Validation error state with red border.

Properties
StateError
isFilled
Disabled

Non-interactive state with gray background, hidden border, and muted peso sign.

Properties
StateDisabled
isFilled
Colors by State

All states share the same container structure. Border color is the primary state indicator. Peso sign and text colors shift in disabled state.

RoleTokenDEFAULTACTIVEERRORDISABLED
Borderfield/border#D7E0EF#005CE5#D61B2Chidden
Backgroundfield/bg#FFFFFF#FFFFFF#FFFFFF#EEF2F9
Label textfield/text/label#0A2757#0A2757#0A2757#0A2757
Value (filled)field/text/filled#0A2757#0A2757#0A2757#90A8D0
Value (empty)field/text/placeholder#90A8D0#90A8D0#90A8D0#C2CFE5
Peso signfield/icon/peso#183462#183462#183462#7E96BE
Layout
PropertyValue
Height46px
Corner radius6px
Peso sign size15 × 15
Flag size25 × 16
Flag corner radius2px
Chevron size32 × 32
Typography
LayerPropertyValue
#labelFontProxima Soft Semibold
Size16px
#valueFontProxima Soft
Size14px
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android — Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:form-elements:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.form.*  // Compose

Package not yet published. These are the planned distribution paths.

Property Mapping
Figma PropertySwiftUI ParamCompose ParamNotes
isFilled (true/false)selection: Binding<String?>selectedValue: String?Derived from selection content
State=DefaultDefault idle state
State=Active.focused()interactionSourceSelection active
State=Error.ebError(true)isError=trueValidation failed
State=Disabled.disabled(true)enabled=falseNon-interactive
SwiftUI
ios/Components/FormElements/EBSelectField.swift
Jetpack Compose
android/components/form/EBSelectField.kt
Usage Snippets Planned API
Default
EBSelectField("Amount", selection: $amount)
EBSelectField(
    label = "Amount",
    selectedValue = amount,
    onValueChange = { amount = it }
)
Error
EBSelectField("Amount", selection: $amount)
    .ebError(true)
EBSelectField(
    label = "Amount",
    selectedValue = amount,
    onValueChange = { amount = it },
    isError = true
)
Disabled
EBSelectField("Amount", selection: $amount)
    .disabled(true)
EBSelectField(
    label = "Amount",
    selectedValue = amount,
    onValueChange = { amount = it },
    enabled = false
)
Accessibility
RequirementiOSAndroid
Minimum touch target44 x 44 pt48 x 48 dp
Accessibility label.accessibilityLabel("Select amount")contentDescription
Role hint.accessibilityHint("Double tap to select")semantics { role=Role.DropdownList }
Error announcementVoiceOver reads error via .accessibilityValueTalkBack reads error via semantics { error() }
Usage Guidelines

Do

Use Select Field for currency amount selection where the peso sign and flag indicator provide essential context for the user.

Don't

Use Select Field for free-text entry — use Input Field instead. Select Field is for predefined selection only.

Do

Show error state with a helper text message below the field explaining the validation issue (e.g. "Minimum amount is 1.00").

Don't

Hide the peso sign or flag — these are essential visual cues that distinguish this field from a generic dropdown.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadySemantic layer names: container, peso-sign, text-container, flag-container, Chevron Down.
C2Variant & Property NamingReadyisFilled=true/false — correct boolean convention for native mapping.
C3Token CoveragePartialColors appear correct but token binding not fully verified.
C4Native MappabilityReadyMaps to custom EBSelectField (SwiftUI) / EBSelectField (Compose).
C5Interaction State CoverageReadyAll 4 states defined: Default, Active, Error, Disabled.
C6Asset & Icon QualityNeeds FixPeso sign uses shape_full BOOLEAN_OPERATION (not a vector). Flag uses raster IMAGE fill.
C7Code Connect LinkabilityPendingNo CLI mappings registered yet.
Code Connect
AspectStatusNotes
Property namingReadyisFilled=true/false — boolean convention now correct for Code Connect mapping
Asset qualityBlockedPeso sign BOOLEAN_OPERATION and raster flag need replacement
State coverageReadyAll 4 states defined
Native component filePendingEBSelectField.swift / EBSelectField.kt not yet created
Variants Inventory (8 total)

4 State values × 2 isFilled values (true/false).

StateisFilledNode ID
Defaulttrue17758:3787
Defaultfalse17758:3797
Activetrue17758:3807
Activefalse17758:3817
Errortrue17758:3827
Errorfalse17758:3837
Disabledtrue17758:3847
Disabledfalse17758:3857
1.1.0 — March 2026 Update
C2 Fix — isFilled boolean naming · node 17758:3786
isFilled renamed from Yes/No to true/false — Figma component now uses correct boolean convention. Enables direct Swift Bool / Kotlin Boolean mapping for Code Connect. Fixed
C2 Fixed
1.0.0 — March 2026 Initial
Initial Assessment · node 17758:3786
Component assessed — 8 variants documented across State (Default/Active/Error/Disabled) × isFilled (true/false). Currency/amount selection field with peso sign, flag, and chevron. Part of Form Elements group. Documented
Initial
Boolean property uses Yes/NoisFilled=Yes/No instead of true/false. Incompatible with Swift Bool and Kotlin Boolean for Code Connect mapping. Fixed in 1.1.0
C2 Fixed
Peso sign uses BOOLEAN_OPERATIONshape_full is a BOOLEAN_OPERATION, not a clean vector path. May render inconsistently on native platforms. Open
C6 Open
Flag uses raster IMAGE fill — Philippine flag in flag-container uses a raster IMAGE fill instead of a vector. May degrade on high-density displays. Open
C6 Open
Code Connect mappings — No CLI mappings registered yet. Blocked by C6 (asset quality). Open
C7 Open
Title Bar Component link

An app-level navigation title bar used at the top of every screen. Includes an iOS status bar stub (44px), a centered title row with optional leading icon (back arrow), trailing icon, leading control ("Done" text), subtext (URL), and an optional title block for large headers. 20 variants across 5 boolean properties. Background is brand blue (#1972F9), all text and icons white.

In Context

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

Send Money
Live Preview

Toggle properties to see the title bar update in real time.

Properties
leading icon
trailing icon
leading control
subtext
title block
DS Health
Reusable
Pass
Navigation title bar used across every screen in the app. Boolean property toggles cover all common configurations: back arrow, trailing action, subtext URL, CTA control, and large header block.
Self-contained
Pass
Carries its own status bar stub, title row, icon slots, subtext, and optional header block. Background color and all text/icon colors are token-bound. No external dependencies.
Consistent
Partial
Boolean properties use yes/no instead of true/false (C2). leading control only available when leading icon=yes and trailing icon=no -- implicit dependency not expressed in property schema.
Composable
Pass
Fits as the top element on any screen. Nests below the system status bar. Content area sits directly below the title bar. Title block expands naturally when toggled on.
Behavior
StateiOSAndroidFigma PropertyNotes
DefaultYesYes5 boolean propertiesNavigation bar. No interaction states beyond tap targets on icons and control text.

Title Bar is a navigation container. Interaction states (pressed, focused) apply to the individual tap targets (leading icon, trailing icon, leading control) rather than the bar itself. These states are not represented as separate variants in the component.

Open Issues
  • Boolean properties (leading icon, trailing icon, leading control, subtext, title block) use yes/no instead of true/false -- incompatible with Swift Bool and Kotlin BooleanC2
  • Trailing icon uses icon-placeholder RECTANGLE (24x24) instead of a swappable icon instance -- blocks native icon slot mapping C6
  • Code Connect CLI mappings not registered C7
Design Recommendations
  • Rename boolean properties from yes/no to true/false for direct native boolean mapping Suggested
  • Replace trailing icon icon-placeholder RECTANGLE with a swappable icon instance from the DS icon library (e.g. more/ellipsis, share, search) Suggested
  • Make leading control dependency explicit -- consider a separate property or document that it requires leading icon=yes and trailing icon=noSuggested
  • Consider adding a dark/transparent variant for screens with hero images or gradient backgrounds Suggested
  • Add showAsset property documentation -- currently only available when title block=yes; its purpose (background image) should be explicitly described in the component spec Suggested
Configurations

20 variants across 2 structural configurations: Standard (no title block, 10 variants) and With Title Block (titleBlock=yes, 10 variants). Each configuration has the same 10 boolean property combinations for leading icon, trailing icon, leading control, and subtext.

Standard
DESDEV

Standard title bar without title block. Status bar (44px) + title row with optional icons, control, and subtext. Height ranges from 84px to 100px depending on subtext.

Properties
leading icon
trailing icon
leading control
subtext
Properties
leading iconyes
trailing iconno
leading controlno
subtextno
title blockno
With Title Block
DESDEV

Title bar with expanded header block (72px) below the title row. Used for screens with prominent section headers. Adds "Header" text at 26px Semibold.

Properties
leading icon
trailing icon
leading control
subtext
Properties
leading iconyes
trailing iconno
leading controlno
subtextno
title blockyes
Colors by State

Single color scheme -- no appearance modes. All colors bound to main/title-bar/color/ tokens. Display/navigation component with no state-driven color changes.

RoleTokenValue
Backgroundmain/title-bar/color/bg#1972F9
Title labelmain/title-bar/color/label-title#FFFFFF
Header labelmain/title-bar/color/label-header#FFFFFF
Subtext / URLmain/title-bar/color/label-url#F6F9FDCC (80% opacity)
CTA textmain/title-bar/color/label-cta#FFFFFF
Iconmain/title-bar/color/icon#FFFFFF
Layout
PropertyValue
Status bar height44px
Title row padding H20px
Title row padding V12px
Leading icon size24 x 24
Trailing icon size24 x 24
Title block height72px
Title block padding H24px
Total height (no subtext, no block)~84px
Total height (with subtext, no block)~100px
Total height (with block)~156--172px
Typography
LayerText StyleFontSizeTrackingLine-height
TitlePrimary/Label/Light/BaseProxima Soft Semibold16px0.25px16px
SubtextPrimary/Label/Light/FineProxima Soft Semibold12px0.5px12px
HeaderPrimary/Headlines/Light/AreaProxima Soft Semibold26px0.85px31px
CTA (control)Primary/Label/Light/SmallProxima Soft Semibold14px0.25px14px
Installation Planned API

iOS -- Swift Package Manager

// In Xcode: File -> Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android -- Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:titlebar:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.titlebar.*  // Compose

Package not yet published. These are the planned distribution paths.

Property Mapping
Figma PropertySwiftUIComposeNotes
leading icon (yes/no).ebLeadingIcon(Image?)leadingIcon: @Composable (() -> Unit)?Back arrow, optional
trailing icon (yes/no).ebTrailingIcon(Image?)trailingIcon: @Composable (() -> Unit)?Action icon, optional
leading control (yes/no).ebLeadingControl("Done")leadingControlText: String?CTA text, replaces trailing icon
subtext (yes/no).ebSubtext("m.gcash.com")subtext: String?URL or secondary text below title
title block (yes/no).ebTitleBlock("Header")titleBlock: String?Large header below title row
SwiftUI
ios/Components/TitleBar/EBTitleBar.swift
Jetpack Compose
android/components/titlebar/EBTitleBar.kt
Usage Snippets Planned API
Basic (title only)
EBTitleBar("Send Money")
EBTitleBar(
    title = "Send Money"
)
With back arrow
EBTitleBar("Send Money")
    .ebLeadingIcon(Image(systemName: "arrow.left"))
EBTitleBar(
    title = "Send Money",
    leadingIcon = { Icon(Icons.Default.ArrowBack, "Back") }
)
Full configuration
EBTitleBar("GCash")
    .ebLeadingIcon(Image(systemName: "arrow.left"))
    .ebTrailingIcon(Image(systemName: "ellipsis"))
    .ebSubtext("m.gcash.com")
    .ebTitleBlock("My Wallet")
EBTitleBar(
    title = "GCash",
    leadingIcon = { Icon(Icons.Default.ArrowBack, "Back") },
    trailingIcon = { Icon(Icons.Default.MoreVert, "More") },
    subtext = "m.gcash.com",
    titleBlock = "My Wallet"
)
With leading control
EBTitleBar("Edit Profile")
    .ebLeadingIcon(Image(systemName: "arrow.left"))
    .ebLeadingControl("Done")
EBTitleBar(
    title = "Edit Profile",
    leadingIcon = { Icon(Icons.Default.ArrowBack, "Back") },
    leadingControlText = "Done"
)
Accessibility
RequirementiOSAndroid
Minimum touch target44 x 44 pt (icons and control)48 x 48 dp (icons and control)
Back button label.accessibilityLabel("Back")contentDescription="Navigate back"
Trailing icon label.accessibilityLabel("More options")contentDescription="More options"
Heading semantics.accessibilityAddTraits(.isHeader) on titlesemantics { heading() } on title
Usage Guidelines

Do

Use EBTitleBar as the top-level navigation element on every screen. Keep the title short and descriptive.

Don't

Nest a title bar inside scrollable content or use it as a section header within a page -- use a section heading component instead.

Do

Use the title block for high-level section headers like "My Wallet" or "Dashboard" where the large text reinforces the current context.

Don't

Show both trailing icon and leading control simultaneously -- they occupy the same trailing slot. Use one or the other.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadySemantic layer names: title, Title Bar, title-block, Leading Icon, Placeholder.
C2Variant & Property NamingNeeds RefinementAll 5 boolean properties use yes/no instead of true/false. leading control has implicit dependency on other properties.
C3Token CoverageReadyAll 6 color roles bound to main/title-bar/color/ tokens.
C4Native MappabilityReadyMaps to NavigationBar (iOS) / TopAppBar (Android, Material 3).
C5Interaction State CoverageReadyNavigation bar -- no interaction states needed beyond individual tap targets on icons and control.
C6Asset & Icon QualityNeeds RefinementTrailing icon uses icon-placeholder RECTANGLE instead of a swappable icon instance.
C7Code Connect LinkabilityNeeds RefinementNo CLI mappings registered yet.
Code Connect
AspectStatusNotes
Property namingNeeds FixAll booleans use yes/no -- must be renamed to true/false before Code Connect mapping
Asset qualityNeeds FixTrailing icon placeholder RECTANGLE needs replacement with icon instance
State coverageReadyNavigation bar -- no interaction states needed
Native component filePendingEBTitleBar.swift / EBTitleBar.kt not yet created
Variants Inventory (20 total)

5 boolean properties (leading icon, trailing icon, leading control, subtext, title block) with implicit constraints yield 20 variants: 10 without title block + 10 with title block.

title blockCombinations coveredCount
no10 combos of leading/trailing icon + leading control + subtext10
yesSame 10 combos with title block enabled10
View full property combination breakdown (20 rows)
leading icontrailing iconleading controlsubtexttitle blockNode ID
nonononono23:175149
nononoyesno23:175365
noyesnonono23:175415
noyesnoyesno23:175427
yesyesnonono23:175377
yesyesnoyesno23:175389
yesnononono23:175487
yesnonoyesno23:175499
yesnoyesnono23:175449
yesnoyesyesno23:175461
nonononoyes23:175159
nononoyesyes23:175169
noyesnonoyes23:175179
noyesnoyesyes23:175189
yesyesnonoyes23:175199
yesyesnoyesyes23:175209
yesnononoyes23:175219
yesnonoyesyes23:175229
yesnoyesnoyes23:175239
yesnoyesyesyes23:175249
1.0.0 -- April 2026 Initial
Initial Assessment -- node 23:175148
Component assessed -- 20 variants documented across 5 boolean properties: leading icon, trailing icon, leading control, subtext, title block. App navigation title bar with brand blue background and white text/icons. All colors bound to main/title-bar/color/ tokens. Documented
Initial
Boolean properties use yes/no -- All 5 boolean properties (leading icon, trailing icon, leading control, subtext, title block) use yes/no instead of true/false. Incompatible with Swift Bool and Kotlin Boolean for Code Connect mapping. Open
C2 Open
Trailing icon uses placeholder RECTANGLE -- icon-placeholder is a 24x24 RECTANGLE instead of a swappable icon instance from the DS icon library. Blocks native icon slot mapping. Open
C6 Open
Code Connect mappings -- No CLI mappings registered yet. Blocked by C2 (boolean naming) and C6 (placeholder icon). Open
C7 Open
Visual Popup FixNeeds RefinementComponent link

A modal with a hero image, title, description, and one or more CTAs. Used to display additional information, gather user input, or seek confirmation for critical actions. 3 variants: Default (single CTA), 2 CTA (primary + secondary), Version 2 (preamble, close icon, content-first layout for onboarding).

Open issues remain
Variant naming mixes paradigms — Default / 2 CTA / Version 2 (C2). Hero image is a raster placeholder with "Replace me" overlay instead of a swappable image slot (C6). No destructive/error/loading state coverage (C5). Code Connect mappings not registered (C7).
In Context

Contexts are illustrative. Final screens will reference actual GCash patterns. Visual Popup overlays the app surface to confirm critical actions or onboard users to a new feature.

Hero imageOkay
Live Preview

Toggle Type to see each variant. Hero image, title, description, and CTA(s) update accordingly.

Properties
Type
DS Health
Reusable
Pass
Three layouts cover info modals, confirmation prompts (single + dual CTA), and onboarding popups (Version 2). Fixed 320 / 312 px width fits standard mobile dialog patterns.
Self-contained
Pass
Carries its own bg (main/modal-popup/color/bg), Shadow/Depth 0, radius/radius-2, and 24px padding. Composes Button instances rather than redefining button styles.
Consistent
Warn
Variant naming mixes three paradigms: Default (generic), 2 CTA (count), Version 2 (version). Native enums need a single semantic axis — e.g. single-cta / dual-cta / dismissible. C2
Composable
Pass
All CTAs are real Button instances. Close icon (V2) is a vector instance. Hero image is the only non-component child — see C6.
Behavior
StateiOSAndroidFigma PropertyNotes
Single CTAYesYesType=DefaultHero (180px) + title + description + primary CTA. Use for info or single-action confirm.
Dual CTAYesYesType=2 CTAAdds a secondary outline + tertiary text button below primary. Use for cancel/confirm pairs.
Dismissible (V2)YesYesType=Version 2Preamble label + title with close icon + content-first layout. Use for onboarding/tutorial popups.
Destructive / Error / LoadingN/AN/ANo variants for destructive confirms or async/loading states. C5
Open Issues
  • Variant naming mixes paradigms — Default (generic), 2 CTA (count), Version 2 (version). Native enum mapping needs one semantic axis. Suggested: single-cta / dual-cta / dismissible. C2
  • No destructive/error/loading state coverage — engineers must improvise these for "Cancel transaction?", error confirms, and async submit flows. C5
  • Hero image is a flat raster placeholder with a "Replace me" overlay. Should be a swappable Image slot (component instance) so product teams override per-popup without editing the master. C6
  • Code Connect CLI mappings not registered. C7
Design Recommendations
  • Rename property Type values to a single semantic axis: single-cta, dual-cta, dismissible. Eliminates the version/count/default mix and maps cleanly to EBVisualPopupKind enum. Restructure
  • Replace the raster Modals Asset with a swappable Image slot — a component placeholder that product teams can instance-swap with their illustration. Matches the pattern Avatar uses for the image type. Asset slot
  • Add a destructive boolean (or kind=destructive mode) so destructive confirms (Cancel / Logout / Delete) can use red CTAs without bespoke overrides. State coverage
  • Consider a loading state for the primary CTA — async submits in popups currently have no documented affordance. Could reuse the planned Button loading state. State coverage
Variants

3 layouts. Default and 2 CTA share the same hero-on-top structure; Version 2 inverts to content-first with the hero embedded inside a light-gray container.

Default — single primary CTA

Hero image (320 × 180, 16:9) + title + 2-line description + single primary CTA. Use for informational modals or single-action confirms ("Okay").

2 CTA — primary outline + tertiary text

Same hero + title + description as Default, then a secondary outline button on top of a tertiary text button. Use for confirm/cancel pairs.

Version 2 — preamble + close icon, content-first

Onboarding/tutorial layout. The popup itself is a single light-gray (bg/color-bg) container — preamble label, title with close icon, description, a 280×180 hero image with 10px radius, then primary CTA. (The outer white frame has zero padding, so only the gray container is visible.)

Colors by Variant

Modal popup ships display-only color tokens — no pressed/disabled states (the popup itself doesn't have interaction states; CTAs handle that via Button tokens).

RoleTokenValue
Modal backgroundmain/modal-popup/color/bg#FFFFFF
Title labelmain/modal-popup/color/label#0A2757
Description labelmain/modal-popup/color/label-primary#6780A9
Preamble (V2)main/modal-popup/color/label-preamble#90A8D0
Close icon (V2)main/modal-popup/color/icon-close#6780A9
V2 inner containerbg/color-bg#F6F9FD
Layout
PropertyTokenValue
Default / 2 CTA width320px
Version 2 width312px
Hero image (Default / 2 CTA)320 × 180 (16:9)
Hero image (V2)280 × 180, 10px radius
Body paddingspace/space-2424px
CTA group padding (vertical)space/space-2424px
2 CTA gap between buttonsspace/space-88px
V2 inner container paddingspace/space-1616px h, 16t / 24b
Corner radiusradius/radius-26px
ShadowShadow/Depth 00 0 4px #E8EEF2C9
Close icon (V2)24 × 24
Typography
ElementDS text styleSpec
TitlePrimary/Headlines/SectionProxima Soft Bold · 22 / 26
DescriptionSecondary/Default/BaseBarkAda Medium · 14 / 20
Preamble (V2)Primary/Label/TinyProxima Soft Bold · 10 / 10 · +0.25
CTA labelPrimary/Label/LargeProxima Soft Bold · 18 / 18 · +0.25
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android — Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:visual-popup:1.0.0")
}
Property Mapping
Figma PropertySwiftUI ParamCompose ParamNotes
Type=Default.ebKind(.singleCTA)kind=EBVisualPopupKind.SingleCTAHero + title + description + primary CTA
Type=2 CTA.ebKind(.dualCTA)kind=EBVisualPopupKind.DualCTAAdds secondary outline + tertiary text button
Type=Version 2.ebKind(.dismissible)kind=EBVisualPopupKind.DismissiblePreamble + close icon + content-first layout
Hero image (raster)heroImage: ImageheroImage: PainterCurrently a placeholder; should become a swappable slot
CTA buttonsprimary / secondary / tertiary: EBButtonprimary / secondary / tertiary: @ComposableCompose Button instances directly
SwiftUI
ios/Components/VisualPopup/EBVisualPopup.swift
Jetpack Compose
android/components/visualpopup/EBVisualPopup.kt
Usage Snippets Planned API
// Default — single CTA
EBVisualPopup(
    title: "Cash In Successful",
    description: "₱500.00 added to your wallet.",
    heroImage: Image("cash-in-success"),
    primary: EBButton("Okay") { /* dismiss */ }
)
.ebKind(.singleCTA)

// 2 CTA — confirm/cancel
EBVisualPopup(
    title: "Cancel transaction?",
    description: "This cannot be undone.",
    heroImage: Image("warning-illustration"),
    primary: EBOutlinedButton("Confirm") { /* confirm */ },
    secondary: EBTextButton("Go Back") { /* dismiss */ }
)
.ebKind(.dualCTA)

// Version 2 — onboarding with close icon
EBVisualPopup(
    preamble: "NEW",
    title: "Save your receipts",
    description: "Tap any transaction to save its receipt.",
    heroImage: Image("receipt-tutorial"),
    primary: EBButton("Got it") { /* dismiss */ },
    onClose: { /* dismiss */ }
)
.ebKind(.dismissible)
// Default — single CTA
EBVisualPopup(
    kind = EBVisualPopupKind.SingleCTA,
    title = "Cash In Successful",
    description = "₱500.00 added to your wallet.",
    heroImage = painterResource(R.drawable.cash_in_success),
    primary = { EBButton("Okay", onClick = { /* dismiss */ }) }
)

// 2 CTA — confirm/cancel
EBVisualPopup(
    kind = EBVisualPopupKind.DualCTA,
    title = "Cancel transaction?",
    description = "This cannot be undone.",
    heroImage = painterResource(R.drawable.warning),
    primary = { EBOutlinedButton("Confirm", onClick = { /* confirm */ }) },
    secondary = { EBTextButton("Go Back", onClick = { /* dismiss */ }) }
)

// Version 2 — onboarding with close icon
EBVisualPopup(
    kind = EBVisualPopupKind.Dismissible,
    preamble = "NEW",
    title = "Save your receipts",
    description = "Tap any transaction to save its receipt.",
    heroImage = painterResource(R.drawable.receipt_tutorial),
    primary = { EBButton("Got it", onClick = { /* dismiss */ }) },
    onClose = { /* dismiss */ }
)
Accessibility
RequirementiOSAndroid
Modal trait / rolePresent via .sheet or .alert — VoiceOver announces as modalDialog announces as modal; TalkBack focus trapped inside
Focus trapAutomatic with .sheetAutomatic with Dialog — set dismissOnClickOutside=false for confirm popups
Close button label (V2).accessibilityLabel("Close")contentDescription="Close"
Hero imageIf decorative: .accessibilityHidden(true). If informative: provide a label.Same — contentDescription=null for decorative, otherwise describe
Tap targetsCTAs use Button which meets HIG 44ptCTAs meet Material 48dp
Destructive roleCurrently undefined — needs role: .destructive when state landsCurrently undefined — needs Button destructive colors when state lands
Usage Guidelines

Do

Use Visual Popup for critical confirms, success states with celebration, and onboarding moments — places where the hero image adds emotional weight.

Don't

Use for inline form errors or transient notifications — those belong in Toast, Banner, or inline error patterns, not a blocking modal.

Do

Use the Default variant when the popup has one obvious next step (Okay, Got it, Continue).

Don't

Use 2 CTA when one button is clearly more important than the other — that's still a Default with the secondary action elsewhere.

Do

Use Version 2 (with close icon) only for onboarding/tutorial popups where the user can dismiss without taking action.

Don't

Add a close icon to confirm/destructive popups — forces the user to consciously choose the CTA.

Criteria Scorecard
IDCriterionStatusNotes
C1Layer Structure & NamingReadySemantic names: Modals Asset, body, header, CTA - Base Button Group, Close.
C2Variant & Property NamingNeeds FixProperty values mix paradigms: Default / 2 CTA / Version 2. Should be one semantic axis.
C3Token CoverageReadyAll colors, spacing, radii, shadow, and typography bound to tokens.
C4Native MappabilityReadyMaps to .sheet / .alert on iOS and Dialog / AlertDialog on Android.
C5Interaction State CoverageNeeds FixNo destructive, error, or loading variants. Close affordance only on Version 2.
C6Asset & Icon QualityNeeds FixHero is a flat raster placeholder with "Replace me" overlay. Should be a swappable Image slot.
C7Code Connect LinkabilityPendingNo CLI mappings registered yet.
Variants Inventory (3 total)
TypeWidthHeroCTAsNode ID
Default320px320 × 180 (raster)1 primary30:81517
2 CTA320px320 × 180 (raster)1 outline + 1 text7995:5265
Version 2312px280 × 180 (raster, in container)1 primary + close icon30:81515
1.0.0 — April 2026 Initial
Initial Assessment · node 30:81526
Component assessed — 3 variants (Default / 2 CTA / Version 2). Hero image, title, description, CTA(s). Used for confirms, success states, and onboarding popups. Documented
Initial
Variant naming mixes paradigmsDefault (generic), 2 CTA (count), Version 2 (version). Should be a single semantic axis (single-cta / dual-cta / dismissible). Open
C2 Open
No destructive/error/loading state — Engineers must improvise these for cancel/delete confirms and async submits. Open
C5 Open
Hero image is a raster placeholder — "Replace me" overlay on a flat Modals Asset image. Should be a swappable Image slot via instance swap. Open
C6 Open
Code Connect mappings — No CLI mappings registered yet. Open
C7 Open