A trigger field that opens a Select Group of options. 16 variants across Type (Default/Peso) × State (Default/Expanded/Error/Disabled) × isFilled. Composes a Select Field trigger with a Select Group menu.
State=Disabled, dropped the product-specific Mobile variant, and now composes Select Field + Select Group. v2.1 finished the schema: isFilled renamed to true/false to agree with the Select Field it wraps, State=Active renamed Expanded, and the redundant isSelected removed. The matrix is a clean Type (2) × State (4) × isFilled (2) = 16 with no correlated axes. No pressed state is correct — form fields use Expanded for interaction feedback, not pressed.Contexts are illustrative. Final screens will reference actual GCash patterns.
Type is now just Default / Peso, so the component is a generic select usable across flows. Peso remains a GCash currency affordance but is a thin content variation, not a bundled feature.Type × State × isFilled — with no redundant or correlated axis. isFilled uses lowercase true/false, agreeing with the Select Field it nests and the DS-wide standard, and State=Expanded names the open menu literally rather than the ambiguous "Active".Select Field trigger instance and a Select Group menu instance rather than hand-building either. The Mobile variant's bundled phone input has been extracted.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default | Yes | Yes | State=Default | Gray #D7E0EF border, white bg, chevron down. Resting state, menu closed. |
| Expanded | Yes | Yes | State=Expanded | Blue #005CE5 border, chevron up, Select Group menu open beneath. This is also the tap feedback — the field goes straight from Default to Expanded on tap. |
| Error | Yes | Yes | State=Error | Red border — weak #F4C7C9 collapsed, strong #D61B2C when expanded. Orthogonal to whether a value is filled. |
| Disabled | Yes | Yes | State=Disabled | Non-interactive; the menu cannot open. Added in v2.0 for conditional form flows (e.g. "Country" locked until "Region" is picked). |
| Filled vs empty | Yes | Yes | isFilled | Whether the field shows a chosen value or placeholder text. Composes with every State. |
| Pressed | N/A | N/A | — | Not modelled by design. Select is a form field, not a button — the field family (Select Field, Recipient Field) uses Expanded / active-style feedback rather than a pressed state. |
- Code Connect mappings not registered. The DropdownItem boolean issue is resolved (Select Item now ships lowercase
true/false). Still blocked on the trigger's own state coverage and on the native library existing — the snippets remain a Planned API. C7 · Code Connect Linkability
- v2.0:
State=Presseddeliberately not modelled — reviewed and confirmed correct. Select belongs to the form-field family (Select Field, Recipient Field), whose convention is Default / Active / Error / Disabled with no pressed state; buttons and list rows use pressed, fields use active. Tapping a Select transitions straight toActive(blue border, menu open), which is the tap feedback — a pressed frame would be a transient flash between the two. (C5) - v2.1:
isFilledvalues renamedno/yes→false/trueacross all 16 variants — Select now agrees with theSelect Fieldit nests and with the DS-wide boolean standard. (C2) - v2.1:
State=Activerenamed toExpanded— the state is named for what it is, the open menu. (C2) - v2.1: Redundant
isSelectedproperty removed — it was perfectly correlated with the oldState=Activeand contributed no combinations. The matrix is unchanged at 16 variants (Type2 ×State4 ×isFilled2), with no collisions. (C2) - v2.1:
isFilledconfirmed as a genuine property, not redundant likeisSelected— it varies independently against everyState, controlling whether the field shows a chosen value or placeholder text. Retained deliberately. (C2) - v2.1: Peso Sign
shape_fullBOOLEAN_OPERATION deliberately not tracked here — owned by the iconography team and handled at the icon-library level. The same node appears in Select Item's Leading slot, so the flattened vector will land across both components at once. Not a Select defect. (C6)
- Register Code Connect mapping to
EBSelect. With the schema settled —Type×State×isFilled, no redundant axis — wire the Figma properties 1:1 to the SwiftUI / Compose API, forwarding State and isFilled down to the nestedEBSelectFieldand the menu toEBSelectGroup. Docs
- Rename
isFilledvalues totrue/false. v2.1: Applied — all 16 variants now readisFilled=false/true, matching the nestedSelect Fieldit wraps and the wider form-field family. The parent no longer disagrees with its own child. Rename - Rename
State=ActivetoExpanded. v2.1: Applied — the state now names what it is (the open menu) rather than the ambiguous "Active", which read as focused or enabled. FreesActiveshould a focus state ever be needed. Rename - Drop
isSelectedand derive it fromState. v2.1: Applied — the redundant property is gone. The matrix is now a cleanType(2) ×State(4) ×isFilled(2) = 16 with no correlated axis, and Code Connect has one less prop to map. Property - Add a
Disabledstate to the variant matrix. v2.0: Applied —State=Disabledships across all four Type × isFilled combinations, so conditional form flows (e.g. "Country" locked until "Region" is picked) no longer need an opacity hack on the parent frame. State - Extract Mobile variant into a dedicated component. v2.0: Applied — the Mobile (phone input) variant is gone.
Typeis now just Default / Peso, so the base component no longer carries country-code logic. Family - Rename
typetoisExpanded. v2.0: Superseded by the restructure —typeno longer encodes expansion.Typenow means content type (Default / Peso) and the open menu isState=Active, so the keyword clash the rename was avoiding is gone. (See the open issue on renamingActive→Expanded.) Rename - Rename DropdownItem
selectedvalues totrue/false. v2.1: Applied on the item — Select Item (the rebuilt DropdownItem) now ships lowercasetrue/falseacross all 36 variants, matching the C2 rule and Radio Button. The string-to-bool conversion layer is gone. Rename - Add a selected-visual state to DropdownItem. v2.0: Applied on the item — Select Item's
isSelected=trueflips the label to brand#005CE5and exposes a checkmark via the Trailing icon slot, so a picked row is unambiguous. State
Default text dropdown. Label header, select trigger with placeholder text and chevron, optional subtext. Used for general-purpose list selection.
Trigger field and dropdown list colors. Border color is the primary state indicator. Error variant uses distinct border tokens.
| Role | Token | DEFAULT | ACTIVE | ERROR (collapsed) | ERROR (expanded) |
|---|---|---|---|---|---|
| Trigger border | selected-field/color/{state}/border | #D7E0EF | #005CE5 | #F4C7C9 | #D61B2C |
| Trigger bg | selected-field/color/{state}/bg | #FFFFFF | #FFFFFF | #FFFFFF | #FFFFFF |
| Placeholder | selected-field/color/{state}/placeholder | #90A8D0 | #90A8D0 | #90A8D0 | #90A8D0 |
| Chevron icon | selected-field/color/{state}/icon | #005CE5 | #005CE5 | #005CE5 | #005CE5 |
| Peso sign (Amount) | selected-field/color/{state}/icon-currency | #183462 | #183462 | – | – |
| Header label | formgroup-header/color/label | #0A2757 | #0A2757 | #0A2757 | #0A2757 |
| Item label | dropdown-item/color/default/label | – | #0A2757 | – | #0A2757 |
| Item border | dropdown-item/color/default/border | – | #E5EBF4 | – | #E5EBF4 |
| Dropdown bg | bg/color-bg-main | – | #FFFFFF | – | #FFFFFF |
| Subtext | text/color-text-weak | #445C85 | #445C85 | – | – |
| Error subtext | border/color-border-destructive | – | – | #D61B2C | #D61B2C |
Error state dropdown with red border. Collapsed uses weak border (#F4C7C9), expanded uses strong border (#D61B2C). Subtext turns red for error messaging.
| Role | Token |
|---|---|
| Trigger height | 46px |
| Corner radius | 6px (radius-2) |
| Trigger padding | 6px top, 8px bottom, 12px horizontal |
| Chevron size | 32 × 32 |
| Peso sign size (Amount) | 15 × 15 |
| Item padding | 16px vertical, 12px left, 16px right |
| Dropdown corner radius | 6px |
| Dropdown shadow | 0 6px 12px rgba(2,14,34,0.16) |
| Header padding bottom | 8px |
Amount selection with peso sign prefix. Same trigger structure as Text but with a currency indicator for monetary value selection.
| Role | Token | Font | Size | Tracking | Line-height |
|---|---|---|---|---|---|
| Header label | Primary/Label/Light/Small | Proxima Soft Semibold | 14px | 0.25px | 14px |
| Trigger placeholder | Primary/Label/Light/Small | Proxima Soft Semibold | 14px | 0.25px | 14px |
| Dropdown item | Primary/Label/Light/Large | Proxima Soft Semibold | 18px | 0.25px | 18px |
| Subtext | — | BarkAda Semibold | 12px | 0px | 18px |
Country code dropdown with phone number input. Bundles a label row (with info icon), a select field for country code, and a Labeled Field for phone number entry. Product-specific to GCash mobile number flows.
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:dropdown:1.0.0") }
Import
import EastBlueDS // SwiftUI import com.eastblue.ds.dropdown.* // Compose
Package not yet published. These are the planned distribution paths.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| variant = Text | EBDropdown(label:items:) | EBDropdown(label, items) |
| variant = Error | .ebError(true) | isError = true |
| variant = Amount | .ebStyle(.amount) | style = EBDropdownStyle.Amount |
| variant = Mobile | .ebStyle(.mobile) | style = EBDropdownStyle.Mobile |
| type = Collapsed | — | — |
| type = Expanded | isPresented: Binding<Bool> | expanded: Boolean |
| subtext (boolean) | helperText: String? | helperText: String? |
EBDropdown("Category", selection: $category) { ForEach(categories) { item in Text(item.name) } }
EBDropdown( label = "Category", items = categories, selectedItem = selected, onItemSelected = { selected = it } )
EBDropdown("Category", selection: $category) { ForEach(categories) { item in Text(item.name) } } .ebError(true) .ebHelperText("Please select a category")
EBDropdown( label = "Category", items = categories, selectedItem = selected, onItemSelected = { selected = it }, isError = true, helperText = "Please select a category" )
EBDropdown("Amount", selection: $amount) { ForEach(amounts) { item in Text(item.formatted) } } .ebStyle(.amount)
EBDropdown( label = "Amount", items = amounts, selectedItem = selected, onItemSelected = { selected = it }, style = EBDropdownStyle.Amount )
| Requirement | iOS | Android |
|---|---|---|
| Minimum touch target | 44 × 44 pt | 48 × 48 dp |
| Accessibility label | .accessibilityLabel("Select category") | contentDescription |
| Role | .accessibilityAddTraits(.isButton) | semantics { role = Role.DropdownList } |
| Expanded state | VoiceOver: "collapsed" / "expanded" | TalkBack: announce expansion state |
| Item selection | .accessibilityValue(selectedItem) | semantics { stateDescription } |
Do
Use Dropdown for selecting from a predefined list of options. Label the trigger clearly so users know what they're selecting.
Don't
Use Dropdown for free-text entry — use Input Field instead. Dropdown is for constrained selection only.
Do
Show error state with helper text below the field explaining the validation issue.
Don't
Use the Mobile variant for generic dropdown needs — it bundles phone-specific UI that adds complexity without value.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Semantic names: label, container, text-container, peso-sign, Chevron Up/Down. Minor: dropdowncontainer missing separator. |
| C2 | Variant & Property Naming | Needs Refinement | DropdownItem selected uses yes/no instead of true/false. type is a generic property name. |
| C3 | Token Coverage | Ready | All colors bound to design tokens. Space, radius, typography, and elevation tokens all present. |
| C4 | Native Mappability | Needs Refinement | Text/Error/Amount map to Menu (iOS) / ExposedDropdownMenuBox (Android). Mobile variant needs custom composition. |
| C5 | Interaction State Coverage | Requires Rework | Missing disabled and pressed states. Only Collapsed, Expanded, and Error defined. |
| C6 | Asset & Icon Quality | Needs Refinement | Chevrons are vector instances. Amount variant Peso Sign uses BOOLEAN_OPERATION (shape_full). |
| C7 | Code Connect Linkability | Needs Refinement | No CLI mappings registered yet. |
| Aspect | Status | Notes |
|---|---|---|
| Property naming | Needs Refinement | DropdownItem selected needs boolean rename; type is generic |
| Asset quality | Needs Refinement | Peso Sign BOOLEAN_OPERATION in Amount variant |
| State coverage | Requires Rework | Missing disabled/pressed states blocks complete mapping |
| Native component file | Needs Refinement | EBDropdown.swift / EBDropdown.kt not yet created |
4 variant values × 2 type values (Collapsed/Expanded). subtext boolean toggleable on all variants.
| variant | type | Node ID |
|---|---|---|
| Text | Collapsed | 18482:31966 |
| Text | Expanded | 18482:31960 |
| Error | Collapsed | 18482:31955 |
| Error | Expanded | 18482:31949 |
| Amount | Collapsed | 18482:31944 |
| Amount | Expanded | 18482:31938 |
| Mobile | Collapsed | 18482:31911 |
| Mobile | Expanded | 18482:31924 |
selected=yes/no instead of true/false. Incompatible with Swift Bool and Kotlin Boolean for Code Connect mapping.
Openshape_full is a BOOLEAN_OPERATION, not a clean vector path. May render inconsistently on native platforms.
Open