A transient bottom-anchored message used for confirmations and inline alerts; auto-dismisses after a short delay.
action slot. Split the overloaded theme axis into appearance = neutral | destructive | pending + theme = light | dark. Replace Large Label with size = small | base. Replace the Pending placeholder circle with a real spinner instance. Add a dismiss contract (swipe + auto-duration).Toasts float over the app screen — not inline with content. Success toasts confirm completed actions ("Transfer sent"), pending toasts acknowledge background work ("Uploading…"), and error toasts surface failures that don't block the flow. They auto-dismiss after ~3 seconds unless swiped.
Add the popup message here
icon-placeholder gray circle instead of a real spinner — consumers can't drop in a live progress indicator without editing the master.theme axis mixes appearance (light/dark surface) with semantic status — Error forces theme=default and loses the dark/light choice. Large Label is really a size axis phrased as a content flag.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Show / auto-dismiss | Yes | Yes | Not modeled | Toasts appear on a host overlay and auto-dismiss after a duration (~3s short, ~5s long). Host-screen concern — no visual state variant needed. |
| Swipe to dismiss | N/A | N/A | Not annotated | Standard gesture on both platforms. Not called out in the component spec. |
| Tap to dismiss | N/A | N/A | Not annotated | Pending variants already wrap the container in a button element in the Figma code — but no interaction callback is documented. |
| Pending spinner animation | N/A | N/A | Gray circle | Pending icon is a static gray circle (icon-placeholder) — should be an animated spinner (ProgressView / CircularProgressIndicator). |
| A11y announcement | N/A | N/A | Not annotated | Error toasts should announce as assertive; default/pending as polite. Not spec'd. |
- Two components model one primitive. Toast + Toast - With Button differ only in the presence of an action button. Action belongs on this component as an optional slot, not a separate family member. Doubling the surface area of every future change. C1 · Layer Structure & Naming
-
themeaxis overloaded with status. Values aredefault | light | darkbutdefaultis only valid whentype=error(it paints the destructive red surface). Real axes areappearance = neutral | destructive | pending×theme = light | dark. Current schema blocks light/dark error variants. C2 · Variant & Property Naming -
Large Labelis a size flag, not a content flag. The two values change padding, font size, and line-height — this is a size axis. Rename tosize = small | base(orcompact/regular) so the schema reads correctly. C2 · Variant & Property Naming - Booleans use
yes/nostrings.With Icon/Large Label— blocks direct SwiftBool/ KotlinBooleanmapping. C2 · Variant & Property Naming - Pending type uses a placeholder icon. 16 × 16 (small) and 24 × 24 (base) gray
icon-placeholdercircles instead of an animated spinner. No instance of a ProgressView / CircularProgressIndicator — native consumers can't wire up a real loading state. C6 · Asset & Icon Quality - No swipe-to-dismiss or auto-duration contract. Standard toast behaviors on both iOS and Android. Neither is annotated in the Figma component spec — engineers have to guess the duration budget and gesture handling. C5 · Interaction State Coverage
- No native primitive mapping documented. SwiftUI has no first-party toast (pre-iOS 17); Compose uses SnackbarHost + Snackbar. The component doesn't call out either mapping — dev-handoff guessing game. C4 · Native Mappability
- Code Connect mappings not registered. Blocked on family consolidation and axis cleanup. C7 · Code Connect Linkability
- Consolidate Toast + Toast - With Button. Target schema:
EBToast(message, appearance = .neutral | .destructive | .pending, theme = .light | .dark, size = .small | .base, leadingIcon?: Icon, action?: EBToastAction). Remove theWith Buttoncomponent from the family. Family - Split
themeintoappearance+theme.appearance = neutral | destructive | pendingcontrols semantic status + surface palette;theme = light | darkcontrols the neutral-surface contrast mode. Unlocks light/dark destructive variants and matches every other DS component's mental model. Property - Rename
Large Labeltosize = small | base. Matches the actual axis (padding, font size, spacing) and mirrors Button / Alert sizing. Rename - Normalize booleans to
true/false.With Iconand any remaining flags. Then renameWith IcontoleadingIcononce promoted to a Slot. Rename - Replace Pending placeholder with a real spinner. Instance-swap a ProgressView / CircularProgressIndicator (or the DS Spinner component when it ships) into the leading-icon slot for
appearance=pending. Same slot covers the checkmark forappearance=neutraland the X forappearance=destructive. Asset - Promote the leading icon to a swappable Slot. Adopt Figma Slots so consumers can drop in any Icon from the DS icon library. Default slot content per appearance: checkmark / spinner / error glyph. Slot
- Add an optional action slot.
action?: EBToastAction— takes label + callback. Covers the "Undo", "Retry", "View" use cases and replaces the need for Toast - With Button entirely. Slot - Document duration + dismiss behavior. Annotate default duration (3s short / 5s long), swipe-to-dismiss, tap-to-dismiss, and the
onDismisscallback contract in the component spec. Not a visual variant — a usage note. Docs - Document the A11y live-region mapping. Error toasts announce as assertive (
UIAccessibility .high/LiveRegionMode.Assertive); default + pending as polite. Spell this out so engineers wire the right roles. A11y
Confirms a completed action — transfer sent, settings saved, upload finished. Default theme places a checkmark glyph on a dark navy surface.
Add the popup message here
Information toast. Theme axis flips surface + label between Dark (navy) and Light (white).
| Role | Token | Theme · Dark | Theme · Light |
|---|---|---|---|
| Surface bg | main/toast/default/{theme}/bg | #0A2757 | #FFFFFF |
| Border | main/toast/default/{theme}/border | #E5EBF4 | #E5EBF4 |
| Label | main/toast/default/{theme}/label | #FFFFFF | #0A2757 |
| Icon | main/toast/default/{theme}/icon | #FFFFFF | #0A2757 |
Surfaces a failure that needs the user's attention — failed transfer, invalid input, expired session. Red surface with a leading X glyph.
Add the popup message here
Critical/error toast. Single appearance regardless of theme.
| Role | Token | Default |
|---|---|---|
| Surface bg | main/toast/error/bg | #D61B2C |
| Border | main/toast/error/border | #F4C7C9 |
| Label | main/toast/error/label | #FFFFFF |
| Icon | main/toast/error/icon | #FFFFFF |
Acknowledges in-flight work the user kicked off — submitting a form, syncing a balance, processing a payment. A spinner glyph signals the action is still running.
Add the popup message here
In-progress / loading state toast. Same surface palette as Default; spinner replaces the static icon.
| Role | Token | Theme · Dark | Theme · Light |
|---|---|---|---|
| Surface bg | main/toast/pending/{theme}/bg | #0A2757 | #FFFFFF |
| Border | main/toast/pending/{theme}/border | #E5EBF4 | #E5EBF4 |
| Label | main/toast/pending/{theme}/label | #FFFFFF | #0A2757 |
| Spinner | main/toast/pending/{theme}/spinner | #FFFFFF | #0A2757 |
The proposed schema collapses Toast + Toast - With Button into one API. Action becomes an optional slot, theme splits into appearance + theme, and Large Label becomes size.
| Figma Property | SwiftUI | Compose |
|---|---|---|
Type: default | pending | error | appearance: neutral | pending | destructive | appearance: EBToastAppearance |
Theme: default | light | dark (overloaded) | theme: light | dark (neutral + pending only) | .ebToastTheme(.dark) |
Large Label: yes | no | size: small | base | .controlSize(.small / .regular) |
With Icon: yes | no | leadingIcon?: Icon (slot) | leadingIcon: Image? |
| (implicit) | message: String | message: String |
| (separate component) | action?: ToastAction | action: EBToastAction? |
| (not modeled) | duration: short | long | duration: EBToastDuration |
| (not modeled) | onDismiss?: () -> Void | onDismiss: (() -> Void)? |
| Requirement | iOS | Android |
|---|---|---|
| Live region — error | Post UIAccessibility.Notification.announcement with .high priority on present. | Modifier.semantics { liveRegion = LiveRegionMode.Assertive } on the Snackbar container. |
| Live region — neutral / pending | Post announcement with default priority. | LiveRegionMode.Polite. |
| Minimum duration | Short ≥ 3s, long ≥ 5s; extend for longer messages per iOS HIG. | SnackbarDuration.Short / Long (Material 3 defaults). |
| Action button label | Action slot owns its own accessibilityLabel. | Action slot owns its own contentDescription. |
| Dismiss gesture | Swipe horizontally to dismiss; respect reduce-motion for the slide-out animation. | Swipe to dismiss built into Snackbar; honor TalkBackUserTouchExplorationEnabled to extend duration. |
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Requires Rework | Two components for one primitive — consolidate with Toast - With Button. |
| C2 | Variant & Property Naming | Requires Rework | theme overloaded with status; Large Label is a size flag; booleans on yes/no. |
| C3 | Token Coverage | Ready | All colors bound to main/toast/color/{mode}/*. Spacing + typography fully tokenized. |
| C4 | Native Mappability | Requires Rework | No SwiftUI first-party primitive; Compose has Snackbar. Needs documented mapping + ToastManager pattern. |
| C5 | Interaction State Coverage | Requires Rework | No auto-duration, swipe, or tap-to-dismiss contract; pending has no animation. |
| C6 | Asset & Icon Quality | Requires Rework | Pending uses icon-placeholder gray circle instead of a real spinner. |
| C7 | Code Connect Linkability | Not Mapped | Blocked on family consolidation and axis cleanup. |
Effective axes today: Type (3) × Theme (3, coupled to Type) × With Icon (2) × Large Label (2). Built variants collapse the illegal combinations — Error only pairs with theme=default; Pending only pairs with theme=dark | light + with icon=yes. = 16 built variants.
| Group | Count | Axes |
|---|---|---|
| Default / Dark | 4 | withIcon=yes/no × largeLabel=yes/no |
| Default / Light | 4 | withIcon=yes/no × largeLabel=yes/no |
| Error / Default | 4 | withIcon=yes/no × largeLabel=yes/no |
| Pending / Dark | 2 | withIcon=yes (forced) × largeLabel=yes/no |
| Pending / Light | 2 | withIcon=yes (forced) × largeLabel=yes/no |
theme axis, rename Large Label to size, and replace the Pending placeholder with a real spinner. Openaction slot. Opentheme mixes appearance + status; Large Label is a size flag; booleans on yes/no. Openicon-placeholder circle; adopt a real spinner instance. Open