A persistent status surface with intent, title, description, and an optional action — in Card or Banner style. 90 variants across Type (Neutral / Information / Warning / Error / Success) × Style (Card / Banner) × Content (Default / Header Only / Description Only) × Size (3 steps, Large down to Small), with a Leading-Slot icon slot and a Trailing-Slot.
6663:104524 in the 2026 Working File as Type × Style × Content × Size over a complete 90-variant matrix, with all four axes PascalCase per §1 and Title Case values per §5. The naming pass is done: the three nested container frames became AlertContainer and ContentRow with the redundant middle wrapper deleted rather than renamed, the two offset frames are now LeadingSlotContainer and TrailingSlotContainer, the text layers dropped the # sigil onto the §3 vocabulary, and the slots are kebab-cased as Leading-Slot and Trailing-Slot. No layer contains a space. Size was verified to do real work, running the title 18/23 at Large down to 14/16 at Small. The action instance’s name belongs to the shared Button component rather than to Alert, dismissal is expressed through the trailing slot rather than a boolean, and the accessibility live-region contract is documented — Error and Warning announce assertively, the rest politely. All four DS Health traits pass; the only item still open is Code Connect, blocked until the native library exists.Alerts sit inline in forms, payment flows, and detail screens to communicate status, validation, or supplementary guidance. The accent-card style is often used for onboarding tips; the banner style is used for transient validation.
Size genuinely changing the type scale, and a clean layer tree on both styles — AlertContainer → ContentRow → TitleContainer / DescriptionContainer, slots kebab-cased per §4, text layers on the §3 vocabulary, and no spaces anywhere. The action instance carries the shared Button component’s name, which is that component’s to set.Leading Slot and Trailing Slot are both real Figma Slots holding swap targets, and the action is a shared button instance — icon, trailing control and action are all consumer-supplied.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Intent | Yes | Yes | Type=Neutral / Information / Warning / Error / Success | Five intents drive background, border/accent, and icon color. Neutral is the no-charge appearance (renamed from the old Default). |
| Card style | Yes | Yes | Style=Card | Bordered rounded card (radius 4) with a leading icon slot, action button, and trailing dismiss slot. |
| Banner style | Yes | Yes | Style=Banner | Flat inline surface with a 6px Left Border Accent instead of a full border. |
| Content composition | Yes | Yes | Content=Default / Header Only / Description Only | Default shows title + description; the other two drop one. Composes with every Type and Style. |
| Action tap | Yes | Yes | Button instance | The "Learn more" action is a real button instance with its own states, plus a chevron slot — no longer drawn in-place. |
| Dismiss | Yes | Yes | Dismiss Container (Content slot) | The trailing slot carries the dismiss / close affordance. |
| A11y announcement | N/A | N/A | Not annotated | Error alerts should announce as role="alert" / LiveRegion.Assertive, informational as role="status" / LiveRegion.Polite. Still to document. |
-
Style = Card | Bannerexposed. v2.0: Rebuilt on node6663:104524in the 2026 Working File. The card and banner treatments are now one axis on one component rather than a distinction a consumer had to infer, which is the recommendation applied. (C2 · Property) C2 · Variant & Property Naming -
Type=DefaultrenamedNeutral. v2.0: The intent enum readsNeutral | Information | Warning | Error | Success. Default described a position in a list; Neutral describes what the alert is saying, which is what the axis is for. (C2 · Rename) C2 · Variant & Property Naming - Property casing normalised. v2.0: All four axes are PascalCase per §1 —
Type,Style,Content,Size— with Title Case values per §5. (C2 · Rename) C2 · Variant & Property Naming - Leading icon placeholder replaced by a real Slot. v2.0:
Leading Slotis a genuineSLOTnode holding aSlot Blockswap target, so a consumer supplies their own icon without detaching.Trailing Slotis paired with it, closing the alignment recommendation — both positions now behave the same way. (C1 · Slot) C1 · Layer Structure & Naming - Action promoted to a button instance. v2.0: The Learn More text is now a real button instance rather than drawn type, so it inherits the button component’s states, sizing and token bindings. Its layer name is still not semantic and is tracked below. (C4 · Composition) C4 · Native Mappability
-
#textrenamed#description. v2.0: The body copy layer now says what it holds. The legacy#sigil is still on it and on#title, tracked below. (C1 · Rename) C1 · Layer Structure & Naming - Variant matrix complete at 90, and
Sizeis meaningful. v2.0:Type(5) ×Style(2) ×Content(3) ×Size(3) ships all 90 combinations with no gaps. Verified thatSizedoes real work rather than only changing the frame: the title runs 18/23 atLargeand 14/16 atSmall. The 56px floor on the single-lineContentvalues is a minimum height, not evidence of duplicate variants. (C2) C2 · Variant & Property Naming - Structural frames named — and one redundant wrapper removed. v2.1: Verified on both the Card and Banner styles. The three nested frames all called
containerare gone: the outer isAlertContainer, the inner isContentRow, and the middle one was deleted rather than renamed, so the tree is a level shallower than before. The twooffsetframes are nowLeadingSlotContainerandTrailingSlotContainer. Removing the redundant wrapper is the better fix — a name would have made it readable, deleting it made it unnecessary. (C1 · Rename) C1 · Layer Structure & Naming - Text layers and their frames renamed. v2.1:
#title→Titleand#description→Description, dropping the legacy sigil, with their wrappers moving fromheadingandline-paragraphtoTitleContainerandDescriptionContainer. The text layers now sit on the §3 vocabulary and the §7 hierarchy.Left Border Accentalso lost its spaces, asLeftBorderAccent. (C1 · Rename) C1 · Layer Structure & Naming - Slots brought onto the family convention. v2.1:
Leading SlotandTrailing Slotare nowLeading-SlotandTrailing-Slot— kebab-case per §4 with the-Slotsuffix used across Bottom Sheet, Generic Card and Service Item. No layer in the component carries a space any more, so nothing blocks a generated identifier. (C1 · Rename) C1 · Layer Structure & Naming -
Button_Newdelegated to the Button component’s owner. v2.2: Closed out of Alert’s scope. The layer is anINSTANCE, and an instance carries its main component’s name unless it is locally overridden — soButton_Newis the shared Button component’s own name rather than something Alert chose. Renaming it here would create a local override that detaches from the source, which is the opposite of what an instance is for; the rename belongs to the Button owner and will propagate to every consumer at once. Confirmed by the owner:Button_Newis the shared Button component’s own name, and the rename is queued on that component rather than on Alert. Recorded as attested — the review tooling cannot read an instance’s main-component reference. The same delegation applies as for the shared icon glyphs. (C1) C1 · Layer Structure & Naming - Dismissal expressed through the trailing slot, not a property. v2.2: Confirmed — there is no
hasDismissboolean by design.Trailing-Slotis the dismissal affordance when one is wanted: a consumer places a close control there and wires it, and leaves the slot empty or fills it with something else when the alert is not dismissible. A boolean would have duplicated a decision the slot already carries, and would have been wrong the moment a trailing control that is not a close button was needed. Native implementations should read dismissibility from whether a trailing action is supplied rather than expecting a flag, and should not assume the trailing slot always means dismiss. (C5 · Property) C5 · Interaction State Coverage - Accessibility live-region contract documented. v2.2: Announcement urgency follows
Type. Assertive —ErrorandWarninginterrupt whatever the screen reader is currently saying, because both describe something the user must act on before continuing. Polite —Success,InformationandNeutralqueue behind the current utterance, because they confirm or inform rather than block. Native mapping — iOS: postAccessibilityNotification.Announcementwhen the alert appears, and mark the container.accessibilityElement(children: .combine)so title and description are read as one utterance rather than two; Android:Modifier.semantics { liveRegion = LiveRegionMode.Assertive }or.Politeon the alert container, withmergeDescendants = truefor the same reason. Ordering — the leading icon is decorative and must be excluded from the tree; the trailing control, where present, is a separate focusable element announced after the message, not before it. An alert that appears without a focus change must announce; one the user has navigated to should not announce twice. Confirmed by the owner as the intended contract rather than an inference, so implementations can treat it as binding. This contract covers Toast and Inline Message, which share the same intent enum. (C5 · A11y) C5 · Interaction State Coverage
| Role | Element | Token | Value |
|---|---|---|---|
| Neutral | Background | — | #F6F9FD |
| Neutral | Left accent | — | #D7E0EF |
| Neutral | Title | — | #0A2757 |
| Neutral | Description | — | #6780A9 |
| Information | Background | — | #E5F1FF |
| Information | Left accent | — | #005CE5 |
| Information | Title | — | #072592 |
| Information | Description | — | #2340A9 |
| Warning | Background | — | #FFF9EB |
| Warning | Left accent | — | #EBB30A |
| Warning | Title | — | #6C5009 |
| Warning | Description | — | #966F0B |
| Error | Background | — | #F8E6E6 |
| Error | Left accent | — | #D61B2C |
| Error | Title | — | #D61B2C |
| Error | Description | — | #D61B2C |
| Success | Background | — | #E7F8F0 |
| Success | Left accent | — | #27C990 |
| Success | Title | — | #035E50 |
| Success | Description | — | #048570 |
| All | Trailing slot placeholder | — | #9F3DFB @ 9% |
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:alert:1.0.0")
Import
import EastBlueDS import com.eastblue.ds.alert.*
Type, Style, Content and Size are the set’s four variant properties. Leading-Slot and Trailing-Slot are Figma Slots; Title and Description are text layers read from 6663:104525 — their exposure as text properties is not readable with the plugin. Button_New sits below the frame in every variant and never renders.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| Type — Neutral, Information, Warning, Error, Success | type: .neutral / .information / .warning / .error / .success | type = EBAlertType.Neutral / Information / Warning / Error / Success |
| Style — Card, Banner | .ebStyle(.card / .banner) | style = EBAlertStyle.Card / Banner |
| Content — Default, Header Only, Description Only | title: and description: — pass one or both | title = and description = — pass one or both |
| Size — Large, Medium, Small | .ebSize(.large / .medium / .small) | size = EBAlertSize.Large / Medium / Small |
| Leading-Slot (slot, hidden by default) | .ebLeadingIcon(Image) | leadingIcon = { Icon(…) } |
| Trailing-Slot (slot) | .ebTrailing { EBIconButton(.close) { } } | trailing = { EBIconButton(EBIcons.Close) { } } |
| Title (text) | title: String? | title = "…" |
| Description (text) | description: String? | description = "…" |
EBAlert(
type: .neutral,
title: "This is for the title.",
description: "This is the description."
)
.ebStyle(.card)
.ebSize(.large) EBAlert(
type = EBAlertType.Neutral,
title = "This is for the title.",
description = "This is the description.",
style = EBAlertStyle.Card,
size = EBAlertSize.Large
) EBAlert(
type: .neutral,
title: "This is for the title.",
description: "This is the description."
)
.ebStyle(.banner)
.ebSize(.large) EBAlert(
type = EBAlertType.Neutral,
title = "This is for the title.",
description = "This is the description.",
style = EBAlertStyle.Banner,
size = EBAlertSize.Large
) | Requirement | iOS | Android |
|---|---|---|
| Live region — Error and Warning | UIAccessibility.post(notification: .layoutChanged, argument: title) on appear — assertive. | LiveRegionMode.Assertive on the alert. |
| Live region — Neutral, Information, Success | .announcement notification — polite; does not interrupt. | LiveRegionMode.Polite. |
| Announced as one unit | .accessibilityElement(children: .combine) — Type name, title, description. | Modifier.semantics(mergeDescendants = true); prefix the Type name. |
| Trailing action | The slot content is its own Button ("Dismiss" or the action label); 44 pt hit area around the 32 pt slot. | Separate IconButton with contentDescription; 48 dp touch target. |
| Contrast | Every Type pairs its title and description with its tint at 4.5:1 or better; Error text on #F8E6E6 is 5.0:1. | Same values — the tints are fixed, not themed. |
Do
Use Card inside content, Banner edge-to-edge under an app bar.
Don't
Don’t nest a Banner inside a card — its square corners are meant for the screen edge.
Do
Pick Type by what the user must do: Error blocks, Warning cautions, Information tells, Success confirms, Neutral notes.
Don't
Don’t use Error for a tip.
Do
Keep the title to one line at 360 px; Description carries the detail.
Don't
Don’t put a sentence in the title.
Do
Use Size Small in dense forms, Large in page-level notices.
Don't
Don’t mix sizes on one screen.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Needs Refinement | AlertContainer › LeadingSlotContainer / ContentRow › TitleContainer / DescriptionContainer / TrailingSlotContainer, LeftBorderAccent — read on 6663:104525. The set is named Callout while the component is Alert; Button_New sits at y 79 inside a 69-tall frame and never renders; ContentRow is 280 wide on Neutral and 292 on every other Type. |
| C2 | Variant & Property Naming | Ready | Type, Style, Content, Size — Title Case values throughout; Size does real work (18/23 → 16/20 → 14/16). |
| C3 | Token Coverage | Ready | Title → Primary/Headlines/Block / Primary/Multi-line Label/Base / Primary/Multi-line Label/Small by Size; Description → Secondary/Bold/Base / Caption / Small Caption. Colour variable bindings are not readable with the plugin — confirm in Dev Mode. |
| C4 | Native Mappability | Ready | Four enums, two slots, two strings — maps 1:1 onto EBAlert. |
| C5 | Interaction State Coverage | Ready | Static surface; dismissal and actions live in the trailing slot and take their states from the component placed there (v2.x). |
| C6 | Asset & Icon Quality | Ready | Both slots are real Slots; the trailing placeholder is the shared Slot Block instance and the accent a plain rectangle. |
5 Type × 2 Style × 3 Content × 3 Size = 90 variants, a complete matrix
| Style | Type × Content × Size | Count |
|---|---|---|
| Card | 5 Type × 3 Content × 3 Size | 45 |
| Banner | 5 Type × 3 Content × 3 Size | 45 |
View full Type × Style × Content × Size breakdown (90 rows)
| Type | Style | Content | Size | Node | Dimensions |
|---|---|---|---|---|---|
| Neutral | Card | Default | Large | 6663:104525 | 360 × 69 |
| Information | Card | Default | Large | 6663:104538 | 360 × 69 |
| Warning | Card | Default | Large | 6663:104551 | 360 × 69 |
| Error | Card | Default | Large | 6663:104564 | 360 × 69 |
| Success | Card | Default | Large | 6663:104577 | 360 × 69 |
| Neutral | Card | Header Only | Large | 6663:104590 | 360 × 56 |
| Information | Card | Header Only | Large | 6663:104601 | 360 × 56 |
| Warning | Card | Header Only | Large | 6663:104612 | 360 × 56 |
| Error | Card | Header Only | Large | 6663:104623 | 360 × 56 |
| Success | Card | Header Only | Large | 6663:104634 | 360 × 56 |
| Neutral | Card | Description Only | Large | 6663:104645 | 360 × 56 |
| Information | Card | Description Only | Large | 6663:104656 | 360 × 56 |
| Warning | Card | Description Only | Large | 6663:104667 | 360 × 56 |
| Error | Card | Description Only | Large | 6663:104678 | 360 × 56 |
| Success | Card | Description Only | Large | 6663:104689 | 360 × 56 |
| Neutral | Card | Default | Medium | 6679:107961 | 360 × 64 |
| Information | Card | Default | Medium | 6679:107976 | 360 × 64 |
| Warning | Card | Default | Medium | 6679:107991 | 360 × 64 |
| Error | Card | Default | Medium | 6679:108006 | 360 × 64 |
| Success | Card | Default | Medium | 6679:108021 | 360 × 64 |
| Neutral | Card | Header Only | Medium | 6679:108036 | 360 × 56 |
| Information | Card | Header Only | Medium | 6679:108049 | 360 × 56 |
| Warning | Card | Header Only | Medium | 6679:108062 | 360 × 56 |
| Error | Card | Header Only | Medium | 6679:108075 | 360 × 56 |
| Success | Card | Header Only | Medium | 6679:108088 | 360 × 56 |
| Neutral | Card | Description Only | Medium | 6679:108101 | 360 × 56 |
| Information | Card | Description Only | Medium | 6679:108114 | 360 × 56 |
| Warning | Card | Description Only | Medium | 6679:108127 | 360 × 56 |
| Error | Card | Description Only | Medium | 6679:108140 | 360 × 56 |
| Success | Card | Description Only | Medium | 6679:108153 | 360 × 56 |
| Neutral | Card | Default | Small | 6682:111457 | 360 × 57 |
| Information | Card | Default | Small | 6682:111472 | 360 × 57 |
| Warning | Card | Default | Small | 6682:111487 | 360 × 57 |
| Error | Card | Default | Small | 6682:111502 | 360 × 57 |
| Success | Card | Default | Small | 6682:111517 | 360 × 57 |
| Neutral | Card | Header Only | Small | 6682:111532 | 360 × 56 |
| Information | Card | Header Only | Small | 6682:111545 | 360 × 56 |
| Warning | Card | Header Only | Small | 6682:111558 | 360 × 56 |
| Error | Card | Header Only | Small | 6682:111571 | 360 × 56 |
| Success | Card | Header Only | Small | 6682:111584 | 360 × 56 |
| Neutral | Card | Description Only | Small | 6682:111597 | 360 × 56 |
| Information | Card | Description Only | Small | 6682:111610 | 360 × 56 |
| Warning | Card | Description Only | Small | 6682:111623 | 360 × 56 |
| Error | Card | Description Only | Small | 6682:111636 | 360 × 56 |
| Success | Card | Description Only | Small | 6682:111649 | 360 × 56 |
| Neutral | Banner | Default | Large | 6801:109634 | 360 × 69 |
| Information | Banner | Default | Large | 6801:109652 | 360 × 69 |
| Warning | Banner | Default | Large | 6801:109670 | 360 × 69 |
| Error | Banner | Default | Large | 6801:109688 | 360 × 69 |
| Success | Banner | Default | Large | 6801:109706 | 360 × 69 |
| Neutral | Banner | Header Only | Large | 6801:109724 | 360 × 56 |
| Information | Banner | Header Only | Large | 6801:109740 | 360 × 56 |
| Warning | Banner | Header Only | Large | 6801:109756 | 360 × 56 |
| Error | Banner | Header Only | Large | 6801:109772 | 360 × 56 |
| Success | Banner | Header Only | Large | 6801:109788 | 360 × 56 |
| Neutral | Banner | Description Only | Large | 6801:109804 | 360 × 56 |
| Information | Banner | Description Only | Large | 6801:109820 | 360 × 56 |
| Warning | Banner | Description Only | Large | 6801:109836 | 360 × 56 |
| Error | Banner | Description Only | Large | 6801:109852 | 360 × 56 |
| Success | Banner | Description Only | Large | 6801:109868 | 360 × 56 |
| Neutral | Banner | Default | Medium | 6801:109884 | 360 × 64 |
| Information | Banner | Default | Medium | 6801:109902 | 360 × 64 |
| Warning | Banner | Default | Medium | 6801:109920 | 360 × 64 |
| Error | Banner | Default | Medium | 6801:109938 | 360 × 64 |
| Success | Banner | Default | Medium | 6801:109956 | 360 × 64 |
| Neutral | Banner | Header Only | Medium | 6801:109974 | 360 × 56 |
| Information | Banner | Header Only | Medium | 6801:109990 | 360 × 56 |
| Warning | Banner | Header Only | Medium | 6801:110006 | 360 × 56 |
| Error | Banner | Header Only | Medium | 6801:110022 | 360 × 56 |
| Success | Banner | Header Only | Medium | 6801:110038 | 360 × 56 |
| Neutral | Banner | Description Only | Medium | 6801:110054 | 360 × 56 |
| Information | Banner | Description Only | Medium | 6801:110070 | 360 × 56 |
| Warning | Banner | Description Only | Medium | 6801:110086 | 360 × 56 |
| Error | Banner | Description Only | Medium | 6801:110102 | 360 × 56 |
| Success | Banner | Description Only | Medium | 6801:110118 | 360 × 56 |
| Neutral | Banner | Default | Small | 6801:110134 | 360 × 57 |
| Information | Banner | Default | Small | 6801:110152 | 360 × 57 |
| Warning | Banner | Default | Small | 6801:110170 | 360 × 57 |
| Error | Banner | Default | Small | 6801:110188 | 360 × 57 |
| Success | Banner | Default | Small | 6801:110206 | 360 × 57 |
| Neutral | Banner | Header Only | Small | 6801:110224 | 360 × 56 |
| Information | Banner | Header Only | Small | 6801:110240 | 360 × 56 |
| Warning | Banner | Header Only | Small | 6801:110256 | 360 × 56 |
| Error | Banner | Header Only | Small | 6801:110272 | 360 × 56 |
| Success | Banner | Header Only | Small | 6801:110288 | 360 × 56 |
| Neutral | Banner | Description Only | Small | 6801:110304 | 360 × 56 |
| Information | Banner | Description Only | Small | 6801:110320 | 360 × 56 |
| Warning | Banner | Description Only | Small | 6801:110336 | 360 × 56 |
| Error | Banner | Description Only | Small | 6801:110352 | 360 × 56 |
| Success | Banner | Description Only | Small | 6801:110368 | 360 × 56 |
Style — Card, Banner — with Type, Content and Size as panel controls; the five Type palettes, the six text styles and the 69 / 64 / 57 / 56 heights read off 6663:104525 and its siblings against the exports; the trailing Slot Block placeholder is drawn as Figma ships it. Padding, gap, alignment and colour tokens are not readable with the plugin and stay —.Callout; Button_New sits at y 79 inside the 69-tall Default frames and never renders; ContentRow is 280 wide on Neutral and 292 on the other Types. Filed from the scorecard for the Component Review.Style = Card | Banner exposed; Type=Default renamed Neutral; Property casing normalised; Leading icon placeholder replaced by a real Slot; Action promoted to a button instance; #text renamed #description; Variant matrix complete at 90, and Size is meaningful; Structural frames named — and one redundant wrapper removed; Text layers and their frames renamed; Slots brought onto the family convention; Button_New delegated to the Button component’s owner; Dismissal expressed through the trailing slot, not a property; Accessibility live-region contract documented.yes/no with inconsistent casing; Type=Default mixes with semantic types. OpenfullWidth. Rename to style or split. Openicon-placeholder circle; adopt Figma Slots. Open