RestructureRequires Rework
Subtext Message Component link

A small caption rendered beneath form fields for helper text or validation messages.

Restructure before native handoff
Asymmetric anatomy (Primary has no icon, Success/Error hardcode icons), misnamed leadingLabel boolean, generic shape_full icon layers, no disabled state. Decide: keep as standalone primitive or fold into form-field supportingText slot.
In Context

Appears directly beneath form fields — Input, Labeled, Select, Recipient, Dropdown — to communicate helper hints, success confirmation, or validation errors.

Live Preview
Message content
Properties
Variant
Size
leadingLabel
trailingIcon
DS Health
Reusable
Partial
Works under any form field. But it's only ever rendered beneath a field — in practice it's a field-composition concern, not a standalone primitive. No disabled variant to mirror field disabled state.
Self-contained
Partial
Carries own type, color, spacing, and icon per variant. However, Success / Error icons are drawn shapes named shape_full — not instance-swapped from the DS Icon library.
Consistent
Warn
Anatomy diverges by variant — Primary has no icon slot, Success / Error hardcode icons. Boolean leadingLabel is misnamed (the Label text renders trailing in the flex row).
Composable
Partial
Instance-placed under form fields today. Native equivalents (SwiftUI convention, Compose supportingText) treat this as a field slot, not a peer component — suggesting it should be folded in.
Behavior
State iOS Android Figma Property Notes
Primary (helper) Yes Yes Variant=Primary Neutral weaker text #6780A9. No icon.
Success Yes Yes Variant=Success Green text #048570, filled check icon #12AF80.
Error Yes Yes Variant=Error Red text + icon #D61B2C.
Disabled N/A N/A No disabled variant today. When parent field is disabled, there's no matched subtext state.
Open Issues
  • Anatomy diverges by variant. Primary has no icon, Success / Error hardcode specific icons (Checkmark Circular / Close). The "leading icon" is not a uniform slot — it's an if-branch on Variant. Consumers can't override the Success / Error icon without detaching. C4 · Native Mappability
  • leadingLabel boolean is misnamed. The "Label" text actually renders on the trailing side of the flex row (after the message content), not leading. Naming contradicts rendered position and will mislead SwiftUI / Compose param names downstream. C2 · Variant & Property Naming
  • Icon layer named shape_full. The inner 12×12 shape inside the Checkmark / Close containers carries a generic, flattened-style name. Suggests a raster image fill or BOOLEAN_OPERATION rather than a proper vector Icon instance swappable from the DS Icon library. C6 · Asset & Icon Quality
  • Container layers named generically. Nested frames labeled container and content don't describe role — Code Connect slot inference relies on semantic names like #leading-icon, #message, #label. C1 · Layer Structure & Naming
  • No Disabled variant. When the parent field is disabled, the subtext has no paired state — consumers either hide it or rely on manual opacity adjustments. Every sibling form field carries a Disabled state; the subtext should too. C5 · Interaction State Coverage
  • Code Connect mappings not registered. Blocked behind C1 / C2 / C4 / C6. Also depends on the family-level decision below — whether this stays a standalone component or folds into the form-field supportingText slot. C7 · Code Connect Linkability
Design Recommendations
  • Fold Subtext Message into each form field as a supportingText slot. This is the native convention on both platforms — Material 3 TextField exposes supportingText, and SwiftUI pairs a Text under the field using the same validation state. Folding it in removes the C4 / C5 gaps (field already has Disabled + Error states) and lets consumers drive the message by passing supportingText: String? + deriving color from isError. The standalone component can stay as an annotation helper for designers but is no longer the canonical consumer integration path. Family
  • Rename leadingLabeltrailingLabel. The "Label" text renders at the end of the flex row — the property name must match rendered position so SwiftUI / Compose params don't surprise developers. If the intent is to actually make the Label leading, swap the layer order in Figma and keep the current name. Rename
  • Normalize anatomy with a real leading-icon slot across all variants. Rebuild so every variant shares the same structure: optional #leading-icon (Icon instance) → #message (text) → optional #trailing-label (Label text). Primary keeps icon = off by default; Success / Error default icon = on. This lets one Variant enum + one boolean icon slot + one boolean label slot cover all six variants uniformly. Property
  • Replace shape_full with DS Icon library instances. Swap the Success checkmark and Error close for Icon=Checkmark Circular and Icon=Close instance-swaps bound to main/subtext-message/*/icon tokens. Flattened boolean shapes can't be retinted, recolored, or resized cleanly and they block Code Connect 1:1 icon param mapping. Asset
  • Add a Disabled variant. Match the 4-state contract of every sibling form field (Default / Active / Error / Disabled). When the parent field is disabled, the subtext needs a paired muted color token (e.g. main/subtext-message/disabled/label). State
  • Rename container layers. container#leading-icon-slot, content#content, inner shape → #icon-glyph. Semantic names drive Code Connect slot inference. Rename
Styles
Primary (helper)
DES DEV

Neutral helper text. No icon. Used for hints, formatting examples, or ambient guidance under a field.

Message content
Properties
Size
Properties
Variant Primary (helper)
Size Small
Colors
Label #6780A9
Layout
Padding 2 vertical · 0 horizontal
Gap (icon ↔ label) 4px
Icon size 12 × 12
Typography
Label style Secondary/Bold/Caption
Label font BarkAda Semibold · 12 / 18
Colors by Variant

Each variant binds its own label (and icon, where applicable) token. No appearance modes. No disabled state.

Role Token TOKENVALUE
Primary label main/subtext-message/primary/label #6780A9
Success label main/subtext-message/success/label #048570
Success icon main/subtext-message/success/icon #12AF80
Error label main/subtext-message/error/label #D61B2C
Error icon main/subtext-message/error/icon #D61B2C
Disabled — (missing)
Success
DES DEV

Valid input confirmation. Green text with filled circular checkmark.

Valid message content
Properties
Size
Properties
Variant Success
Size Small
Colors
Label #048570
Icon #12AF80
Layout
Padding 2 vertical · 0 horizontal
Gap (icon ↔ label) 4px
Icon size 12 × 12
Typography
Label style Secondary/Bold/Caption
Label font BarkAda Semibold · 12 / 18
Typography by Size

Both sizes use the Secondary (BarkAda Semibold) type scale.

Role Token FONTSIZELINE HEIGHTWEIGHT
Base Secondary/Bold/Caption BarkAda 12 px 18 px Semibold (600)
Small Secondary/Bold/Small Caption BarkAda 10 px 15 px Semibold (600)
Error
DES DEV

Validation error. Red text with filled circular close icon.

Invalid message content
Properties
Size
Properties
Variant Error
Size Small
Colors
Label #D61B2C
Icon #D61B2C
Layout
Padding 2 vertical · 0 horizontal
Gap (icon ↔ label) 4px
Icon size 12 × 12
Typography
Label style Secondary/Bold/Caption
Label font BarkAda Semibold · 12 / 18
Layout
Role Token TOKEN
Padding left 2 px space/space-2
Padding top 4 px space/space-4
Gap (icon ↔ content) 4 px space/space-4
Icon frame 16 × 16 px
Icon glyph 12 × 12 px
Installation Planned API

iOS — Swift Package Manager

// In Xcode: File → Add Package Dependencies
"https://github.com/AY-Org/eb-ds-ios"

Android — Gradle (Kotlin DSL)

dependencies {
    implementation("com.eastblue.ds:form-elements:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.form.*  // Compose

Package not yet published. These are the planned distribution paths.

Property Mapping

Assumes the recommended architecture: supportingText slot on each form field (preferred), with this standalone component as a secondary annotation helper.

Figma PropertySwiftUICompose
Variant = Primary .ebSubtextStyle(.primary) style = EBSubtextStyle.Primary
Variant = Success .ebSubtextStyle(.success) style = EBSubtextStyle.Success
Variant = Error .ebSubtextStyle(.error) style = EBSubtextStyle.Error
Size = Base / Small .controlSize(.regular / .small) size = EBSubtextSize.Base / Small
leadingLabel (Yes/No) trailingLabel: String? trailingLabel: String? = null
trailingIcon (Yes/No) leadingIcon: Image? leadingIcon: @Composable (() -> Unit)?
SwiftUI
ios/Components/FormElements/EBSubtextMessage.swift
Jetpack Compose
android/components/form/EBSubtextMessage.kt
Usage Snippets Planned API
Preferred: via form-field supportingText
EBInputField("Email", text: $email)
    .ebError(!isValid)
    .ebSupportingText("Enter a valid email address")
EBInputField(
    value = email,
    onValueChange = { email = it },
    placeholder = "Email",
    isError = !isValid,
    supportingText = { Text("Enter a valid email address") }
)
Standalone primitive
EBSubtextMessage("Valid message content")
    .ebSubtextStyle(.success)
    .controlSize(.small)
EBSubtextMessage(
    text = "Valid message content",
    style = EBSubtextStyle.Success,
    size = EBSubtextSize.Small
)
Accessibility
RequirementiOSAndroid
Error announcement Wire to field .accessibilityValue so VoiceOver reads the error with the field value. Use semantics { error(msg) } on the field, not a standalone live region.
Icon is decorative Mark the leading icon .accessibilityHidden(true) — the text carries the meaning. Icon contentDescription = null; semantics go on the text.
Dynamic Type / font scaling Caption type must scale with Dynamic Type. Don't hard-lock font size. Use sp units and respect fontScale.
Color-only meaning Pair red with the close icon so red isn't the sole error cue. Same — both a color and an icon are required for error/success.
Usage Guidelines

Do

Pass the message via the parent field's supportingText slot. This keeps validation state and message colocated.

Don't

Render this beneath a field as a separate sibling component — the parent field can't coordinate disabled / error state with an external peer.

Do

Keep error messages specific and actionable ("Enter 11 digits, starting with 09"). Use Primary for ambient hints, Success for confirmation.

Don't

Use Success as a decorative "looks good!" under every valid field — reserve it for meaningful post-validation confirmation.

Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Requires Rework Generic container, content, shape_full layers. No semantic slot names.
C2 Variant & Property Naming Requires Rework leadingLabel renders trailing — name contradicts position. Booleans already true/false (good).
C3 Token Coverage Ready Dedicated main/subtext-message/* tokens for label + icon. Spacing uses space/*.
C4 Native Mappability Requires Rework Anatomy diverges by variant. Natively this is a field slot (supportingText), not a peer component.
C5 Interaction State Coverage Requires Rework No Disabled variant. Sibling fields have 4 states; subtext has 3 variants.
C6 Asset & Icon Quality Requires Rework Icons are shape_full layers — likely flattened / boolean shapes, not vector Icon instances.
C7 Code Connect Linkability Not Mapped Blocked until family decision + C1 / C2 / C4 / C6 resolved.
Code Connect
Aspect Status Notes
Property naming Requires Rework leadingLabel must be renamed to match rendered position
Slot inference Requires Rework Generic layer names block slot detection
State coverage Requires Rework Missing Disabled variant
Native component file Not Mapped Depends on family decision: standalone EBSubtextMessage or field supportingText slot
Variants Inventory (6 total)

3 Variant values × 2 Size values. The leadingLabel and trailingIcon booleans are not part of the 6 — they toggle at the instance level.

VariantSizeNode ID
PrimaryBase11855:8764
PrimarySmall11855:8767
SuccessBase11855:8770
SuccessSmall11855:8776
ErrorBase11855:8782
ErrorSmall11855:8788
1.0.0 — April 2026Major
Initial Assessment · node 18687:71133
Component assessed — 6 variants documented (3 Variant × 2 Size). Primary / Success / Error with Base and Small sizes. Used as helper / validation message beneath Form Elements. Documented
Initial
Anatomy diverges by variant — Primary has no icon slot; Success / Error hardcode specific icons. Not a uniform slot contract. Open
C4 Open
leadingLabel misnamed — The "Label" text renders on the trailing side of the flex row. Property name contradicts rendered position. Open
C2 Open
Icon layer named shape_full — Inner 12×12 glyph carries a generic, flattened-style name. Suggests raster fill or boolean op rather than a proper vector Icon instance. Open
C6 Open
Container layers named genericallycontainer / content don't describe role. Open
C1 Open
No Disabled variant — Sibling form fields all carry Disabled. Subtext doesn't. Open
C5 Open
Code Connect mappings — Not registered. Blocked by family decision (fold into field supportingText slot vs keep standalone) + C1 / C2 / C4 / C6. Open
C7 Open