A single selectable row inside a Dropdown popover — label, optional leading icon, and selected/disabled states.
disabeld (C2). Country variant uses a raster PNG flag (C6). No pressed/focused state variants (C5). Disabled is modeled as a type value rather than an orthogonal state (C4).Dropdown Item is the row primitive consumed by the Dropdown overlay and by Dropdown Item Group. Not used standalone.
main/dropdown-item/color/*.disabeld is misspelled and ships in the generated TypeScript type. Disabled is modeled as a type rather than an orthogonal state, so you cannot combine it with text / amount / country cleanly.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default (unselected) | Yes | Yes | selected=false | Label #0A2757, bottom border #E5EBF4. |
| Selected | Yes | Yes | selected=true | Label #005CE5 (brand), same divider. No background highlight — relies on text color alone. |
| Disabled | Yes | Yes | type=disabeld | Soft fill #F6F9FD, label #C2CFE5. Modeled as a content type rather than a state (C4). |
| Pressed | No | No | — | Not defined. iOS highlight and Android ripple will have to be improvised at instance level. |
| Focused | No | No | — | Not defined. Required for keyboard / D-pad navigation in dropdown overlays. |
- Enum value
disabeldis misspelled. The variant name ships into the generated TS type (type?: "text" | "amount" | "country" | "text with tag" | "disabeld"). Every consumer has to mirror the typo or lose autocomplete. Rename todisabled. C2 · Variant & Property Naming - Country flag is a raster PNG. The
countryvariant embeds a Philippines image (imgPhilippines) as a raster fill, not a vector flag instance. Blocks clean native handoff and freezes the row to a single locale. C6 · Asset & Icon Quality - No pressed or focused state variants. Only
selectedon/off plus a pseudo-disabled type. Touch feedback (iOS highlight, Android ripple) and keyboard focus are not covered at the DS layer. C5 · Interaction State Coverage - Disabled is modeled as a
typevalue. It collides with content types (text, amount, country) — you can't express "amount + disabled" or "country + disabled" in the current schema. Should be an orthogonalstate/disabledaxis. C4 · Native Mappability - No selected-visual affordance beyond label color. Selected state only changes label hex (#0A2757 → #005CE5). A checkmark trailing slot or background fill would make the picked item unambiguous, especially for color-blind users. C5 · Interaction State Coverage
- Code Connect mappings not registered. Blocked by the typo, missing states, and raster asset above. C7 · Code Connect Linkability
- Rename
disabeldenum value todisabled. Zero visual change, fixes the type surface, and unblocks Code Connect naming hygiene. Rename - Promote disabled to its own axis. Split the current 5-value
typeinto two props:type(text / text with tag / amount / country) ×disabled(true / false). Matches how every native primitive models enabled/disabled and collapses the matrix to 4 × 2 × 2 (selected) = 16 compositional variants with clean semantics. Property - Replace the raster Philippines flag with a vector flag slot. Introduce a
leadingAssetslot (or `flag` slot) that accepts a vector flag instance. Current PNG blocks reuse for any non-PH locale and ships a raster asset to native. Slot - Add pressed and focused state variants. Define a state axis so iOS highlight and Android ripple map to tokenized backgrounds instead of being improvised at instance level. State
- Add an explicit selected-visual affordance. A trailing checkmark (or tokenized background fill) on
selected=trueremoves the reliance on label color alone — improves accessibility and scannability. State - Generalize the amount variant around a trailing value slot. Instead of a baked-in peso + amount text, expose a trailing content slot that the peso sign and amount compose into. Opens the row to reuse for any key/value pair (balance, fee, exchange rate). Slot
- Register Code Connect mapping to
EBDropdownItem. After the rename and state work, wire the Figma properties 1:1 to the SwiftUI / Compose API. Docs
Plain text row. Default content type used by Dropdown. Label switches from neutral #0A2757 (default) to brand #005CE5 (selected).
All color roles are bound to the main/dropdown-item/color/* token family. Dropdown Item has no variable modes — colors are keyed by state only.
| Role | Token | DEFAULT | SELECTED | DISABLED |
|---|---|---|---|---|
| Row bg | main/dropdown-item/color/{state}/bg | transparent | transparent | #F6F9FD |
| Label | main/dropdown-item/color/{state}/label | #0A2757 | #005CE5 | #C2CFE5 |
| Bottom border | main/dropdown-item/color/{state}/border | #E5EBF4 | #E5EBF4 | #E5EBF4 |
| Peso sign (amount) | main/dropdown-item/color/{state}/icon-currency | #0A2757 | #005CE5 | – |
| Badge bg (text with tag) | main/badge/negative/heavy/background | #D61B2C | #D61B2C | – |
| Badge label (text with tag) | main/badge/negative/heavy/label | #FFFFFF | #FFFFFF | – |
Row with a trailing Badge instance (Negative/Heavy variant in stock). Used when an option needs an inline status label.
| Role | Token | Token |
|---|---|---|
| Row width | 366px (fill) | — |
| Row height | 50px (text / text with tag) · 51.2–52px (amount / country) | — |
| Padding top/bottom | 16px | space/space-16 |
| Padding left | 12px | space/space-12 |
| Padding right | 16px | space/space-16 |
| Gap (country / text with tag) | 8px | space/space-8 |
| Flag size (country) | 25 × 16 | — |
| Flag radius | 2px | radius/radius-1 (approx) |
| Peso sign size (amount) | 18 × 18 | — |
| Bottom border | 1px solid | main/dropdown-item/color/{state}/border |
| Row corner radius | 0 | radius/radius-0 |
Peso sign (vector, Proxima-sized) + amount text. Icon currency token flips to brand on selected.
| Role | Token | Font | Size | Tracking | Line-height |
|---|---|---|---|---|---|
| Label (all types) | Primary/Label/Light/Large | Proxima Soft Semibold | 18px | 0.25px | 18px |
| Amount text | Primary/Label/Light/Large | Proxima Soft Semibold | 18px | 0.25px | 18px |
| Badge label (text with tag) | Primary/Label/Fine | Proxima Soft Bold | 12px | 0.5px | 12px |
Leading flag (25 × 16, 2px radius) + country name and dial code. <strong>Flag is a raster PNG</strong>, not a vector instance — open issue (C6).
Soft fill row with muted label. Currently only exists at <code>selected=false</code>. <strong>Enum value is misspelled (<code>disabeld</code>)</strong> — open issue (C2).
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
Dropdown Item is bundled with the Dropdown package. The planned SwiftUI API exposes it as EBDropdownItem; on Compose it maps to Material 3's DropdownMenuItem with EB-styled content.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| type = text | EBDropdownItem(label:) | DropdownMenuItem(text = { Text(label) }) |
| type = text with tag | EBDropdownItem(label:tag:) | trailing = { EBBadge(…) } |
| type = amount | .ebStyle(.amount) | style = EBDropdownItemStyle.Amount |
| type = country | EBDropdownItem(flag:name:dialCode:) | leadingIcon = { FlagIcon(…) } |
| type = disabeld Typo | .disabled(true) | enabled = false |
| selected = true | isSelected: Bool | selected: Boolean |
| selected = false | isSelected: Bool (default) | selected: Boolean (default) |
EBDropdownItem("Dropdown Item") .isSelected(category == "item") .onTap { category = "item" }
EBDropdownItem( label = "Dropdown Item", selected = selected == "item", onClick = { selected = "item" } )
EBDropdownItem("Dropdown Item") { EBBadge("Label", level: .heavy, state: .negative) }
EBDropdownItem( label = "Dropdown Item", trailing = { EBBadge("Label", level = EBBadgeLevel.Heavy, state = EBBadgeState.Negative) } )
EBDropdownItem(amount: "1,000.00") .ebStyle(.amount)
EBDropdownItem( label = "1,000.00", style = EBDropdownItemStyle.Amount )
EBDropdownItem( flag: Image("flag_ph"), name: "Philippines", dialCode: "+63" )
EBDropdownItem( label = "Philippines +63", leadingIcon = { FlagIcon(CountryCode.PH) } )
| Requirement | iOS | Android |
|---|---|---|
| Minimum touch target | 44 × 44 pt (row is 50pt tall) | 48 × 48 dp |
| Accessibility label | .accessibilityLabel("Philippines, +63") | contentDescription = "Philippines, +63" |
| Role | .accessibilityAddTraits(.isButton) | Role.Button |
| Selected announcement | .accessibilityAddTraits(.isSelected) | selected = true in semantics |
| Disabled announcement | .disabled(true) → VoiceOver says "dimmed" | enabled = false → TalkBack says "disabled" |
Do
Consume Dropdown Item through Dropdown or Dropdown Item Group. Keep labels short — 18px Semibold is the only supported text size.
Don't
Don't use Dropdown Item outside a dropdown overlay. For standalone list rows, use the List Item component instead.
Do
Pair selected state with a trailing checkmark (once added) so the picked item is unambiguous, especially on the country and amount variants.
Don't
Don't rely on the disabeld type to build disabled versions of amount or country — it only ships for the text content type. Use the planned disabled state axis instead.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Semantic names: container, name, offset, Peso Sign - Proxima, Field Trailing Flag, philippines. |
| C2 | Variant & Property Naming | Requires Rework | Enum value disabeld is misspelled and ships into the generated TS type. |
| C3 | Token Coverage | Ready | All colors bound to main/dropdown-item/color/*; space + radius + typography tokens all present. |
| C4 | Native Mappability | Needs Refinement | Maps to a custom SwiftUI row / Material 3 DropdownMenuItem. Disabled should be an orthogonal prop, not a type value. |
| C5 | Interaction State Coverage | Requires Rework | No pressed or focused variants. Selected state relies on label color alone — no checkmark or background fill. |
| C6 | Asset & Icon Quality | Requires Rework | Country variant uses a raster PNG flag, not a vector instance. Peso sign is a vector via Peso Sign - Proxima. |
| C7 | Code Connect Linkability | Needs Refinement | No CLI mappings registered yet; blocked by C2, C5, C6. |
| Aspect | Status | Notes |
|---|---|---|
| Property naming | Requires Rework | Rename disabeld → disabled before registering |
| Asset quality | Requires Rework | Replace raster PH flag with vector flag slot |
| State coverage | Requires Rework | Pressed / focused variants missing |
| Native component file | Needs Refinement | EBDropdownItem.swift / EBDropdownItem.kt not yet created |
5 type values × 2 selected values = 10 theoretical slots, but disabeld only ships with selected=false, giving 9 actual variants.
| type | selected | Node ID | Notes |
|---|---|---|---|
| text | false | 23:199454 | Neutral label |
| text | true | 23:199456 | Brand label |
| text with tag | false | 883:29328 | Trailing Badge instance |
| text with tag | true | 883:30370 | Brand label + Badge |
| amount | false | 23:199458 | Peso sign + "X,XXX.XX" |
| amount | true | 23:199465 | Brand peso + brand label |
| country | false | 23:199472 | Raster flag + "Philippines +63" |
| country | true | 23:199476 | Brand "Philippines +63" |
| disabeld Typo | false | 883:30386 | Soft fill + muted label |
disabeld misspelled — ships into the generated TS type. Rename to disabled.
Openselected on/off and a pseudo-disabled content type. Touch/keyboard feedback unmodeled.
Opentype value — collides with content types (text / amount / country). Should be an orthogonal disabled axis.
Open