The date field a user actually taps. Eight versions across four states and a filled flag, built on the system's own Dropdown rather than a redrawn field.
Dropdown - Generic instead of redrawing a form field, which is what closed the old consistency gap with the rest of the form family. Error and Pressed exist for the first time, and Disabled moved onto State so the impossible combinations the old two-boolean schema allowed are gone. It is badged Needs Refinement for two inherited details from Dropdown, not for anything in its own structure.Sits inline in a form — a transaction history filter, a scheduling screen, a date of birth field. Tapping it opens Date Picker - Calendar as an overlay; the chosen date returns to the field and flips isFilled.
State with the same vocabulary as Date Picker - Header Trigger, and looks like every other form field because it is one.Dropdown - Generic, and pairs with Date Picker - Calendar as its overlay.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| State=Default | N/A | N/A | 312 × 68 | White field, #D7E0EF border. |
| State=Pressed | N/A | N/A | 312 × 68 | Border goes brand blue while the calendar is open. |
| State=Disabled | N/A | N/A | 312 × 68 | Filled #EEF2F9, no border. Same height as Default. |
| State=Error | N/A | N/A | 312 × 94 | Red border plus subtext — the only version that is taller. |
| isFilled=false | N/A | N/A | placeholder | "Select option" in the muted value colour. |
| isFilled=true | N/A | N/A | value | The chosen date in the heading colour. |
- The calendar glyph paints once. The icon carried a
#025AE9fill under a#005CE5stroke that never rendered — two near-identical blues where one was needed. Cleared in Figma during this run; the glyph is now a singleborder/color-border-primaryfill. C6 · Asset & Icon Quality - The field is a Dropdown, not a redraw. Every version wraps one
Dropdown - Genericinstance. The old component drew its own field, which is why it drifted from the rest of the form family on border colour, height and label placement. C4 · Native Mappability - Error and Pressed exist. Neither had a version before, so a form could not show an invalid date and the field gave no feedback while its calendar was open. Both are now
Statevalues, each covering both filled and empty. C5 · Interaction State Coverage - One state setting instead of overlapping booleans.
isDisabledusedYesandNowhileisFilledusedFalseandTrue, and the two together permitted combinations that could not happen. Disabled is now aStatevalue, leavingState×isFilled— orthogonal, and complete at eight of eight. C2 · Variant & Property Naming - The calendar glyph is a vector. It shipped as a raster image that could not recolour. It is now a
shape_fullvector inside aCalendaricon instance. C6 · Asset & Icon Quality - Disabled stopped being taller than Default. Every version carries Dropdown's
descriptionframe. Disabled sized itself to include the empty frame and came out 26px taller than Default, so disabling a date field pushed the rest of the form down. It now hugs its content at 68, and only Error — which has real subtext — is 94. C1 · Layer Structure & Naming - The boolean values are lowercase.
isFilledreadFalseandTrue. It now matches the rest of the family —falseandtrue, likeisDisabledon the cell andhasCareton the header trigger. C2 · Variant & Property Naming - The calendar moved out of the trigger. The old component composed its calendar inline, so the field and the surface could not be used or assessed apart. The calendar is now Date Picker - Calendar, opened as an overlay. C4 · Native Mappability
- Let the description frame size to the field. Dropdown's
descriptionframe is 366 wide inside a 312 wide component — a 54px overflow on every version. It cannot be set to fill width while hidden, which is why it is fixed. Worth solving inDropdown - Genericso every field built on it inherits the fix. Composition - Drive the unused Dropdown slots by property, not by hiding. Composing Dropdown brings a
peso-signprefix and aflag-containerholding a raster Philippines flag. Both are currently switched off by hand. Hand-hidden layers reappear the moment someone swaps the instance — if Dropdown exposed booleans for them, a date field could turn them off declaratively. Property
- Document that the picker is a wrapper, not a reimplementation. v2.1: Applied — a Usage Guideline pairs "present the platform's date picker inside this field's styling" with "don't rebuild the calendar from the Figma grid — locale and accessibility come free from the platform", and the install footnote says plainly that this is the component an app places while the other four are what it renders. Docs
- Announce the field as a date control. v2.1: Applied — Accessibility answers all three parts for both platforms: the field is announced as a date control with its value spoken as a date, activating it is described as opening a calendar, and the Error subtext is tied to the field so it is read on focus rather than left as loose text. A fourth row covers Disabled being announced rather than only greyed. A11y
- Take the
[Option 02]suffix off the name. v2.1: Applied — node7201:112099readsDate Picker. Both halves of the recommendation landed: the calendar was renamed toDate Picker - Calendarfirst, freeing the name for the component that actually takes it. As family lead this is the one member whose name carries no prefix, which is what §3.2 expects. Rename
Four states across two fill flags. Every colour here belongs to the Dropdown - Generic instance the component is built from — this component paints nothing of its own. Error is the fourth column, shown in the notes rather than as a column of its own. The two disabled #value colours are deliberate and not a mistake: a disabled field that already holds a date keeps the darker text/color-text-weakest so the value stays legible, while an empty disabled field drops to the lighter text/color-text-disabled.
| Role | Token | Default | Pressed | Disabled |
|---|---|---|---|---|
| Field surface | bg/color-bg-main · disabled bg/color-bg-strong | #FFFFFF | #FFFFFF | #EEF2F9 |
| Field border | border/color-border · pressed border/color-border-primary · error border/color-border-error | #D7E0EF | #005CE5 | – |
| #label | text/color-text | #0A2757 | #0A2757 | #0A2757 |
| #value — placeholder | text/color-text-weakest · disabled text/color-text-disabled | #90A8D0 | #90A8D0 | #C2CFE5 |
| #value — filled | text/color-text · disabled text/color-text-weakest | #0A2757 | #0A2757 | #90A8D0 |
| Calendar icon | border/color-border-primary · disabled border/color-border-primary-disabled | #005CE5 | #005CE5 | #9BC5FD |
| #subtext — Error only | text/color-text-weak | #445C85 | – | – |
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:date-picker:1.0.0")
Import
import EastBlueDS import com.eastblue.ds.datepicker.*
Planned API — the native library does not exist yet. The artifact is the Date Picker family, not this component: Cell, Header, Header Trigger, Calendar and the picker itself all ship in <code>com.eastblue.ds:date-picker</code> and import <code>com.eastblue.ds.datepicker.*</code>. This is the component an app actually places; the other four are what it renders.
Two properties, and neither is a plain parameter. State splits across a modifier and a message; isFilled is not passed at all — it follows from whether the binding holds a date, which is why the Figma panel has no text property for the value. The label, the placeholder and the calendar icon belong to the Dropdown - Generic instance this component is built from.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| State — Default, Pressed, Disabled, Error | .disabled(true) for Disabled · errorText: String? for Error. Default and Pressed are not parameters — the platform reports the press | enabled: Boolean = true for Disabled · errorText: String? for Error. Pressed comes from the InteractionSource |
| isFilled — true, false | selection: Binding<Date?> — derived, the field is filled when the binding holds a date | selected: LocalDate? — derived the same way, with onSelect to set it |
EBDatePicker( label: "Label", selection: $date )
EBDatePicker( label = "Label", selected = date, onSelect = { date = it } )
EBDatePicker( label: "Label", selection: $date ) .disabled(true)
EBDatePicker( label = "Label", selected = date, onSelect = { date = it }, enabled = false )
EBDatePicker( label: "Label", selection: $date, errorText: "Input your subtext here." )
EBDatePicker( label = "Label", selected = date, onSelect = { date = it }, errorText = "Input your subtext here." )
| Requirement | iOS | Android |
|---|---|---|
| Announced as a date control | .accessibilityLabel(label) with .accessibilityValue(date.formatted(date: .long, time: .omitted)) | contentDescription plus stateDescription carrying the spoken date |
| Activation is described | .accessibilityHint("Opens a calendar") | onClickLabel = "Open calendar" |
| Error subtext is tied to the field | Append to accessibilityValue so it reads on focus | Modifier.semantics { error(message) } |
| Disabled is announced, not just greyed | .disabled(true) sets the trait automatically | enabled = false sets the trait automatically |
Do
Present the platform's date picker inside this field's styling.
Don't
Don't rebuild the calendar from the Figma grid — locale and accessibility come free from the platform.
Do
Let isFilled follow from whether a date is selected.
Don't
Don't expose isFilled as something a developer sets by hand.
Do
Use Error with a message that says what to do.
Don't
Don't turn the border red without subtext explaining why.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | One Dropdown - Generic instance per version and nothing drawn by hand, which is the point of the component. Two quirks are inherited from that instance rather than introduced here: its description frame is 366px wide inside a 312px field, and it carries a hidden currency prefix and country flag that a date field never uses. Both belong to Dropdown. |
| C2 | Variant & Property Naming | Ready | State × isFilled is orthogonal and complete, with lowercase booleans. |
| C3 | Token Coverage | Ready | Field, border, text and icon colours all follow the shared form tokens through Dropdown. |
| C4 | Native Mappability | Ready | A form field with a trailing icon that presents a picker — the standard pattern on both platforms. |
| C5 | Interaction State Coverage | Ready | Default, Pressed, Disabled and Error, each across both filled states. |
| C6 | Asset & Icon Quality | Ready | The calendar glyph is a vector instance, now a single border/color-border-primary fill after the stray stroke was cleared. It is still a BOOLEAN_OPERATION and the hidden flag beside it is a raster — both live in the shared icon and the Dropdown instance, not in this component. |
4 State × 2 isFilled = 8 versions, complete with no gaps.
| State | isFilled | Height | Node |
|---|---|---|---|
| Default | false | 68 | 7201:112100 |
| Default | true | 68 | 7201:112105 |
| Pressed | false | 68 | 7201:112102 |
| Pressed | true | 68 | 7201:112107 |
| Disabled | false | 68 | 7201:112112 |
| Disabled | true | 68 | 7201:112110 |
| Error | false | 94 | 7201:112116 |
| Error | true | 94 | 7201:112114 |
Date Picker Trigger [Option 02] — a working title and a discarded alternative carried into the file. Renaming the calendar to Date Picker - Calendar freed the name for the component that actually takes it. This is what makes the release a minor rather than a patch.#025AE9 fill under a #005CE5 stroke that never rendered — two near-identical blues where one was needed. Cleared in Figma during this run.Primary/SemiBold/Subheading, which is not in the database, alongside three font-spec rows. Both #label and #value resolve to Primary/Label/Light/Small, and the Error subtext to Secondary/Bold/Caption — genuinely BarkAda, since that is what the Secondary family is.#D81E1E when the node says #D61B2C — a value borrowed from the button destructive palette. The bindings are bg/color-bg-main, bg/color-bg-strong, border/color-border, border/color-border-primary, border/color-border-error, text/color-text, text/color-text-weakest and border/color-border-primary-disabled.filled:true and state:disabled; with both active neither wins predictably, so Disabled+filled painted #0A2757 and Disabled+empty painted a value read in error. A compound state:disabled|filled:true key settles it. The two colours are deliberate: a disabled field holding a date keeps the darker text/color-text-weakest so the value stays legible, while an empty one drops to text/color-text-disabled.Label row, Field, Corner radius, Calendar icon and a Description frame row describing an overflow. Now Height · Width · Radius · Padding H · Padding V · Gap · Alignment, with Height switching to 94px in Error. Gap is 0px: label, field and subtext stack flush, which is worth stating because the render suggests otherwise.#D81E1E against Figma's #D61B2C; the disabled calendar icon stayed full-strength blue where Figma dims it to #9BC5FD; and the whole field rendered in the documentation font, since the preview declared font-family: inherit. The subtext now declares BarkAda explicitly, because there it is correct.getSnippet, so the snippet stood still while the controls moved. It now follows both settings, and the two languages differ where they should: Disabled is a trailing .disabled(true) modifier in SwiftUI and an enabled = false argument in Compose.isFilled was a dropdown where the guide asks for a toggle. The values were already ordered off-then-on; only control: 'toggle' was missing, which hasCaret and hasWeek6 both carry.gcash/east-blue-ios and com.gcash.eastblue:components:1.0.0, with no Import line — the last of these in the family. It now cites com.eastblue.ds:date-picker:1.0.0 and imports com.eastblue.ds.datepicker.*.State by value in Prop=Value form, which the guide forbids outright, and three more named layers rather than properties. Two rows now, and both say something a developer can act on: State splits across a modifier and a message, and isFilled is never passed — it follows from whether the binding holds a date, which is exactly why the Figma panel has no text property for the value.EBDatePicker("Date of birth", …), where the card and the live snippet use label:. Three snippets remain but are re-keyed to what changes the call — the base field, Disabled and Error. Default and Pressed produce identical code, so a fourth would have been noise.12879:49826 in Sticker Sheets v2 is deprecated. The calendar it composed inline is now Date Picker - Calendar.Dropdown - Generic rather than redrawing a form field, closing the consistency gap with the rest of the form family.isDisabled (Yes/No) folded into State, leaving State × isFilled orthogonal and complete. Boolean values lowercased.Calendar icon instance, no longer a raster image.description frame, so disabling a field shifted the form.Dropdown - Generic.