A form field with a label-on-top layout, used for plain text and value inputs.
Contexts are illustrative. Final screens will reference actual GCash patterns.
isFilled now uses true/false and property renamed to State. Action button layer renamed to action-button (C1 resolved).trailing-icon uses a rectangle placeholder instead of a swappable icon instance (C6), limiting icon customization at the consumer level.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default | Yes | Yes | State=Default | Gray #D7E0EF border, white bg. |
| Active (Focused) | Yes | Yes | State=Active | Blue #005CE5 border. |
| Error | Yes | Yes | State=Error | Red #D61B2C border. |
| Disabled | Yes | Yes | State=Disabled | #EEF2F9 bg, border hidden. |
-
isFilledrenamed fromYes/Nototrue/false— now maps directly to SwiftBool/ KotlinBooleanC2 Fixed - Property
staterenamed toState(capitalized) — consistent with sibling Form Elements fields C2 Fixed -
Button - XSmalllayer renamed toaction-button— now a semantic slot name for flexible consumer customization C1 Fixed - Trailing icon uses shared Placeholder component instance — swappable by design. Internal RECTANGLE is the default visual, replaced by designers when consuming the component C6 Closed
- Code Connect mappings not registered. Structural work is complete — registration can proceed against the 8-variant
State × isFilledschema. C7 · Code Connect Linkability
- Replace hardcoded
Button - XSmallwith anactionslot. Today the trailing action is baked in — consumers can't swap in other button variants or remove it. A named slot makes the action composable via instance swap. Slot - Replace
trailing-iconplaceholder with a swappable icon instance. The currenticon-placeholderRECTANGLE blocks designers from overriding the trailing icon without detaching. Follow the instance-swap pattern used elsewhere in the DS. Slot - Add a
helperTextslot. Validation messages and hint copy are handled externally today — a first-class slot keeps the form anatomy self-contained. Slot
Idle state with gray border. Leading/trailing icon placeholders, label + value text container, and XSmall action button.
All states share the same container structure. Border color is the primary state indicator. Text colors depend on isFilled (true/false).
| Role | Token | DEFAULT | ACTIVE | ERROR | DISABLED |
|---|---|---|---|---|---|
| Border | field/border | #D7E0EF | #005CE5 | #D61B2C | hidden |
| Background | field/bg | #FFFFFF | #FFFFFF | #FFFFFF | #EEF2F9 |
| Label (filled) | field/text/label | #0A2757 | #0A2757 | #0A2757 | #90A8D0 |
| Value (filled) | field/text/value | #0A2757 | #0A2757 | #0A2757 | #C2CFE5 |
| Value (empty) | field/text/placeholder | #90A8D0 | #90A8D0 | #90A8D0 | #C2CFE5 |
Focused state with blue border indicating active input.
Validation error state with red border.
Non-interactive state with gray background and hidden border.
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.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| isFilled (true/false) | text: Binding<String> | value: String |
| State = Default | — | — |
| State = Active | .focused() | interactionSource |
| State = Error | .ebError(true) | isError = true |
| State = Disabled | .disabled(true) | enabled = false |
| #label (TEXT) | label: String | label: String |
| #value (TEXT) | text: Binding<String> | value: String |
| leading-icon | leadingIcon: Image? | leadingIcon: @Composable? |
| trailing-icon | trailingIcon: Image? | trailingIcon: @Composable? |
| action-button | action: EBFieldAction? | action: @Composable? |
EBLabeledField("Label", text: $value) .leadingIcon(Image("icon-placeholder")) .trailingIcon(Image("chevron-right"))
EBLabeledField( value = text, onValueChange = { text = it }, label = "Label", leadingIcon = { Icon(Icons.Default.Placeholder, null) }, trailingIcon = { Icon(Icons.Default.ChevronRight, null) } )
EBLabeledField("Label", text: $value) .leadingIcon(Image("icon-placeholder")) .ebError(true)
EBLabeledField( value = text, onValueChange = { text = it }, label = "Label", leadingIcon = { Icon(Icons.Default.Placeholder, null) }, isError = true )
EBLabeledField("Label", text: $value) .leadingIcon(Image("icon-placeholder")) .disabled(true)
EBLabeledField( value = text, onValueChange = { text = it }, label = "Label", leadingIcon = { Icon(Icons.Default.Placeholder, null) }, enabled = false )
| Requirement | iOS | Android |
|---|---|---|
| Minimum touch target | 44 x 44 pt | 48 x 48 dp |
| Accessibility label | .accessibilityLabel("Label") | contentDescription |
| Error announcement | VoiceOver reads error via .accessibilityValue | TalkBack reads error via semantics { error() } |
| Action button label | .accessibilityLabel("Action") on button | contentDescription on button |
Do
Use Labeled Field when the input needs a persistent label above the value, a leading icon for context, and an optional action button.
Don't
Use Labeled Field for simple text entry — use Input Field instead. Labeled Field is for complex form rows with icon context.
Do
Provide meaningful icons in the leading and trailing slots — they help users identify the field purpose at a glance.
Don't
Leave the icon placeholders as-is in production — always swap in a contextual icon or hide the slot.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Layer renamed to action-button, now a semantic slot name. |
| C2 | Variant & Property Naming | Ready | isFilled uses true/false. Property renamed to State (capitalized). Both fixes confirmed in Figma. |
| C3 | Token Coverage | Needs Refinement | Colors appear correct but token binding not verified. |
| C4 | Native Mappability | Ready | Maps to custom EBLabeledField on both platforms. |
| C5 | Interaction State Coverage | Ready | All 4 states defined: Default, Active, Error, Disabled. |
| C6 | Asset & Icon Quality | Needs Refinement | trailing-icon uses icon-placeholder RECTANGLE — not a swappable icon instance. |
| C7 | Code Connect Linkability | Needs Refinement | No CLI mappings registered yet. |
| Aspect | Status | Notes |
|---|---|---|
| Property naming | Ready | isFilled=true/false and State (capitalized) — C2 fixed in Figma, ready for mapping |
| State coverage | Ready | All 4 states defined |
| Icon slots | Needs Refinement | leading-icon uses Placeholder instance (OK). trailing-icon uses RECTANGLE (blocked). |
| Action slot | Ready | Renamed to action-button — semantic slot name, ready for Code Connect mapping |
| Native component file | Needs Refinement | EBLabeledField.swift / EBLabeledField.kt not yet created |
4 State values × 2 isFilled values.
| State | isFilled | Node ID |
|---|---|---|
| Default | true | 17758:3714 |
| Default | false | 17758:3723 |
| Active | true | 17758:3732 |
| Active | false | 17758:3741 |
| Error | true | 17758:3750 |
| Error | false | 17758:3759 |
| Disabled | true | 17758:3768 |
| Disabled | false | 17758:3777 |
Button - XSmall renamed to action-button. Now uses a semantic slot name, enabling flexible consumer customization and clean Code Connect mapping.
FixedisFilled values changed from Yes/No to true/false. Now maps directly to Swift Bool and Kotlin Boolean for Code Connect.
Fixedstate renamed to State (capitalized) to align with sibling Form Elements fields (Input Field, etc.).
FixedisFilled=Yes/No instead of true/false. Incompatible with Swift Bool and Kotlin Boolean for Code Connect mapping.
Openstate uses lowercase, inconsistent with other Form Elements using State (capitalized).
OpenButton - XSmall is not a named action slot, limiting consumer customization.
Openicon-placeholder in trailing-icon is a RECTANGLE, not a swappable icon instance.
Open