A centred no-content surface — visual, header (title + description), and an action. 4 variants across Style (Default/Subtle) × VisualType (Icon/Asset), with a Visual Container, Action Container slot, and a token-bound header.
Style (Default/Subtle) × VisualType (Icon/Asset). The duplicate header booleans merged into one header (title + description), color became Style, and the icon, asset, and action are all real Figma slots. The visual slot was unified to a single Visual Container so switching VisualType keeps its content, and visualType was recased to VisualType to match the enum convention. Only Code Connect registration remains.Contexts are illustrative. Final screens will reference actual GCash patterns. Empty State fills a surface where content would normally sit — empty transaction lists, no search results, first-run tabs.
#F6F9FD, Default white.Style × VisualType = a complete 2 × 2, replacing the old 7-boolean / 256-combination schema. Values match the token namespace (Default / Subtle), the duplicate header booleans are gone, and both enums are Title Case.Visual Container for the icon or asset, and an Action Container holding a canonical Button instance. Switching VisualType preserves the visual slot content because both types share one slot name.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default | Yes | Yes | Style=Default | White background — use when the empty state sits on a light-blue surface. |
| Subtle | Yes | Yes | Style=Subtle | Light-blue #F6F9FD background — use when sitting on a white surface. |
| Icon visual | Yes | Yes | VisualType=Icon | 64px icon in the Visual Container. Compact empties. |
| Asset visual | Yes | Yes | VisualType=Asset | 230px illustration in the Visual Container. Richer, first-run empties. |
| Pressed / Disabled | N/A | N/A | — | Not modelled by design — Empty State is a display surface. Interactivity lives on the Button in the Action Container, which carries its own states. |
- Code Connect mappings not registered. The schema collapse, slot adoption, and naming are all resolved. Registration is unblocked but the SwiftUI / Compose mappings are not yet wired and the native component does not exist — snippets remain a Planned API. C7 · Code Connect Linkability
- v2.0: 7-boolean schema collapsed to two enums —
Style(Default/Subtle) ×VisualType(Icon/Asset), a clean 2 × 2. The old 256-combination surface with mutually exclusive booleans is gone. (C2) - v2.0: Duplicate
header/header1booleans merged into a single header frame with#heading+#description. (C2) - v2.0:
color=white/grey bluerenamedStyle=Default/Subtle, matching the token namespace and dropping the space-in-value. Subtle ships#F6F9FD. (C2) - v2.0: Icon placeholder replaced with a real Figma slot, and the flat coloured asset rectangle with an
Assetslot wrapping an instance — both swappable. (C6) - v2.0: Action promoted to a slot too —
Action Containerholds a canonicalButtoninstance rather than a drawn button. (C6) - v2.1: Visual slot unified to a single
Visual Containeracross all four variants (wasIcon Container/Asset Container). SwitchingVisualTypeon an instance now preserves the slot content, and Code Connect maps one slot instead of two. (C1) - v2.1:
visualTyperecased toVisualType— Title Case, matching the enum convention (Style,Type,State) rather than the boolean camelCase style. (C2)
- Register Code Connect mapping to
EBEmptyState. WireStyleandVisualTypeto the SwiftUI / Compose API, and map theVisual ContainerandAction Containerslots to@ViewBuilder/@Composablecontent slots. Docs
- Collapse to one title + description. v2.0: Applied — the duplicate header booleans merged into a single header with
#heading+#description. Property - Rename
color→Stylewith values Default / Subtle. v2.0: Applied — matches the token namespace and drops the space-in-value. Rename - Adopt Figma Slots for icon, asset, and action. v2.0: Applied — all three are real slots, and the visual slot was unified to a single
Visual Containerin v2.1. Slot - Document "icon vs asset". v2.0: Superseded by the schema — the choice is now an explicit
VisualTypeenum (Icon / Asset) rather than a convention to document. Docs
White background — use when the surface behind is dark or tinted. Asset placeholder uses <code>#EEF2F9</code>.
| Role | Token | Token | Value |
|---|---|---|---|
| Default (white) | bg | main/empty-state/color/default/bg | #FFFFFF |
| — | title | main/empty-state/color/default/label-title | #0A2757 |
| — | description | main/empty-state/color/default/description | #6780A9 |
| — | asset placeholder | main/empty-state/color/default/placeholder | #EEF2F9 |
| Subtle (grey blue) | bg | main/empty-state/color/subtle/bg | #F6F9FD |
| — | title | main/empty-state/color/subtle/label-title | #0A2757 |
| — | description | main/empty-state/color/subtle/description | #6780A9 |
| — | asset placeholder | main/empty-state/color/subtle/placeholder | #D7E0EF |
Light blue-grey background (<code>#F6F9FD</code>) — use when the surface behind is white. Asset placeholder uses <code>#D7E0EF</code>.
| Role | Token | Value |
|---|---|---|
| Container width | — | 360px |
| Top padding | — | 16px |
| Bottom padding | space/space-24 | 24px |
| Top header padding | space/space-16 + space/space-24 | 24h / 16b |
| Content padding | space/space-24 | 24h / 16t |
| Content gap (title ↔ description) | — | 10px |
| Button top padding | space/space-24 | 24px |
| Icon slot size | — | 64 × 64 |
| Asset size | — | 360 × 230 |
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:empty-state:1.0.0") }
| Figma Property | SwiftUI | Compose |
|---|---|---|
| color=white/grey blue | style: EBEmptyStateStyle | .ebStyle(.default/.subtle) |
| header1 / topHeading | title: String | title: String |
| topDescription / description | description: String? | description: String? |
| hasIcon + icon placeholder | Figma Slot → ViewBuilder | @ViewBuilder icon |
| hasAsset + asset placeholder | Figma Slot → ViewBuilder | @ViewBuilder asset |
| hasButton + buttonInstance | Figma Slot → ViewBuilder | @ViewBuilder action |
// Empty transaction list — icon + title + description + action EBEmptyState( title: "No transactions yet", description: "Your transactions will show up here.", icon: { Image(systemName: "tray") }, action: { EBButton("Cash In") { /* ... */ } } ) .ebStyle(.default) // Full illustration — asset slot instead of icon EBEmptyState( title: "No favorites added", description: "Tap the heart on any contact to save them here.", asset: { Image("empty-favorites").resizable().scaledToFit() } ) .ebStyle(.subtle)
// Empty transaction list — icon + title + description + action EBEmptyState( title = "No transactions yet", description = "Your transactions will show up here.", style = EBEmptyStateStyle.Default, icon = { Icon(painterResource(R.drawable.tray), contentDescription = null) }, action = { EBButton("Cash In", onClick = { /* ... */ }) } ) // Full illustration — asset slot instead of icon EBEmptyState( title = "No favorites added", description = "Tap the heart on any contact to save them here.", style = EBEmptyStateStyle.Subtle, asset = { Image(painterResource(R.drawable.empty_favorites), contentDescription = null) } )
| Requirement | iOS | Android |
|---|---|---|
| Role | Group as a single accessibility element with combined label | mergeDescendants = true on the container |
| Decorative icon / asset | .accessibilityHidden(true) | contentDescription = null |
| Action button | Separate accessibility element with its own label | Standard Button semantics |
| Live region | Announce when empty state appears (e.g. after filtering returns 0 results) | liveRegion = LiveRegionMode.Polite |
Do
Use for first-run, no-results, and "nothing here yet" surfaces. Always include a primary action when the user can take a step to fill the empty state.
Don't
Use as an error surface — use Inline Message (error type) or a Toast. Empty State assumes the absence of content is expected, not a failure.
Do
Pick either icon or illustration. Don't stack both — the duplication adds visual noise without adding meaning.
Don't
Leave the description blank — a one-line context sentence ("Your transactions will show up here") teaches the user what the feature does.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Semantic: header, content, asset-container, button-container, Icon-Slot. |
| C2 | Variant & Property Naming | Requires Rework | Color values don't match tokens; duplicate header/header1; 7 booleans = 256 combos. |
| C3 | Token Coverage | Ready | All colors bound — just mismatched in the property names. |
| C4 | Native Mappability | Ready | VStack / Column with slots — straightforward native builds. |
| C5 | Interaction State Coverage | Ready | Display-only surface. |
| C6 | Asset & Icon Quality | Requires Rework | Icon + asset are placeholders. Should be slots. |
| C7 | Code Connect Linkability | Needs Refinement | Blocked by C2 restructure. |
Boolean props (hasIcon, hasAsset, hasButton, header, header1, topDescription, topHeading) multiply the effective prop combinations to 256. After restructure to slot-based API, this collapses to 2 style variants × optional slots — unlimited configurations without variant explosion.
| color (current) | Proposed style | Node ID |
|---|---|---|
white | default | 27:169326 |
grey blue | subtle | 27:169339 |
color values don't match token namespace (default/subtle). Openheader/header1 booleans + duplicate top/bottom heading surfaces. Open