A read-only form field showing a label and value; non-interactive.
4996:25507 in the 2026 Working File. TrailingContent = None | Badge | Link | Icon × Size = SM | LG × Status = Default | Error | Warning ships all 24 combinations with no gaps, layer naming follows the §3 vocabulary and the trailing containers are kebab-case per §4. The axis rename from State to Status settles the last divergence: a display-only field has no interaction states to describe, so the form-field exception in §6 did not apply and the property now says what it actually carries. All four DS Health traits pass; the only item still open is Code Connect, blocked until the native library exists.Contexts are illustrative. Final screens will reference actual GCash patterns.
Label and Value, the trailing containers are kebab-case per §4, and the 24-variant matrix is complete with no gaps. The axis now reads Status = Default | Error | Warning, which is correct for a display-only component: it reports what the system knows about the value rather than describing an interaction that cannot occur.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default | Yes | Yes | TrailingContent=None | Label + value only, no trailing slot. |
| Trailing badge | Yes | Yes | TrailingContent=Badge | Badge instance in the trailing slot (e.g. "Change" status chip). |
| Trailing link | Yes | Yes | TrailingContent=Link | Text link in the trailing slot (e.g. "What is this?" contextual help). |
| Trailing icon | Yes | Yes | TrailingContent=Icon | Edit icon (pencil) in the trailing slot — typically navigates to an editable state. |
| Checkmark | Yes | Yes | hasCheckmark=true | Displays a 13×13 checkmark next to the value (e.g. verified status). |
| Description | Yes | Yes | hasDescription=true | Shows a subtext message below the value (e.g. "Message content" helper text). |
- Overloaded
variantproperty renamed. v2.0: Rebuilt on node4996:25507in the 2026 Working File. The catch-allvariantenum is nowTrailingContent = None | Badge | Link | Icon— PascalCase per §1, semantic values, and a clean 1:1 native enum mapping. (C2 · Rename) C2 · Variant & Property Naming - Size values aligned to the standard scale. v2.0:
Size=Default— which described a starting state rather than a size — is nowSize = SM | LG, matching §5'sXS · SM · MD · LG · XLset and the values Amount Text Field uses. (C2 · Rename) C2 · Variant & Property Naming - Checkmark converted to a vector. v2.0: The
imgCheckCDN raster is gone. The checkmark still exists as acheckvector group inside aCheckmarkframe (4996:25514), hidden by default — so it renders crisply at any density when shown. (C6 · Asset) C6 · Asset & Icon Quality - Error and Warning states added and made selectable. v2.1:
State = Default | Error | Warningis now a real third axis, givingTrailingContent(4) ×Size(2) ×State(3) = 24 uniquely-named variants. An intermediate pass had all 24 authored but only 8 distinct names — three groups shared identicalTrailingContent × Sizenames and differed only by theirSubtext Messagestate, so Figma treated them as conflicts and consumers had no way to select Error or Warning. Naming the axis made two-thirds of the component reachable. (C2 · State) C2 · Variant & Property Naming - Text layers renamed to
LabelandValue. v2.2:#label→Labeland#text→Value, matching §3 Form-content definitions exactly. Sampled across four variants spanning all three State groups and both sizes. Sets up the text properties, whose names default from the layer names. (C1 · Rename) C1 · Layer Structure & Naming -
LabelandValueconfirmed exposed as text properties. v2.2: Closed on owner confirmation — both are exposed, so consumers set copy from the instance panel without detaching. Not independently verifiable from the assessment tooling, which cannot read component property definitions. (C2 · Property) C2 · Variant & Property Naming - Wrapper frame names confirmed intentional. v2.2: Closed by owner decision —
content-containerandtext-containerstay as they are. They are internal auto-layout wrappers with no property or override surface, so their names never reach a consumer or a Code Connect mapping; the guidelines govern property names rather than layer names. (C1) C1 · Layer Structure & Naming -
containerrenamed toFieldRow. v2.1: Verified by exact string match across all 24 variants — no trailing whitespace and no leftovers. (C1) C1 · Layer Structure & Naming - Composes the shared
Subtext Messageinstance. v2.0: Every variant carries aSubtext Messageinstance for its helper, error and warning copy, so that anatomy propagates from one source rather than being re-authored. Same pattern as Text Area and Upload File. (Composition) C1 · Layer Structure & Naming - Variant matrix verified complete at 24. v2.4: Re-read on the live node.
TrailingContent = None | Badge | Link | Icon×Size = SM | LG×State = Default | Error | Warningships all 24 combinations with no gaps — SM at 55–56px and LG at 72px throughout.LabelandValuecarry the §3 vocabulary, and the trailing containers are kebab-case (badge-container,icon-container,text-link-container) per §4. (C1) C1 · Layer Structure & Naming -
Stateaxis renamed toStatus. v2.5: Verified on the live node — all 24 variants now readTrailingContent×Size×Status = Default | Error | Warning. The axis was carrying three statuses and no interaction values on a component that is display-only and can never be focused, pressed or disabled, so the form-field exception in §6 of the Property Naming Guidelines did not reach it. The rename cost nothing structurally — same three values, same 24 variants — and the component now reads correctly against the guideline the rest of the family is measured by. (C2 · Rename) C2 · Variant & Property Naming -
Subtext Messagecopy exposed via nested instance properties. v2.5: The message and its trailing label can be set from the parent without detaching. Attested rather than verified — nested-instance property exposure is not readable through the review tooling, so this is recorded on the owner’s confirmation. (C1 · Slot) C1 · Layer Structure & Naming
Display-only component — no interaction states. All colors token-bound.
| Role | Token | Value |
|---|---|---|
| Label | main/view-only-field/color/label | #6780A9 |
| Value text | main/view-only-field/color/text | #0A2757 |
| Text link | main/view-only-field/color/label-link | #005CE5 |
| Edit icon | main/view-only-field/color/icon | #005CE5 |
| Subtext description | main/subtext-message/primary/label | #6780A9 |
| Badge bg (default) | main/badge/information/light/background | #E5F1FF |
| Badge label | main/badge/information/light/label | #005CE5 |
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:form-elements:1.0.0")
Import
import EastBlueDS import com.eastblue.ds.form.*
Package not yet published. These are the planned distribution paths.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| TrailingContent = None | Badge | Link | Icon | trailing: EBTrailingContent | trailingContent = EBTrailingContent.None |
| Size = SM | LG | .controlSize(.small / .large) | size = EBFieldSize.SM / LG |
| Status = Default | Error | Warning | .ebStatus(.error / .warning) | status = EBFieldStatus.Default |
| hasCheckmark (boolean, default False) | showCheckmark: Bool | hasCheckmark: Boolean = false |
| hasDescription (boolean, default True) | description: String? | description: String? |
| Value (text) | value: String | value: String |
| Label (text) | label: String | label: String |
EBViewOnlyField(label: "Label", value: "Text") .controlSize(.large)
EBViewOnlyField( label = "Label", value = "Text", size = EBFieldSize.LG )
EBViewOnlyField(label: "Label", value: "Text") .ebTrailing(.badge)
EBViewOnlyField( label = "Label", value = "Text", trailingContent = EBTrailingContent.Badge )
EBViewOnlyField(label: "Label", value: "Text") .ebCheckmark(true)
EBViewOnlyField( label = "Label", value = "Text", hasCheckmark = true )
EBViewOnlyField(label: "Label", value: "Text") .ebStatus(.error)
EBViewOnlyField( label = "Label", value = "Text", status = EBFieldStatus.Error )
EBViewOnlyField(label: "Label", value: "Text") .ebStatus(.warning) .ebDescription(nil)
EBViewOnlyField( label = "Label", value = "Text", status = EBFieldStatus.Warning, description = null )
| Requirement | iOS | Android |
|---|---|---|
| Accessibility label | .accessibilityLabel("\(label): \(value)") | contentDescription = "$label: $value" |
| Trailing action (with Icon) | Button with .accessibilityLabel("Edit \(label)") | IconButton with contentDescription = "Edit $label" |
| Text link | Button with .accessibilityHint("Opens help") | TextButton with semantics { role = Role.Button } |
| Min touch target (trailing action) | 44 × 44 pt | 48 × 48 dp |
Do
Use View Only Field to display verified or system-set data (phone number, email, account status) where the user shouldn't edit directly.
Don't
Use for editable input — use Input Field, Labeled Field, or Select Field instead. Read-only fields imply the value is final or managed elsewhere.
Do
Use the with Icon variant with a pencil to indicate the field can be edited in a separate screen — provides a clear tap target.
Don't
Rely on the icon alone without an accessibility label — screen readers need to announce the trailing action's purpose.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | FieldRow wraps Label and Value on the §3 vocabulary, with the trailing containers kebab-cased per §4 — badge-container, text-link-container, icon-container. The wrapper frame names are a recorded intentional exception. |
| C2 | Variant & Property Naming | Ready | The overloaded variant enum is now TrailingContent = None | Badge | Link | Icon, Size uses the standard SM | LG scale, and the axis carrying Error and Warning was renamed State → Status in v2.5 — correct for a display-only component, which has no interaction states for a State axis to describe. |
| C3 | Token Coverage | Needs Refinement | Colors read cleanly off the node, including the amber #CA970C warning pair. Bindings themselves are not readable through the review tooling, so the token paths on the Style tab are indicative and need a Dev Mode confirmation. |
| C4 | Native Mappability | Ready | Maps to a SwiftUI VStack / Compose Column of label + value, with the trailing content as an optional closure. No border or fill — it is a display row, so there is no framework chrome to fight. |
| C5 | Interaction State Coverage | Not Applicable | Display-only. Nothing here is focused, pressed or disabled, which is why the axis carries Status rather than State. The trailing badge, link and icon own their own tap states. |
| C6 | Asset & Icon Quality | Ready | The checkmark is a vector — a 13 × 13 filled circle at #025AE9 with the tick knocked out — replacing the raster CDN image. The edit icon and the status glyphs are shared instances. |
TrailingContent (4) × Size (2) × Status (3) = 24 variants — the full matrix, no gaps. hasCheckmark, hasDescription, Value and Label are component properties rather than variant axes, so they do not multiply the count. SM is 55px in Default and 56px in Error and Warning, where the status glyph sets the row height.
| TrailingContent | Size | Status | Count |
|---|---|---|---|
| None | SM · LG | Default · Error · Warning | 6 |
| Badge | SM · LG | Default · Error · Warning | 6 |
| Link | SM · LG | Default · Error · Warning | 6 |
| Icon | SM · LG | Default · Error · Warning | 6 |
View full TrailingContent × Size × Status breakdown (24 rows)
| TrailingContent | Size | Status | Dimensions | Node ID |
|---|---|---|---|---|
| None | SM | Default | 360 × 55 | 4996:25508 |
| None | LG | Default | 360 × 72 | 4996:25562 |
| None | SM | Error | 360 × 56 | 5001:33489 |
| None | LG | Error | 360 × 72 | 5001:33543 |
| None | SM | Warning | 360 × 56 | 5001:34115 |
| None | LG | Warning | 360 × 72 | 5001:34169 |
| Badge | SM | Default | 360 × 55 | 4996:25520 |
| Badge | LG | Default | 360 × 72 | 4996:25574 |
| Badge | SM | Error | 360 × 56 | 5001:33475 |
| Badge | LG | Error | 360 × 72 | 5001:33529 |
| Badge | SM | Warning | 360 × 56 | 5001:34101 |
| Badge | LG | Warning | 360 × 72 | 5001:34155 |
| Link | SM | Default | 360 × 55 | 4996:25534 |
| Link | LG | Default | 360 × 72 | 4996:25588 |
| Link | SM | Error | 360 × 56 | 5001:33461 |
| Link | LG | Error | 360 × 72 | 5001:33501 |
| Link | SM | Warning | 360 × 56 | 5001:34087 |
| Link | LG | Warning | 360 × 72 | 5001:34141 |
| Icon | SM | Default | 360 × 55 | 4996:25548 |
| Icon | LG | Default | 360 × 72 | 4996:25602 |
| Icon | SM | Error | 360 × 56 | 5001:33447 |
| Icon | LG | Error | 360 × 72 | 5001:33515 |
| Icon | SM | Warning | 360 × 56 | 5001:34073 |
| Icon | LG | Warning | 360 × 72 | 5001:34127 |
TrailingContent, Size and Status as selects, hasCheckmark and hasDescription as toggles, and Value and Label as text inputs. The booleans and text properties were invisible to get_node_info, which returns variant properties only. DocumentedSpecCard.astro handled selects and toggles but had no control for a Figma TEXT property, so Value and Label had nowhere to go. Added control: 'input' to the schema and the renderer. Documented#025AE9 and filled. The form was then still wrong: exporting the boolean child alone renders a plain circle, and only the check group shows the white tick knocked out of it. Alignment corrected too — 8px after the value, vertically centred in the value box, measured from the real text width at runtime rather than a per-character estimate. Documentedvariant=Default row and Size=Large are gone. Now maps all seven properties: three enums, two booleans and two text properties. Documentedtotal: 8 on a “4 variant × 2 Size” matrix to the real 24 (TrailingContent × Size × Status), with a grouped summary and a collapsible full breakdown per the inventory conventions. codeConnect was an empty array and is now filled. Documentedvariant overloaded and Size=Default a non-size, C6 still reported a raster checkmark from the Figma CDN. All resolved in v2.0. C5 is now Not Applicable rather than Ready: a display-only component has no interaction states to cover. DocumentedState axis renamed Status — the axis carried three statuses and no interaction values on a component that can never be focused, pressed or disabled, so the §6 form-field exception did not reach it. Same 24 variants, one property renamed. ResolvedSubtext Message copy exposed via nested instance properties — the message and its trailing label can be set from the parent without detaching. ResolvedTrailingContent (4) × Size (2) × Status (3) with no gaps, SM at 55–56px and LG at 72px throughout. ResolvedLabel and Value — onto the §3 vocabulary. ResolvedLabel and Value confirmed exposed as text properties — a consumer sets the copy without detaching. Resolved#D61B2C, an amber triangle at #CA970C. Resolvedcontainer renamed FieldRow. Resolvedvariant property renamed — four trailing content types packed into one enum called variant are now TrailingContent = None | Badge | Link | Icon, which says what the axis controls. ResolvedSize=Default was never a size name; the axis is now SM | LG. ResolvedSubtext Message instance rather than redrawing the description row. Resolvedvariant conflates 4 different trailing content types into one enum. Should be renamed to trailingContent or split into semantic properties.
Open