RestructureRequires Rework
Search Field Component link

A search input field with a leading magnifying-glass icon and an optional clear button.

Rework before handoff
Only Default and Filled exist — no focused, error, or disabled states. Search glyph is a raster img, trailing slot is an unresolved Placeholder circle, and the banded top/bottom border diverges from the rest of the Form Elements family. Consider composing from Input Field + leading/trailing icon slots instead of shipping a bespoke component.
In Context

Contexts are illustrative. Final screens will reference actual GCash patterns.

Live Preview
Search
Properties
state
DS Health
Reusable
Warn
Only usable when the surface tolerates a banded (top+bottom border) look. Won't compose into forms that use the standard rounded-rect field styling. No size variants, no dark mode.
Self-contained
Warn
Trailing icon-container holds an unresolved Placeholder wrapper with a raw icon-placeholder circle — consumers must instance-swap to get a usable clear/cancel affordance. Missing focused, error, and disabled tokens.
Consistent
Warn
Variant axis is state=default/filled — conflates "has content" with the four interaction states used by every sibling field (Default/Active/Error/Disabled). Token namespace (main/search/*) isolates this from the shared field/* tokens other siblings rely on.
Composable
Partial
Trailing icon-container is a real slot and accepts a swap (swapIcon). Leading icon is not slotted — locked to the bundled search glyph (which is raster, not vector).
Behavior
State iOS Android Figma Property Notes
Default (empty) Yes Yes state=default Placeholder text at 50% opacity, trailing slot holds placeholder circle.
Filled (has query) Yes Yes state=filled Text at full opacity (#0A2757), identical container, trailing slot unchanged.
Focused No No No visible focused variant. Native focus ring cannot be approximated from DS.
Error No No No error state defined.
Disabled No No No disabled state defined.
Open Issues
  • State coverage is incomplete. Only default and filled are shipped; focused, error, and disabled are absent. Native TextField / SearchBar expect all four interaction states for focus rings, validation, and disabled styling. C5 · Interaction State Coverage
  • Leading search glyph is a raster asset. The search icon renders via <img src={imgShapeFull}> (raster PNG) rather than a vector instance from the icon library. Blocks token-based recoloring and fails crisp rendering on high-density displays. C6 · Asset & Icon Quality
  • Trailing slot ships a Placeholder wrapper with a raw circle. icon-container contains a Placeholder frame wrapping an icon-placeholder pink-circle shape. This is authoring scaffolding that should be replaced with a real clear/cancel icon (or removed) before the component leaves design. C1 · Layer Structure & Naming
  • state variant axis conflates content and interaction. state=default/filled is a derived content signal (has a value or not) — it shouldn't occupy the same axis that other Form Elements reserve for Default/Active/Error/Disabled. Also fails Code Connect's expectation of boolean-like or enum-of-states schemas. C2 · Variant & Property Naming
  • Banded border diverges from the Form Elements family. Container uses border-top + border-bottom only, no left/right, radius-0. Every sibling (Input, Labeled, Select, Recipient, View Only) uses a full rounded-rect stroke at 6px radius. No native primitive renders this shape by default — forces custom background work on both platforms. C4 · Native Mappability
  • Code Connect mappings not registered. Structural gaps (C1/C2/C5/C6) must be resolved before linking. No native file exists yet. C7 · Code Connect Linkability
Design Recommendations
  • Compose from Input Field instead of shipping a parallel primitive. Once Input Field gains leadingIcon / trailingIcon slots (already recommended in its assessment), a Search Field becomes Input Field + search glyph leading + clear-button trailing — no new component needed. Retires main/search/* tokens and inherits Default/Active/Error/Disabled for free. Composition
  • Swap the raster shape_full for the canonical search icon instance. Reference the same vector icon used elsewhere (24px Search Small) so it inherits main/{component}/color/icon-leading recoloring across modes. Asset
  • Replace the trailing Placeholder wrapper with a real Clear (X) icon instance. The current Placeholder > container > icon-placeholder path is authoring scaffolding. Bind to a 24px Close / Clear icon and expose it as an optional slot that hides when state=default. Slot
  • Add Active, Error, and Disabled variants, and split content-filled from interaction state. Adopt the sibling schema: State = Default | Active | Error | Disabled plus a boolean isFilled = true/false. That yields 8 variants and matches Input Field's axis model exactly. State
  • Align the border treatment with the Form Elements family. If Search Field remains a standalone component, switch to the shared 6px rounded-rect stroke — the banded top/bottom look is a screen-level pattern (section divider), not a field chrome treatment, and can be added by the surrounding layout. Family
  • Rename the main/search/color/default/* namespace to match the new schema. Either retire to field/* (if composed) or expand to main/search/color/{default|active|error|disabled}/* so tokens cover every state. Remove the /default/ sub-mode once other states exist. Token
  • Document search semantics for native handoff. iOS uses .searchable(text:) on a container (it is not a standalone view); Android uses Material 3 SearchBar (which expands into full-screen search) or a TextField with a leading search icon. Document both paths and the Enter-to-submit / Escape-to-clear keyboard contract. Docs
  • Add role="search" / search semantics and a labeled clear button. The clear-button slot needs its own accessibility label ("Clear search"). iOS VoiceOver and Android TalkBack must announce the field as a search input, not a generic text field. A11y
Styles
Default
DES DEV

Empty state. Placeholder label at 50% opacity (#90A8D0), leading search glyph at 80% opacity.

Search
Properties
State
Properties
Variant Default
State Default
Colors
Bg #FFFFFF
Border #D7E0EF
Text #0A2757
Placeholder #90A8D0
Layout
Field height 48px
Padding H 12px
Padding V 14px
Border radius radius/radius-2 (6px)
Border 1px solid
Icon size 20 × 20
Typography
Value style Primary/Label/Light/Small
Value font BarkAda Semibold · 14 / 14 · +0.25
Colors by State

Only a single variable mode (default) is bound on main/search/*. Focused, error, and disabled tokens do not exist yet.

Role Token DEFAULTFILLED
Background main/search/color/default/bg #FFFFFF #FFFFFF
Border (top + bottom) main/search/color/default/border #F6F9FD (80%) #F6F9FD (80%)
Placeholder main/search/color/default/placeholder #90A8D0 (50%)
Text main/search/color/default/text #0A2757
Icon (leading) main/search/color/default/icon-leading #6780A9 (80%) #6780A9 (80%)
Icon (trailing) main/search/color/default/icon-trailing #6780A9 #6780A9
Filled
DES DEV

State shown when a query has been entered. Text uses #0A2757 at full opacity.

Search
Properties
State
Properties
Variant Filled
State Filled
Colors
Bg #FFFFFF
Border #D7E0EF
Text #0A2757
Placeholder #90A8D0
Layout
Field height 48px
Padding H 12px
Padding V 14px
Border radius radius/radius-2 (6px)
Border 1px solid
Icon size 20 × 20
Typography
Value style Primary/Label/Light/Small
Value font BarkAda Semibold · 14 / 14 · +0.25
Layout
Role Token Token
Container size 360 × 56 px
Padding (horizontal) 22 px left / 24 px right — / space/space-24
Padding (vertical) 16 px space/space-16
Gap (icon ↔ text) 8 px space/space-8
Gap (trailing slot) 12 px space/space-12
Corner radius 0 radius/radius-0
Border 1 px top + bottom only
Leading icon size 24 × 24 px
Trailing slot size 24 × 24 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
Figma PropertySwiftUICompose
state = default / filled text: Binding<String> query: String
— (missing) .focused() / @FocusState interactionSource
— (missing) .disabled(true) enabled = false
swapIcon (trailing) trailingIcon: Image? trailingIcon: @Composable
label prompt: Text placeholder: String
SwiftUI
ios/Components/FormElements/EBSearchField.swift
Jetpack Compose
android/components/form/EBSearchField.kt
Usage Snippets Planned API
Standalone Search Field
EBSearchField("Search", text: $query,
    onSubmit: { runSearch(query) },
    onClear: { query = "" })
EBSearchField(
    query = query,
    onQueryChange = { query = it },
    onSearch = { runSearch(query) },
    placeholder = "Search"
)
Preferred: .searchable on a container
NavigationStack {
    List(results) { row in Text(row.title) }
}
.searchable(text: $query, prompt: "Search")
SearchBar(
    query = query,
    onQueryChange = { query = it },
    onSearch = { runSearch(query) },
    active = active,
    onActiveChange = { active = it },
    placeholder = { Text("Search") },
    leadingIcon = { Icon(Icons.Default.Search, null) }
) { /* results */ }
Alternative: compose from EBInputField
EBInputField("Search", text: $query)
    .ebLeadingIcon(Image("search"))
    .ebTrailingIcon(query.isEmpty ? nil : Image("close")) { query = "" }
    .ebRole(.search)
EBInputField(
    value = query,
    onValueChange = { query = it },
    placeholder = "Search",
    leadingIcon = { Icon(Icons.Default.Search, null) },
    trailingIcon = if (query.isNotEmpty()) {
        { IconButton({ query = "" }) { Icon(Icons.Default.Close, "Clear search") } }
    } else null
)
Accessibility
RequirementiOSAndroid
Minimum touch target 44 × 44 pt (container is 56pt ✓) 48 × 48 dp (container is 56dp ✓)
Search role / trait .searchable or .accessibilityAddTraits(.isSearchField) SearchBar sets role automatically, else semantics { role = Role.TextField; contentType = ContentType.SearchQuery }
Clear button label .accessibilityLabel("Clear search") contentDescription = "Clear search"
Submit / Enter .submitLabel(.search) + onSubmit keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search)
Escape to clear Hardware keyboard: handle in onKeyPress(.escape) Handle in onKeyEvent for keyboard users
Usage Guidelines

Do

Use Search Field for free-text query input that filters or retrieves results. Show the clear (X) button only when the field has content.

Don't

Use Search Field for destinations that don't actually filter or search. Use Input Field for generic text entry.

Do

Pair with a results region below the field and announce result counts to assistive tech when the query updates.

Don't

Ship the placeholder circle in the trailing slot — always swap to a real Clear / Cancel icon before publishing a screen.

Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Requires Rework Trailing Placeholder > container > icon-placeholder chain is authoring scaffolding, not a named icon.
C2 Variant & Property Naming Requires Rework state=default/filled conflates content with interaction. Diverges from sibling State axis (Default/Active/Error/Disabled).
C3 Token Coverage Needs Refinement All visible colors bound to main/search/color/default/*, but only a default sub-mode exists — no tokens for focused/error/disabled.
C4 Native Mappability Requires Rework Top+bottom banded border isn't a native default. Missing role=search semantics in layer model.
C5 Interaction State Coverage Requires Rework Only default/filled. No focused, error, or disabled variants.
C6 Asset & Icon Quality Requires Rework Leading search glyph is a raster img (shape_full), not a vector instance.
C7 Code Connect Linkability Not Mapped Blocked by C1/C2/C5/C6. No CLI mappings registered.
Code Connect
Aspect Status Notes
Property naming Requires Rework state=default/filled axis needs split into State (enum) + isFilled (bool)
State coverage Requires Rework Missing Active / Error / Disabled
Icon quality Requires Rework Raster leading glyph + placeholder trailing slot
Native component file Not Mapped EBSearchField.swift / EBSearchField.kt not yet created
Variants Inventory (2 total)

A single state axis with two values. Both variants are 360 × 56 px.

stateDimensionsNode ID
default360 × 5650:78118
filled360 × 5650:78126
1.0.0 — April 2026Major
Initial Assessment · node 18577:14520
Component assessed — 2 variants documented (state=default/filled). Part of Form Elements group. Verdict: Restructure / Requires Rework. Documented
Initial
State coverage incomplete — only default and filled; focused, error, disabled missing. Open
C5 Open
Leading glyph is rastershape_full rendered via img, not a vector instance. Open
C6 Open
Trailing slot ships Placeholder wrapper — unresolved Placeholder > icon-placeholder circle rather than a real Clear icon. Open
C1 Open
state axis conflates content and interactiondefault/filled is a derived content signal, not a state-machine value. Open
C2 Open
Banded border diverges from family — top+bottom only, radius-0. Siblings use full rounded-rect stroke at 6px. Open
C4 Open
Code Connect mappings — not registered. Blocked by C1/C2/C5/C6. Open
C7 Open