KeepNeeds Refinement
Subtext Message Component link

A small caption rendered beneath form fields for helper text or validation messages. 8 variants across Status (Default / Success / Error / Disabled) × Size (Small / Default), each with a leading status icon and a #message plus trailing #label. (Assessed in the 2026 Working File.)

Rebuilt — uniform anatomy across all statuses
The rebuild made the anatomy symmetric: every variant now carries a leading-icon whose glyph changes per status, rather than Primary having none and Success/Error hardcoding theirs. A Disabled status was added, the misnamed leadingLabel boolean is gone, and the text surface is two named properties (#message + trailing #label). Schema is a clean Status × Size matrix. It has also earned its place as a standalone primitive — Toggle with Label, Segmented Control - Group, Callout, and Upload File all compose it. Only Code Connect registration remains.
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
Pass
The shared helper/validation line across the system — composed by Toggle with Label, Segmented Control - Group, Callout, and Upload File. Two sizes (Small / Default) cover dense and standard form rows, and a Disabled status now mirrors the parent field.
Self-contained
Pass
Carries its own type, color, and spacing per status, token-bound. The leading icon is an instance whose glyph changes per status rather than a drawn shape per variant.
Consistent
Pass
Anatomy is uniform — every variant has the same leading-icon + content structure, with the glyph varying by status. The misnamed leadingLabel boolean is gone; text is now two named properties (#message and a trailing #label). Clean Status × Size matrix, 8 variants, no gaps.
Composable
Pass
Nested as a real instance by every form component that needs helper or validation text, so its styling and status colors propagate from one place. Maps to the native supporting-text slot without the consumer redrawing it.
Behavior
State iOS Android Figma Property Notes
Default (helper) Yes Yes Status=Default Neutral #6780A9 text with a neutral leading icon. Standard helper copy.
Success Yes Yes Status=Success Success palette with a check glyph in the leading icon.
Error Yes Yes Status=Error Error #D61B2C text and icon, with the error glyph — not a recoloured checkmark.
Disabled Yes Yes Status=Disabled Muted text + icon, mirroring a disabled parent field. Added in the rebuild.
Size Yes Yes Size=Small / Default Small for dense rows (15px line), Default for standard form rows (18px). Composes with every Status.
Message + label Yes Yes #message · #label #message carries the helper/validation copy; #label is the trailing text on the right of the row.
Issues
  • Code Connect mappings not registered. Anatomy and schema are settled, so registration is unblocked — but the SwiftUI / Compose mappings are not yet wired and the native component does not exist. Snippets remain a Planned API. C7 · Code Connect Linkability
  • v2.0: Anatomy made uniform — every variant now carries the same leading-icon + content structure with the glyph varying by status, replacing the old split where Primary had no icon and Success / Error hardcoded theirs. (C4)
  • v2.0: Misnamed leadingLabel boolean removed — the text surface is now two named properties, #message plus a trailing #label, so the name no longer contradicts the rendered position. (C2)
  • v2.0: Status=Disabled added at both sizes, so a disabled parent field has a matched subtext state instead of consumers hiding it or hand-tuning opacity. (C5)
  • v2.0: Layer naming improved — the icon wrapper is now semantically leading-icon, and the text nodes are exposed as #message / #label properties rather than generic frames. (C1)
  • v2.0: Status icon glyph confirmed to change per status (check for Success, error mark for Error) rather than a single recoloured checkmark — reviewed and verified. (C6)
  • v2.0: Standalone-primitive question settled — it earns its place as a shared component rather than folding into a field slot, since Toggle with Label, Segmented Control - Group, Callout, and Upload File all compose it. (C4)
  • v2.0: The leading icon's shape_full BOOLEAN_OPERATION is not tracked here — it is owned by the iconography team and fixed at the icon-library level, the same as the Peso Sign. Not a Subtext Message defect. (C6)
  • v2.1: Size=Base renamed Size=Default across all 8 variants — the size step now reads naturally against Small and matches how Default is used as the standard step elsewhere in the DS. (C2)
Recommendations
  • Register Code Connect mapping to EBSubtextMessage. Wire Status and Size 1:1, and map #message / #label to the native supporting-text parameters. Docs
  • Give every variant a uniform leading-icon slot. v2.0: Applied — all four statuses share the same anatomy, with the glyph varying per status. Slot
  • Fix the misnamed leadingLabel boolean. v2.0: Applied — the boolean is gone; text is now #message + trailing #label. Rename
  • Add a Disabled variant. v2.0: Applied — Status=Disabled at both sizes, mirroring the parent field. State
  • Use semantic layer names. v2.0: Applied — leading-icon plus #message / #label text properties. Rename
  • Rename Size=Base to Default. v2.1: Applied — Small / Default across all 8 variants. 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