A card hero introducing a transaction or recipient — avatar, title, separator, label-value row and description, on a brand or default surface.
4368:12856 as Detail Hero, with Surface = Brand | Default and the email = yes | no boolean retired. Layer naming is complete across both variants — Title, SenderDetails, Label, Value and Description, every one matching between surfaces so each exposes as a single text property. The avatar placeholder is a deliberate swap target, the single metadata row is the intended scope, the spacer instances are a system-wide annotation convention, the typeface split against Page Banner is a decision rather than drift, and the hero is static by design. It stays filed with the header family because that is where designers look for it, even though by anatomy it is a card hero. All four DS Health traits pass; the only item still open is Code Connect, blocked until the native library exists.Detail Hero appears at the top of transaction detail screens and recipient profile cards — introducing the person or transaction below the app bar.
email boolean removed the transaction-specific coupling, and the single metadata row is confirmed as the intended scope rather than a limitation.Placeholder is a deliberate swap target for consumer content rather than an unfinished avatar, so nothing external is required to render.Surface axis and brand fill, and renamed onto the §3 vocabulary — Title · Label · Value · Description, identical in both variants, with the duplicate #text and the legacy # prefix both gone.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default (Brand) | Yes | Yes | Surface=Brand | White title and description on the brand surface. |
| Default (Default) | Yes | Yes | Surface=Default | Dark title and description on the default white surface. |
| Pressed / Disabled | N/A | N/A | — | Static — no interactive states. |
- Renamed to Detail Hero and moved out of the Header family. v2.0: Rebuilt on node
4368:12856in the 2026 Working File. The previous assessment’s central call — that this is not a header — is settled: it has no navigation role and no title-only scope, and its anatomy is a card hero (avatar, title, separator, label-value, description). The name now says so, which also frees it from being read as a sibling of the three components that genuinely are headers. (C1 · Rename) C1 · Layer Structure & Naming -
Surface = Brand | Defaultexposed. v2.0: The same axis Page Banner and Brand App Bar landed on, so all three read consistently, and a detail hero on a settings screen can use the default surface instead of being forced onto brand blue. PascalCase per §1, Title Case values per §5, and the brand fill#1972F9matches Page Banner’s exactly. (C2 · Property) C2 · Variant & Property Naming -
email = yes | noboolean retired. v2.0: The property that hardcoded one specific metadata field into the component’s schema is gone. The API no longer claims this component is about email addresses. (C2 · Property) C2 · Variant & Property Naming - Separator alpha treatment settled. v2.1: The separator is
#F6F9FDat 24% on brand and#E5EBF4on default — the same treatment as Page Banner’s border, which the owner has confirmed as intentional and token-bound. Recorded here as covered by that decision rather than reopened per-component. (C3 · Token) C3 · Token Coverage - Layer naming pass complete across both variants. v2.2: Verified on the live node.
#title→Title,sender-details→SenderDetails, the two siblings that both read#text→LabelandValue, and#description→Description— the last of these landing in v2.3, which cleared the final cross-variant mismatch. Every text layer now carries the same name inSurface=BrandandSurface=Default, so each exposes as a single text property, and all four follow the §3 vocabulary. The legacy#prefix is gone from the component entirely. (C1 · Rename) C1 · Layer Structure & Naming - Avatar placeholder confirmed intentional. v2.2: The
Placeholderinstance is the deliberate swap target rather than an unfinished avatar — the consumer instance-swaps their own content into it, and the DS is not prescribing Avatar specifically, since a detail hero also fronts merchants and transactions that have a logo or an icon rather than a person. Attested rather than verified: instance-swap property definitions are not readable through the review tooling, so this is recorded on the owner’s confirmation. (C4 · Composition) C4 · Native Mappability - Single metadata row confirmed as the intended scope. v2.2:
SenderDetailsholds one label-value pair by design. A detail hero introduces the subject of the screen; the full metadata list belongs to the rows below it, not to the hero. Confirmed by the owner rather than left as an implied limitation, so a consumer needing several rows knows to compose them beneath rather than to extend this component. (C2 · Property) C2 · Variant & Property Naming - Spacer instances confirmed a deliberate convention. v2.2: The
_space_8/_space_12/_space_16instances are a system-wide spacing-annotation device, not layout elements left in by accident — the same pattern appears in Chip. Confirmed by the owner. Attested rather than verified: layer visibility flags are not readable through the review tooling, so the reviewer cannot see whether they render, only that they exist in the tree. (C1 · Docs) C1 · Layer Structure & Naming - Label-value typography confirmed intentional. v2.2: The row is Proxima Soft 14 here where Page Banner’s equivalent is BarkAda 14. Confirmed by the owner as a deliberate difference rather than drift — the two components carry different weight in their screens, and the review flagged it as a type-system question rather than a defect. Recorded so the split reads as a decision to anyone comparing the two. (C3 · Token) C3 · Token Coverage
- Avatar is static — no pressed state needed. v2.2: The hero is informational; nothing in it is tappable, so the absence of pressed and disabled coverage is correct rather than missing. Consistent with the rest of this family, where interaction lives in the surfaces below the hero. (C5) C5 · Interaction State Coverage
- Stays grouped with the header family in the docs. v2.3: Detail Hero is a card hero rather than a header by anatomy, and the rename records that. It nonetheless stays filed alongside Section Header, Page Banner and Brand App Bar, because that is where a designer looks for it — all four answer the question “what goes at the top of this screen?”, and splitting them across groups would hide the one comparison that matters. The naming distinction is carried by the component name, not by the filing. (Docs) C1 · Layer Structure & Naming
| Role | Element | Token | Value |
|---|---|---|---|
| Brand | Surface | — | #1972F9 |
| Brand | Title | — | #FFFFFF |
| Brand | Separator | — | #F6F9FD @ 24% |
| Brand | Label | — | #F6F9FD @ 72% |
| Brand | Value | — | #FFFFFF |
| Brand | Description | — | #F6F9FD @ 72% |
| Brand | Placeholder | — | #C2CFE5 |
| Default | Surface | — | #FFFFFF |
| Default | Title | — | #0A2757 |
| Default | Separator | — | #E5EBF4 |
| Default | Label | — | #6780A9 |
| Default | Value | — | #0A2757 |
| Default | Description | — | #6780A9 |
| Default | Placeholder | — | #C2CFE5 |
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:header:1.0.0")
Import
import EastBlueDS import com.eastblue.ds.header.*
Surface is the set’s only variant property. Placeholder is a nested icon instance; Title, Label, Value and Description are text layers read from 4464:13247 — whether they are exposed as text properties is not readable with the plugin. The four _space_ spacer instances are a confirmed convention (v2.x) and have no API.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| Surface — Brand, Default | .ebSurface(.brand / .default) | surface = EBHeroSurface.Brand / Default |
| Placeholder (nested icon instance) | .ebLeadingIcon(Image) | leadingIcon = { Icon(…) } |
| Title (text) | title: String | title = "…" |
| Label (text) | label: String | label = "…" |
| Value (text) | value: String | value = "…" |
| Description (text) | description: String | description = "…" |
EBDetailHero(
title: "Add Label Here",
label: "label:",
value: "add text here",
description: "Add description here."
)
.ebSurface(.brand) EBDetailHero(
title = "Add Label Here",
label = "label:",
value = "add text here",
description = "Add description here.",
surface = EBHeroSurface.Brand
) EBDetailHero(
title: "Add Label Here",
label: "label:",
value: "add text here",
description: "Add description here."
)
.ebSurface(.default) EBDetailHero(
title = "Add Label Here",
label = "label:",
value = "add text here",
description = "Add description here.",
surface = EBHeroSurface.Default
) | Requirement | iOS | Android |
|---|---|---|
| Heading | Title carries .accessibilityAddTraits(.isHeader); the hero is the screen’s first heading. | Modifier.semantics { heading() } on the title. |
| Label and value read together | .accessibilityElement(children: .combine) on the SenderDetails row — "label, add text here". | Modifier.semantics(mergeDescendants = true) on the row. |
| Contrast on Brand | White title on #1972F9 is 4.0:1; the 72% label and description are decorative-weight and must repeat in the body. | Same values — the surface colours are fixed, not themed. |
| Status bar | Brand needs .preferredColorScheme(.dark) for the status bar; Default keeps light content. | isAppearanceLightStatusBars false on Brand, true on Default. |
Do
Use Brand at the top of a transaction detail screen; Default when the hero sits below a Brand app bar.
Don't
Don’t stack a Brand hero under a Brand app bar — two blues merge.
Do
Keep the Title to one line; the 26 px line-height is sized for one.
Don't
Don’t put the amount in the Title — that is the Value row.
Do
Use the single Label / Value row for the one fact the user came for.
Don't
Don’t add rows — the set builds one SenderDetails row by design (v2.x).
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Placeholder, Title, separator, SenderDetails › Label / Value, Description — read on 4464:13247. The _space_8 / _space_12 / _space_16 spacer instances are the confirmed convention. |
| C2 | Variant & Property Naming | Ready | Surface — Brand, Default; the email boolean is gone. |
| C3 | Token Coverage | Ready | Title → Primary/Headlines/Section, Label → Primary/Label/Light/Small, Value → Primary/Label/Small, Description → Secondary/Bold/Caption. Colour variable bindings are not readable with the plugin — confirm in Dev Mode. |
| C4 | Native Mappability | Ready | One enum and four strings — maps 1:1 onto EBDetailHero. |
| C5 | Interaction State Coverage | Not Applicable | Static hero with no tap target; no states to build. |
| C6 | Asset & Icon Quality | Ready | Placeholder is an icon-library instance (32 × 32) that consumers swap; nothing is drawn inline. |
1 Surface × 2 values = 2 variants
| Surface | Node | Dimensions |
|---|---|---|
| Brand | 4464:16379 | 360 × 218 |
| Default | 4464:13247 | 360 × 218 |
Surface value — Brand, Default; geometry, colours and the four text styles verified on 4464:16379 and 4464:13247 against the exports. Padding, gap, alignment and colour tokens are not readable with the plugin and stay —.Surface schema. Property Mapping one row per property, Usage Snippets per Surface, Guidelines added, Scorecard regraded from the email era, Variants Inventory on the two live nodes.Surface = Brand | Default exposed; email = yes | no boolean retired; Separator alpha treatment settled; Layer naming pass complete across both variants; Avatar placeholder confirmed intentional; Single metadata row confirmed as the intended scope; Spacer instances confirmed a deliberate convention; Label-value typography confirmed intentional; Avatar is static — no pressed state needed; Stays grouped with the header family in the docs.email=yes|no with a flexible metadata: [LabelValuePair] slot. Open