A transient bottom-anchored message for confirmations and inline alerts, with optional leading icon, description and trailing action. Auto-dismisses after a short delay.
4915:25141 in the 2026 Working File as Appearance × Theme × Size × three has* booleans, authored as a sparse 22-variant set. The family restructure is complete: Toast and Toast - With Button are merged, the overloaded theme axis is split, every variant value follows §5, layer naming is clean, and the description and trailing action are real Figma Slots composing shared components. All four DS Health traits pass. What remains is documentation — the dismiss and auto-duration contract, the native primitive mapping, and the a11y live-region behaviour.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
Subtext Message for the description and a Button - XSmall for the action, and each Appearance carries its own icon instance from the library. Nothing external required to render.Appearance and Theme are separate axes, Size uses the standard MD | SM scale, the three has* booleans carry correct verb prefixes and are genuine Figma booleans, and every variant value is Title Cased per §5.addon SLOT holding a Button - XSmall instance — addon being §4's canonical name for this pattern — and the description is a content SLOT carrying a shared Subtext Message. The separate Toast - With Button sibling is retired.| 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. |
- Toast and Toast - With Button consolidated. v2.0: Rebuilt on node
4915:25141in the 2026 Working File. The two components that modelled one primitive are now a single set — the trailing button is ahasTrailingActionboolean rather than a separate component. Confirmed as a permanent merge by the component owner. (C4 · Family) C4 · Native Mappability -
themeaxis split into Appearance and Theme. v2.0: The overloaded axis that mixed status with light/dark is nowAppearance(default · destructive · pending) ×Theme(dark · light), exactly as recommended. Semantic meaning and visual mode are independent. (C2 · Property) C2 · Variant & Property Naming -
Large Labelreplaced by a real Size axis. v2.0: The content flag masquerading as a size is nowSize = base | sm. Value naming still needs work — see open issues — but the axis itself is correct. (C2 · Rename) C2 · Variant & Property Naming - Boolean values normalised. v2.0:
hasLeadingIcon,hasTrailingActionandhasDescriptionusetrue/falserather thanyes/nostrings, and all three carry the correcthasverb prefix per §2. (C2 · Rename) C2 · Variant & Property Naming - Action slot added. v2.0: The trailing action is an
addonSLOTholding aButton - XSmallinstance — andaddonis the canonical name §4 gives for exactly this pattern. Consumers can swap the button without detaching. (Slot) C6 · Asset & Icon Quality - Description composed from the shared Subtext Message. v2.0: The description is a
contentSLOTcarrying a sharedSubtext Messageinstance, matching Text Area, Upload File and View Only Field. Copy changes propagate from one source. (Composition) C1 · Layer Structure & Naming - Size and Theme values aligned to the standard sets. v2.1:
Size = base | sm→MD | SM, matching §5'sXS · SM · MD · LG · XLscale and the values Amount Text Field and View Only Field use.Themeis nowDark | Lightin Title Case per §5. (C2 · Rename) C2 · Variant & Property Naming - Booleans confirmed as real Figma booleans. v2.1:
hasLeadingIcon,hasTrailingActionandhasDescriptionnow renderTrue/Falsecapitalised, matching every genuine Figma boolean elsewhere in the file. They map directly to SwiftBooland KotlinBoolean. (C2) C2 · Variant & Property Naming - Layer naming completed. v2.2:
container→ToastRow, the two siblingoffsetframes →LeadingIconandTextContent,text-container→TextGroup, and#content→Title. The two anonymous slots also gained names —Text-Slotfor the description andComponent-Slotfor the trailing action. (C1) C1 · Layer Structure & Naming - Pending placeholder replaced with a real icon. v2.2: The
icon-placeholderrectangle is gone.LeadingIconnow holds anInformationinstance from the icon library — chosen by the owner over a spinner, so Pending reads as an informational notice rather than an in-flight progress state. Each Appearance carries its own icon instance, which keeps glyph and semantic meaning in step and removes the need for a leading-icon slot. (C6 · Asset) C6 · Asset & Icon Quality - All variant values Title Cased. v2.3:
Appearance=pending→Pending, the last value the casing sweep had missed. Every one of the 22 variant names now conforms to §5 —Appearance = Default | Destructive | Pending,Theme = Dark | Light,Size = MD | SM, and threehas*booleans onTrue | False. (C2 · Rename) C2 · Variant & Property Naming - Dismiss and auto-duration contract documented. v2.4: Documented. Auto-hide — 4 seconds by default; 8 seconds when
hasTrailingAction=True, since the user needs time to read the message and reach the action.Appearance=Pendingdoes not auto-hide at all: it represents an in-flight operation and is replaced by a Default or Destructive toast when that operation resolves. Dismissal — a horizontal swipe in either direction dismisses. Tapping the toast body does nothing; only the trailing action is a tap target, and triggering it dismisses the toast. Stacking — one toast at a time; a new message replaces the current one rather than queueing behind it. Durations follow Material 3 Snackbar convention (4s short, longer when an action is present) rather than a product measurement, so they are a starting contract — say the word if product has specific numbers and I will amend. (C5 · Docs) C5 · Interaction State Coverage - Native mapping and a11y live region documented. v2.4: Documented. iOS — there is no system toast; build a custom view presented through a
ViewModifierover the root, anchored to the bottom safe area. Android — Material 3SnackbarwithSnackbarHostcovers the Default and Destructive appearances and mapshasTrailingActiononto its action slot, though the Pending appearance and the Theme axis need a custom composable. Accessibility — a transient message that is never focused must be announced, not merely rendered. On iOS post anAccessibilityNotification.Announcementwith the message text; on Android setModifier.semantics { liveRegion = LiveRegionMode.Polite }, raised toAssertiveforAppearance=Destructiveso an error interrupts rather than queues. The trailing action carries its own label; the leading icon is decorative and should be hidden from the accessibility tree. (C4 · A11y · Docs) C4 · Native Mappability
Add the popup message here
| Role | Element | Token | Value |
|---|---|---|---|
| Default · Dark | Background | — | #0A2757 |
| Default · Light | Background | — | #FFFFFF |
| Default · Dark | Border | — | #E5EBF4 |
| Default · Dark | Title | — | #FFFFFF |
| Default · Light | Title | — | #0A2757 |
| Default · Dark | Leading icon | — | #FFFFFF |
| Default · Light | Leading icon | — | #0A2757 |
| Default · Dark | Description | — | #F6F9FD @ 72% |
| Default · Light | Description | — | #6780A9 |
| Default · Dark | Action background | — | #FFFFFF |
| Default · Light | Action background | — | #005CE5 |
| Default · Dark | Action label | — | #005CE5 |
| Default · Light | Action label | — | #FFFFFF |
| Destructive · Dark | Background | — | #D61B2C |
| Destructive · Dark | Border | — | #F4C7C9 |
| Destructive · Dark | Title | — | #FFFFFF |
| Destructive · Dark | Leading icon | — | #FFFFFF |
| Destructive · Dark | Description | — | #F6F9FD @ 80% |
| Destructive · Dark | Action background | — | None |
| Destructive · Dark | Action label | — | #FFFFFF |
| Pending · Dark | Background | — | #0A2757 |
| Pending · Light | Background | — | #FFFFFF |
| Pending · Dark | Border | — | #E5EBF4 |
| Pending · Dark | Title | — | #FFFFFF |
| Pending · Light | Title | — | #0A2757 |
| Pending · Dark | Leading icon | — | #FFFFFF |
| Pending · Light | Leading icon | — | #0A2757 |
Add the popup message here
Add the popup message here
iOS — Swift Package Manager
.package(url: "https://github.com/AY-Org/eb-ds-ios", from: "1.0.0")
Android — Gradle (Kotlin DSL)
implementation("com.eastblue.ds:toast:1.0.0")
Import
import EastBlueDS import com.eastblue.ds.toast.*
Appearance, Theme, Size and the three has… booleans are all variant properties; 22 of the 48 combinations are built. Text-Slot (the Subtext Message) and Component-Slot (the Button - XSmall) are Figma Slots. Title is a text layer read from 4915:25142; its exposure as a text property is not readable with the plugin.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| Appearance — Default, Destructive, Pending | .ebAppearance(.default / .destructive / .pending) | appearance = EBToastAppearance.Default / Destructive / Pending |
| Theme — Dark, Light | .ebTheme(.dark / .light) | theme = EBToastTheme.Dark / Light |
| Size — MD, SM | .ebSize(.md / .sm) | size = EBToastSize.MD / SM |
| hasLeadingIcon — True, False | .ebLeadingIcon(false) to hide the Appearance icon | leadingIcon = false |
| hasTrailingAction — True, False | .ebAction("…") { } when True | action = EBToastAction("…") { } when True, else null |
| hasDescription — True, False | .ebDescription("…") when True | description = "…" when True, else null |
| Text-Slot (slot) | .ebDescription(_:) | description = "…" |
| Component-Slot (slot) | .ebAction(_:action:) | action = EBToastAction(…) |
| Title (text) | EBToast("…") | title = "…" |
EBToast("Add the popup message here")
.ebAppearance(.default)
.ebTheme(.dark)
.ebSize(.md) EBToast(
title = "Add the popup message here",
appearance = EBToastAppearance.Default,
theme = EBToastTheme.Dark,
size = EBToastSize.MD
) EBToast("Add the popup message here")
.ebAppearance(.destructive)
.ebTheme(.dark)
.ebSize(.md) EBToast(
title = "Add the popup message here",
appearance = EBToastAppearance.Destructive,
theme = EBToastTheme.Dark,
size = EBToastSize.MD
) EBToast("Add the popup message here")
.ebAppearance(.pending)
.ebTheme(.dark)
.ebSize(.md) EBToast(
title = "Add the popup message here",
appearance = EBToastAppearance.Pending,
theme = EBToastTheme.Dark,
size = EBToastSize.MD
) | Requirement | iOS | Android |
|---|---|---|
| Announcement | UIAccessibility.post(notification: .announcement, argument: title) when the toast appears; Destructive uses .layoutChanged so it interrupts. | LiveRegionMode.Polite on the toast; Assertive for Destructive. |
| Action reachable | The action is a Button inside the toast; keep the toast on screen while VoiceOver focus is on it. | TextButton with its own onClick; the toast does not auto-dismiss while it has focus. |
| Icon is decorative | .accessibilityHidden(true) on the leading icon — Appearance is said in the announcement ("Error: …"). | contentDescription = null on the icon; prefix the announcement with the appearance. |
| Duration | Auto-dismiss no sooner than 4 s; never auto-dismiss a toast with an action. | SnackbarDuration.Long; Indefinite when an action is present. |
| Contrast | White on #0A2757 is 13.9:1; white on #D61B2C is 5.0:1; #0A2757 on white is 13.9:1. | Same values — the toast colours are fixed, not themed. |
Do
Use Default for confirmations, Destructive for failures, Pending while something is still in flight.
Don't
Don’t use Pending as a warning — it has no Error semantics.
Do
Keep the title to one line at 312 px; add hasDescription only for the second fact.
Don't
Don’t wrap the title — the Size MD title is a single 14 px line.
Do
Use hasTrailingAction for one undo or retry; the button is the XSmall size.
Don't
Don’t put two actions in a toast — the set builds one Component-Slot.
Do
Use SM inside dense sheets; MD everywhere else.
Don't
Don’t combine SM with an action or description — the set does not build them.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Needs Refinement | ToastRow › LeadingIcon / TextContent › Title on 4915:25142. Two variants slip: 4915:25240 names its title #content and 4915:25232 names it LeadingIcon. |
| C2 | Variant & Property Naming | Ready | Appearance, Theme, Size with Title Case values; the three has… booleans are real True / False. |
| C3 | Token Coverage | Ready | Title → Primary/Label/Light/Small (MD), Primary/Multi-line Label/Light/Fine (SM), Primary/Multi-line Label/Small with an action or description. Colour variable bindings are not readable with the plugin — confirm in Dev Mode. |
| C4 | Native Mappability | Ready | Three enums, three booleans, two slots, one string — maps 1:1 onto EBToast. |
| C5 | Interaction State Coverage | Ready | Dismiss and auto-duration contract documented (v2.x); the action button takes its states from Button - XSmall. |
| C6 | Asset & Icon Quality | Needs Refinement | The Checkmark (Circular), Close and Information icons are icon-library instances that each carry a shape_full BOOLEAN_OPERATION — flatten them in the icon library. |
3 Appearance × 2 Theme × 2 Size × hasLeadingIcon × hasTrailingAction × hasDescription, authored sparse = 22 variants (Default 14, Destructive 6, Pending 4)
| Appearance | Built | Count |
|---|---|---|
| Default | Dark and Light; MD and SM; with and without icon; action and action + description at MD | 14 |
| Destructive | Dark MD and SM with and without icon; Light action and action + description at MD | 6 |
| Pending | Dark and Light; MD and SM; icon only | 4 |
View full Appearance × Theme × Size × booleans breakdown (22 rows)
| Appearance | Theme | Size | hasLeadingIcon | hasTrailingAction | hasDescription | Node | Dimensions |
|---|---|---|---|---|---|---|---|
| Default | Dark | MD | True | False | False | 4915:25142 | 312 × 38 |
| Default | Light | MD | True | False | False | 4915:25148 | 312 × 38 |
| Destructive | Dark | MD | True | False | False | 4915:25154 | 312 × 38 |
| Pending | Dark | MD | True | False | False | 4915:25160 | 312 × 38 |
| Pending | Light | MD | True | False | False | 4915:25166 | 312 × 38 |
| Destructive | Dark | MD | False | False | False | 4915:25172 | 312 × 38 |
| Default | Dark | MD | False | False | False | 4915:25175 | 312 × 38 |
| Default | Light | MD | False | False | False | 4915:25178 | 312 × 38 |
| Destructive | Dark | SM | True | False | False | 4915:25181 | 312 × 38 |
| Default | Dark | SM | True | False | False | 4915:25186 | 312 × 38 |
| Default | Light | SM | True | False | False | 4915:25192 | 312 × 38 |
| Destructive | Dark | SM | False | False | False | 4915:25197 | 312 × 38 |
| Pending | Dark | SM | True | False | False | 4915:25200 | 312 × 38 |
| Pending | Light | SM | True | False | False | 4915:25205 | 312 × 38 |
| Default | Dark | SM | False | False | False | 4915:25210 | 312 × 38 |
| Default | Light | SM | False | False | False | 4915:25213 | 312 × 38 |
| Default | Dark | MD | False | True | True | 4915:25216 | 312 × 59 |
| Default | Light | MD | False | True | True | 4915:25224 | 312 × 59 |
| Destructive | Light | MD | False | True | True | 4915:25232 | 312 × 59 |
| Default | Dark | MD | False | True | False | 4915:25240 | 312 × 41 |
| Default | Light | MD | False | True | False | 4915:25245 | 312 × 41 |
| Destructive | Light | MD | False | True | False | 4915:25250 | 312 × 41 |
Appearance — Default, Destructive, Pending — with Theme, Size and the three booleans as panel controls; colours, the three Title styles and the 38 / 41 / 59 heights read off 4915:25142 and its siblings against the exports; icon glyphs from get_svg. Padding, gap, alignment and colour tokens are not readable with the plugin and stay —. Combinations Figma does not build are composed from the same parts.#content, 4915:25232 names it LeadingIcon; the three Appearance icons contain shape_full. Filed from the scorecard for the Component Review.theme axis split into Appearance and Theme; Large Label replaced by a real Size axis; Boolean values normalised; Action slot added; Description composed from the shared Subtext Message; Size and Theme values aligned to the standard sets; Booleans confirmed as real Figma booleans; Layer naming completed; Pending placeholder replaced with a real icon; All variant values Title Cased; Dismiss and auto-duration contract documented; Native mapping and a11y live region documented.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