East Blue
KeepNeeds Refinement
Search Field Component link

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

Keep — all findings resolved
Rebuilt on node 4697:18836 in the 2026 Working File and confirmed a standalone primitive. State = Default | Focused | Error | Disabled, the leading glyph is a library icon instance, the trailing slot holds real Close / Error icons and appears only where it does something, every layer carries a semantic name, and colors come from the shared generic token scale. The banded top/bottom border and the constant container chrome are both intentional — this is a full-width element above content, not a field inside a form stack. Error on the State axis is now a documented system-wide exception recorded in §6 of the Property Naming Guidelines rather than a per-component argument, and the filled-but-unfocused state is derived from value presence in code instead of doubling the variant set. Native search semantics, the keyboard contract and the accessibility requirements are all documented. All four DS Health traits pass; the only item still open is Code Connect, blocked until the native library exists.
In Context

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

Live Preview
Search
Properties
state
DS Health
Reusable
Pass
Purpose-built as a full-width element that sits above other content — the banded top/bottom chrome is the correct treatment for that role, confirmed by the component owner. Works anywhere that pattern applies. No size axis or dark mode yet, neither currently required.
Self-contained
Pass
TrailingIcon holds real Close / Error icon instances, and all four interaction states carry their own label and glyph colors. Nothing external required to render.
Consistent
Pass
Variant axis is State = Default | Focused | Error | Disabled, matching the sibling fields; every layer carries a semantic name (Container · Value · TrailingIcon); colors come from the shared generic token scale rather than a component-scoped namespace. Error on the State axis is a documented, deliberate exception.
Composable
Pass
TrailingIcon is a real slot carrying swappable Close and Error instances. The leading glyph is deliberately not slotted — a search field’s search icon is part of what identifies the component, and making it swappable would turn this into a generic icon-prefixed input, which is a different component’s job.
Behavior
State iOS Android Figma Property Notes
Default (empty) Yes Yes State=Default Placeholder label at #90A8D0, no trailing icon — nothing to clear in the empty state.
Focused Yes Yes State=Focused Value at #0A2757 with a caret. Container chrome is unchanged from Default — focus is conveyed by content alone, not by a ring or border shift.
Error Yes Yes State=Error Value at #0A2757, trailing slot swaps to the red Error icon. The container border does not change color.
Disabled Yes Yes State=Disabled Label and leading glyph mute to #C2CFE5, no trailing icon.
Issues
  • State coverage completed. v2.0: Rebuilt on node 4697:18836. State = Default | Focused | Error | Disabled now ships all four interaction states, matching the schema every sibling field uses. Replaces the old two-value default/filled axis. (C5) C5 · Interaction State Coverage
  • Leading search glyph is now a vector instance. v2.0: The raster <img> was replaced with a Search Small icon instance, restoring token-based recoloring and crisp rendering at any density. Applied as recommended. (C6 · Asset) C6 · Asset & Icon Quality
  • Trailing slot holds real icons. v2.0: The Placeholder scaffolding wrapper is gone. TrailingIcon now carries a Close instance in Default, Focused and Disabled, and an Error instance in Error. Applied as recommended. (C1 · Slot) C1 · Layer Structure & Naming
  • State axis no longer conflates content with interaction. v2.0: filled was dropped from the axis rather than split into a boolean — content-filled is derived from whether a value is present, and the axis now carries interaction states only. Resolves the C2 finding, though by a different route than the recommended isFilled split. (C2) C2 · Variant & Property Naming
  • Layer naming cleaned up. v2.1: search-field → Container, #search → Value, icon-container → TrailingIcon, across all four variants, and the Error variant's placeholder junk text (@@ . @#$!# #_12) was replaced with realistic sample content. (C1) C1 · Layer Structure & Naming
  • Clear button removed from the empty state. v2.2: TrailingIcon was deleted from State=Default, so the empty field no longer offers a clear affordance with nothing to clear. Value widened from 250 to 282 to take the freed space. The trailing slot now appears only where it does something — Close in Focused, Error in Error. (C4 · Slot) C4 · Native Mappability
  • Disabled state's trailing icon resolved. v2.2: Rather than muting the Close icon, TrailingIcon was removed from State=Disabled altogether — a disabled field has nothing to clear either. Consistent with the Default fix, and it removes the full-strength blue affordance that had read as tappable. (C5 · State) C5 · Interaction State Coverage
  • Banded border confirmed intentional. v2.3: Closed by owner decision — not a divergence. Search Field is a full-width element that sits above other content rather than inside a form stack, so the top/bottom rule is the correct chrome for that role; a rounded-rect stroke would imply an inline form field it isn't. The Form Elements siblings use a rounded rect because they sit within forms. Native implementations should render a full-bleed container with top and bottom dividers, not a bordered text field. (C4 · Family) C4 · Native Mappability
  • State=Error exception documented. v2.3: Closed on owner confirmation — Error stays on the State axis as a deliberate, documented exception to the State/Status rule, matching how most design systems model form-field validation. Keeps the set at 4 variants instead of the 6 a split would require. Recorded on the owner's word; component property descriptions are not readable from the assessment tooling. (C2 · Property) C2 · Variant & Property Naming
  • Token namespace resolved — reverted to generic tokens. v2.3: Closed by owner decision. The component-scoped main/search/color/default/* namespace was abandoned in favour of the shared generic tokens, so the single-sub-mode problem disappears — there is no longer a per-component token tier that has to enumerate every state. Colors now inherit from the system scale like the rest of Form Elements. (Token) C3 · Token Coverage
  • Confirmed as a standalone primitive. v2.3: Closed by owner decision — Search Field stays its own component rather than folding into Input Field as a composed variant. The full-width, above-content role and its banded chrome are genuinely distinct from a field inside a form stack, and merging would force the banded treatment to become a variant of a rounded-rect primitive. This also settles the token direction: main/search/* is expanded, not retired. (Composition) C4 · Native Mappability
  • Search icon delegated to the iconography team. v2.3: Removed from this component's scope. The Search Small glyph is an instance of the shared icon library — the correct setup, so edits to the icon propagate everywhere it is used. The shape_full BOOLEAN_OPERATION lives in the library source component (4629:59009), not in Search Field, so flattening it is the icon owner's call and affects every consumer equally. No action here. (C6) C6 · Asset & Icon Quality
  • Filled-but-unfocused state documented rather than added as a variant. v2.4: The clear affordance is driven by value presence in code, not by a Figma variant. State stays a pure interaction axis of four; a field holding a value while unfocused renders as Default chrome with the Value color of Focused (#0A2757) and the trailing Close icon shown. Adding a hasValue boolean would have doubled the set to depict something the runtime already derives from whether the text is empty. Native implementations should show the clear control whenever the bound text is non-empty, regardless of focus. (State · Docs) C5 · Interaction State Coverage
  • State / Status exception recorded at family level. v2.4: Written into §6 of the Property Naming Guidelines rather than re-argued per component. Text-entry components may carry Error on the State axis; everything else keeps State (interaction) and Status (system report) on separate axes. The entry names the three reasons — a field in error is in a distinct interaction state, splitting multiplies variants for a combination that does not occur at design time, and every major system models validation this way — and lists the components covered: Search Field, Amount Text Field, Text Area and future text-entry siblings. Amount Text Field had already settled the same question on its own page; the guideline entry now carries it once so no third component has to. (Family · Docs) C2 · Variant & Property Naming
  • Native search semantics documented. v2.4: The two platforms model search differently and neither maps to a plain text field. iOS: .searchable(text:) is a modifier applied to a NavigationStack or List, not a standalone view — the system owns placement, the cancel button and the scroll-to-reveal behavior, so a hand-built field should not be substituted. Android: Material 3 offers SearchBar, which expands into a full-screen search surface with its own result list, or a plain TextField with a leading search icon where inline search is wanted. Search Field’s banded full-width chrome maps to the latter. Keyboard contract: Enter submits (onSubmit / ImeAction.Search), Escape or the clear control empties the field and returns focus to it, and the field never submits on every keystroke unless the screen is explicitly live-filtering. (Docs) C4 · Native Mappability
  • Search and clear-button accessibility documented. v2.4: The field must announce as a search input rather than a generic text field — .accessibilityAddTraits(.isSearchField) on iOS, Modifier.semantics { role = Role.SearchField } or the equivalent contentDescription on Android — so VoiceOver and TalkBack read it correctly and users can jump to it by type. The trailing control needs its own label, “Clear search”, not the icon name; it is a button, not decoration, and must be reachable and at least 44×44pt / 48×48dp as a touch target even though the glyph is 24×24. In Disabled the field should be announced as dimmed rather than hidden from the tree. (A11y) C5 · Interaction State Coverage
  • Container chrome confirmed constant across states. v2.4: The border stays #E5EBF4 in all four states — focus is carried by the caret and the clear control, error by the trailing icon. Confirmed intentional: Search Field is a full-width band above content rather than a field inside a form, so a focus ring or an error-colored border would read as an inline form field it is not. Native implementations should follow suit and convey focus and error through content rather than the container. Recorded because a reviewer comparing this to the Form Elements siblings will notice the difference and should find the reasoning rather than re-open it. (C5 · Docs) C5 · Interaction State Coverage
Styles
Search Field
Search
Properties
State
Label
hasClearButton
Properties
State Default
Label Search
hasClearButton True
Colors
Band #FFFFFF
Border (top + bottom) #E5EBF4
Leading glyph #445C85
Label #90A8D0
Trailing icon –
Typography
Label —
Layout
Height 56px
Width 360px (fill)
Radius 0px
Padding H 22px / 24px
Padding V 16px
Gap 8px
Alignment —
Colors by State

Read off node 4697:18836. Colors come from the shared generic token scale. The band and its border are constant across all four states: focus is carried by the caret and the clear control, error by the trailing glyph. Token paths are indicative pending a variable-binding read, which the review tooling cannot perform.

Role Element Token Value
Default Band surface/default #FFFFFF
— Border (top + bottom) border/subtle #E5EBF4
— Leading glyph icon/secondary #445C85
— Label text/placeholder #90A8D0
Disabled Leading glyph icon/disabled #C2CFE5
— Label text/disabled #C2CFE5
Focused Label text/primary #0A2757
— Trailing icon (Close) icon/interactive #025AE9
Error Label text/primary #0A2757
— Trailing icon (Error) icon/error #D61B2C
Installation Planned API

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.

Property Mapping
Figma PropertySwiftUICompose
State = Default | Disabled | Focused | Error .ebState(.default) · .disabled(true) · @FocusState state = EBFieldState.Default · enabled = false · interactionSource
Label (text) EBSearchField(placeholder:) placeholder: String
hasClearButton (boolean) .ebClearButton(_ show: Bool) hasClearButton: Boolean
— no Figma property text: Binding<String> query: String + onQueryChange: (String) -> Unit
Usage Snippets Planned API
Standalone search field
EBSearchField("Search", text: $query)
    .ebState(.default)
    .onSubmit { runSearch(query) }
EBSearchField(
    placeholder = "Search",
    query = query,
    onQueryChange = { query = it },
    onSearch = { runSearch(query) }
)
Without the clear button
EBSearchField("Search", text: $query)
    .ebClearButton(false)
EBSearchField(
    placeholder = "Search",
    query = query,
    onQueryChange = { query = it },
    hasClearButton = false
)
Error state
EBSearchField("Search", text: $query)
    .ebState(.error)
EBSearchField(
    placeholder = "Search",
    query = query,
    onQueryChange = { query = it },
    state = EBFieldState.Error
)
Disabled
EBSearchField("Search", text: $query)
    .disabled(true)
EBSearchField(
    placeholder = "Search",
    query = query,
    onQueryChange = { query = it },
    enabled = false
)
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 Ready Container · Value · TrailingIcon — every layer semantically named, scaffolding removed.
C2 Variant & Property Naming Needs Refinement State = Default | Focused | Error | Disabled matches the sibling axis. Remaining: Error is a Status value on the State axis.
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 Needs Refinement Banded border is intentional — render as a full-bleed container with top/bottom dividers, not a bordered text field. Remaining: role=search semantics undocumented.
C5 Interaction State Coverage Needs Refinement All four states ship, each with correct trailing affordance. Remaining: filled-but-unfocused isn't modeled.
C6 Asset & Icon Quality Ready Leading glyph is a Search Small instance from the shared icon library, so icon edits propagate. The shape_full boolean op lives in the library source, outside this component's scope.
Variants Inventory (4 total)

A single State axis with four values, all 360 × 56. Label (text) and hasClearButton (boolean) are component properties rather than variant axes, so they do not multiply the count — 4 variants cover the set.

StateDimensionsNode ID
Default360 × 564697:18837
Disabled360 × 564706:18270
Focused360 × 564706:18282
Error360 × 564706:18328
2.5 — September 2026Minor
Style + Code tabs rebuilt against node 4697:18836
Style tab rebuilt as a single card — one spec card driven by a panel mirroring the Figma property panel: State (select), Label (text input), hasClearButton (toggle). Replaces four fixed cards. Documented
Style
hasClearButton constrained to real variants — the toggle is enabled only on Focused, the one variant carrying a Close control. Default and Disabled have no TrailingIcon layer, and Error holds the error glyph. Documented
Style
Specs re-measured from the node — band 56 (was 48), leading glyph 24 × 24 (was 20 × 20), border #E5EBF4 (was #D7E0EF), value BarkAda 14 / 20 tracking 0 (was 14 / 14 · +0.25). Banded top + bottom border confirmed by export. Documented
Style
Property mapping corrected — the retired state = default / filled axis and a non-existent swapIcon row are gone, along with two rows whose Figma column read — (missing). Now maps the three real properties plus the value binding. Documented
Code
Usage snippets and variants inventory updated — four snippets replacing two, and the inventory corrected from total: 2 to 4, noting that Label and hasClearButton are component properties rather than variant axes. Documented
Code
2.4 — September 2026Minor
Documentation pass — remaining recommendations closed
Filled-but-unfocused state documented rather than added as a variant — the clear affordance is driven by value presence in code; State stays a pure interaction axis of four. Resolved
State
State / Status exception recorded at family level — written into §6 of the Property Naming Guidelines, so text-entry components may carry Error on State without each one re-arguing it. Resolved
C2
Native search semantics documented — iOS .searchable(text:) is a container modifier, not a standalone view; Android splits between M3 SearchBar and a TextField. Enter / Escape contract recorded. Resolved
Docs
Accessibility documented — search role/trait, a “Clear search” label for the trailing control, and 44 × 44pt / 48 × 48dp touch targets despite the 24 × 24 glyph. Resolved
A11y
Container chrome confirmed constant — the border stays #E5EBF4 in all four states; focus is carried by the caret and clear control, error by the trailing glyph. Resolved
C5
2.3 — September 2026Minor
Owner decisions recorded
Banded border confirmed intentional — a full-width element above content, not a field inside a form stack. A rounded-rect stroke would imply an inline form field it is not. Resolved
C4
State=Error exception documented — Error stays on the State axis as a deliberate exception, keeping the set at 4 variants. Resolved
C2
Token namespace resolved — the component-scoped namespace was abandoned in favour of the shared generic tokens, so the single-sub-mode problem disappears. Resolved
C3
Confirmed a standalone primitive — stays its own component rather than folding into Input Field as a composed variant. Resolved
C4
Search icon delegated — the glyph is an instance of the shared icon library, so the shape_full boolean is the icon owner’s call and affects every consumer equally. Resolved
C6
2.2 — September 2026Patch
Trailing slot corrected
Clear button removed from the empty state — TrailingIcon deleted from State=Default, so an empty field no longer offers a clear affordance with nothing to clear. Value widened 250 → 282. Resolved
C4
Disabled trailing icon resolved — removed rather than muted; a disabled field has nothing to clear either, and the full-strength blue had read as tappable. Resolved
C5
2.1 — September 2026Patch
Layer naming
Layer naming cleaned up — search-field → Container, #search → Value, icon-container → TrailingIcon across all four variants, and the Error variant’s placeholder junk text replaced with realistic sample content. Resolved
C1
2.0 — September 2026Major
Rebuilt on node 4697:18836 — 2026 Working File
State coverage completed — State = Default | Focused | Error | Disabled ships all four interaction states, replacing the two-value default/filled axis. Resolved
C5
Leading glyph is a vector instance — the raster <img> replaced with a Search Small icon instance, restoring token-based recolouring. Resolved
C6
Trailing slot holds real icons — the Placeholder scaffolding is gone; TrailingIcon carries Close and Error instances. Resolved
C1
state axis no longer conflates content with interaction — filled dropped rather than split into a boolean; content-filled is derived from value presence. Resolved
C2
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 raster — shape_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 interaction — default/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