A canonical surface for ads, promos, and sponsored placements — supports banner, promo, and hero size families with a single content slot.
Each of the three size families has a canonical home in the app: banners inline within transaction flows, promos on the dashboard tile grid, and heroes full-width on home or category surfaces.
content slot — no hardcoded rasters, no placeholder "replace me" assets.<family>-<size>. Content and state are orthogonal axes. Caption typography uses the same DS text style across all promo and hero sizes.hero-md), into list rows for inline banners, and into the dashboard grid for promo tiles. No sibling "Ad Carousel" or "Ad Group" needed.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| banner-sm | N/A | N/A | GADBannerView | AdView |
| banner-lg | N/A | N/A | GADBannerView | AdView |
| banner-mrec | N/A | N/A | GADBannerView | AdView |
| promo-sm | N/A | N/A | custom EBAdSpace | custom composable |
| promo-md | N/A | N/A | custom EBAdSpace | custom composable |
| hero-sm | N/A | N/A | custom EBAdSpace | custom composable |
| hero-md | N/A | N/A | custom EBAdSpace | custom composable |
| isLoading | N/A | N/A | RedactedShape | shimmer composable |
- Adoption — deprecate and delete the three legacy components. Swap all Figma usages of Ads On Receipt, Ad Space - Group - Large, and Dashboard Promo Cards to
Ad Spacewith the matchingsize. Delete the old components plus the Placeholder Banner and Promo Cards Images asset libraries once zero-usage is confirmed. Family - Asset pipeline — ship promo and hero imagery as product assets, not DS assets. Product teams export 1×/2×/3× image assets from their marketing pipeline and pass them into the
contentslot. The DS ships the container, typography, radius, and skeleton only — no imagery. This is what retires the 8-variant Placeholder Banner and 6-variant Promo Cards Images asset sets. Asset - Carousel composition — no "Ad Carousel" component. Multi-ad rails (e.g. a horizontal row of
hero-md) are authored as<Carousel><AdSpace size="hero-md" /><AdSpace size="hero-md" />…</Carousel>. The oldcarousel=yespseudo-variant is retired because it was a static 3-card Figma preview, not a runtime carousel. Document this composition pattern in the Carousel and Ad Space guidelines. Composition - Tokens — propose a
main/ad-space/color/*namespace. Shipmain/ad-space/color/surface(card background),main/ad-space/color/caption(caption text), andmain/ad-space/color/loading-skeleton(shimmer fill). Today the legacy components reuse genericbg/color-bg-mainandbg/color-bg-strong; a dedicated namespace makes cross-family theming (dark mode, partner-branded surfaces) tractable. Token - Telemetry — bake impression and tap tracking into the native component. The iOS
EBAdSpaceview and Android composable should emitonImpression(50% visible for ≥1s) andonTapcallbacks. AdMob-backedbanner-*sizes get this for free;promo-*andhero-*need product-side reporting. Document the contract in the Code tab so consumer teams wire analytics consistently. Docs - A11y — treat ads as labeled buttons, not decorative images. Every Ad Space is tappable and leads somewhere; the whole surface must expose a single accessibility label (caption + "Advertisement" trait). Banner family should announce "Advertisement" as its accessibility hint per App Store / Play Store disclosure norms. A11y
- Caption — optional, typography-constrained, never mandatory. Promo and hero families accept an optional caption string; banner family has none (AdMob renders its own chrome). Caption uses
Secondary/Bold/Caption— one line for promo-sm, up to two lines for promo-md / hero-*. Empty caption hides the label slot; it does not reserve space. Slot - See siblings: Carousel Card and the broader Carousel family — Ad Space heroes wrap inside the DS Carousel; keep skeleton treatment aligned across card primitives. Family
IAB-standard banner sizes driven by AdMob. The DS provides a fixed-dimension surface; the ad SDK renders the creative in the content slot.
Surface tile that hosts the ad creative. Skeleton + label colors are the only DS-owned tokens.
| Role | Token | Default |
|---|---|---|
| Surface | ad-space/color/surface | #FFFFFF |
| Loading skeleton | ad-space/color/loading-skeleton | #EEF2F9 |
| "Ad" marker | text/color-text-subtle | #6780A9 |
Product-owned dashboard tiles. Image fills the upper portion; an optional caption sits beneath.
Same DS-owned palette as the Banner; promo creative fills the surface.
| Role | Token | Default |
|---|---|---|
| Surface | ad-space/color/surface | #FFFFFF |
| Loading skeleton | ad-space/color/loading-skeleton | #EEF2F9 |
| "Ad" marker | text/color-text-subtle | #6780A9 |
Full-width hero banners for home and category surfaces. <code>hero-md</code> is the canonical item inside a DS Carousel for multi-ad rails.
Same DS-owned palette as the Banner; hero creative fills the larger surface.
| Role | Token | Default |
|---|---|---|
| Surface | ad-space/color/surface | #FFFFFF |
| Loading skeleton | ad-space/color/loading-skeleton | #EEF2F9 |
| "Ad" marker | text/color-text-subtle | #6780A9 |
| Figma Property | SwiftUI | Compose |
|---|---|---|
size: banner-sm | banner-lg | banner-mrec | promo-sm | promo-md | hero-sm | hero-md | size: EBAdSpaceSize | size: EBAdSpaceSize |
isLoading: Boolean | isLoading: Bool | isLoading: Boolean |
content: Frame (slot) | content: () -> AnyView | content: @Composable () -> Unit |
caption: String? | caption: String? | caption: String? |
| (not modeled) | onImpression: (() -> Void)? | onImpression: (() -> Unit)? |
| (not modeled) | onTap: (() -> Void)? | onClick: (() -> Unit)? |
| Requirement | iOS | Android |
|---|---|---|
| Ad disclosure | Add .accessibilityHint("Advertisement") so VoiceOver announces the trait alongside the caption. | Set Modifier.semantics { contentDescription = "Advertisement, ${caption}" }. |
| Ad as a single button | Whole surface wrapped in Button { onTap() } with accessibilityElement(children: .combine). | Modifier.clickable { onClick() }.semantics(mergeDescendants = true). |
| Image alt | Banner content is decorative from a11y's perspective — caption carries the meaning. | contentDescription = null on the inner image; caption carries meaning. |
| Min touch target | All seven sizes exceed 44 pt height ✓ | All seven sizes exceed 48 dp height ✓ |
| Loading state | accessibilityLabel("Loading advertisement") on the skeleton; suppress individual shimmer announcements. | contentDescription = "Loading advertisement" on the skeleton container. |
| Focus ring | .focused() → 2 px outline using border/focus token for iPad keyboard nav. | Modifier.focusable() + border in border/focus. |
| AdMob compliance | Google Mobile Ads SDK handles its own a11y metadata for banner-* — do not override. | Google Mobile Ads SDK handles its own a11y metadata for banner-* — do not override. |
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Single Ad Space component, semantic frame naming (surface, content, caption). No legacy "Group"/"Large"/"hifi"/"midfi" baggage. |
| C2 | Variant & Property Naming | Ready | One size enum, one isLoading boolean, one caption string. Content and state axes are orthogonal. |
| C3 | Token Coverage | Ready | Proposes a main/ad-space/color/* namespace (surface, caption, loading-skeleton) plus radius/radius-1|2|3 per family. All typography bound to DS text styles. |
| C4 | Native Mappability | Ready | banner-* maps 1:1 to GADBannerView / AdView. promo-* / hero-* map to EBAdSpace (ZStack/Box image + optional caption). Carousel composition reuses EBCarousel. |
| C5 | Interaction State Coverage | Ready | Default, pressed, focused, disabled, and loading states all modeled. Loading is an orthogonal boolean, not a variant. |
| C6 | Asset & Icon Quality | Ready | No DS-side assets. Content flows through the slot; placeholder libraries are retired. |
| C7 | Code Connect Linkability | Ready | Clean single-component mapping: EBAdSpace with enum size, boolean isLoading, slot content, string caption. 1:1 param mapping to both platforms. |
| Aspect | Status | Notes |
|---|---|---|
| Property naming | Ready | Clean: size (7), isLoading (bool), caption (string), content (slot). |
| Token coverage | Ready | main/ad-space/color/* namespace proposed; all colors and radii bound. |
| State coverage | Ready | Default + loading modeled; pressed/focused handled at the native layer. |
| Native component file | Needs Refinement | EBAdSpace.swift / EBAdSpace.kt not yet published — Planned API. |
| AdMob SDK wiring | Ready | Google Mobile Ads SPM + Gradle dependencies documented in Installation. |
One size enum with 7 values across 3 size families. isLoading is an orthogonal boolean, not a variant (would otherwise double the count to 14). caption is optional input, not a variant.
| Family | Size value | Dimensions | Aspect | Corner radius | Content slot |
|---|---|---|---|---|---|
| banner | banner-sm | 320 × 50 | 32:5 | 4 | AdMob · IAB Mobile Banner |
| banner | banner-lg | 320 × 100 | 16:5 | 4 | AdMob · IAB Large Banner |
| banner | banner-mrec | 300 × 250 | 6:5 | 4 | AdMob · IAB MREC |
| promo | promo-sm | 131 × 126 | 4:3 (image) | 8 | Image + 1-line caption |
| promo | promo-md | 224 × 200 | 3:2 (image) | 8 | Image + up to 2-line caption |
| hero | hero-sm | 296 × 174 | 17:10 | 12 | Image + optional caption overlay |
| hero | hero-md | 336 × 174 | 15:8 | 12 | Image + caption; default Carousel item |
Ad Space with a 7-value size enum grouped into three families: banner (IAB / AdMob), promo (dashboard tile), hero (full-width).
Initialtype=hifi | midfi was a placeholder-authoring crutch, not a runtime variant. Replaced with an orthogonal isLoading boolean. Content flows through the content slot regardless of state.
Resolvedcarousel=yes pseudo-variant retired — The legacy "carousel preview" variant was a static 3-card Figma layout, not a runtime carousel. Multi-ad rails are now authored by composing multiple AdSpace size="hero-md" instances inside the DS EBCarousel.
Resolvedcontent slot; product teams provide imagery from their own asset pipeline.
Resolvedbanner-* sizes map to GADBannerView / AdView. promo-* / hero-* map to a single EBAdSpace view/composable. Clean enum, boolean, string, slot signatures ready for Code Connect CLI registration.
Resolvedmain/ad-space/color/surface, main/ad-space/color/caption, main/ad-space/color/loading-skeleton. Replaces ad-hoc use of generic surface tokens across the legacy trio.
InitialonImpression (50% visible ≥1s) and onTap. AdMob-backed banner-* inherits the SDK's tracking; product-owned promo-* / hero-* wire consumer analytics via the callbacks.
Initial