RestructureRequires Rework
Date Picker Component link

The full calendar UI used to pick a date — header, weekday row, and a 7×6 day grid.

Restructure required before handoff
Component is structurally an Input Field with a calendar glyph — the whole calendar panel should be a separate popover, not nested in the trigger. State × isDisabled axes produce invalid combinations (C2). Calendar icon is a raster (C6). No Error/Pressed states (C5). Blocks native DatePicker mapping.
In Context

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

Date of Birth March 2026
Live Preview
LabelValue
Properties
State
isFilled
isDisabled
DS Health
Reusable
Partial
Trigger is generic enough for any date field context. Re-use is limited because the calendar panel is bundled inline — consumers can't show the trigger without the whole calendar, and can't show the calendar without the trigger.
Self-contained
Warn
Carries its own tokens for trigger states, but no Error state despite sibling Select Field having one. Calendar glyph is a raster image reference, not a DS icon instance.
Consistent
Warn
isDisabled uses Yes/No (should be true/false). Axis design is ambiguous — Disabled is conceptually a state, not orthogonal to State, yielding 5 usable of 8 combinations. Diverges from Input Field / Select Field pattern which use a single State enum including Disabled.
Composable
Warn
Calendar popover (Date Picker - Group) is composed inline, not as a separate overlay with its own positioning. Cannot be swapped for a sheet, bottom sheet, or native dialog. Blocks 1:1 mapping to SwiftUI DatePicker / Compose DatePickerDialog.
Behavior
State iOS Android Figma Property Notes
Default (empty) Yes Yes State=Default, isFilled=false Gray #D7E0EF border. Placeholder "Value" #90A8D0.
Default (filled) Yes Yes State=Default, isFilled=true Gray border, selected date shown in #0A2757.
Active (opening — empty) Yes Yes State=Active, isFilled=false Blue #005CE5 2px border. Inline calendar panel attached below.
Active (filled) Yes Yes State=Active, isFilled=true Blue border, filled value, calendar visible.
Disabled Yes Yes isDisabled=Yes Gray #EEF2F9 bg. Value #90A8D0. No border. Calendar glyph dims.
Error No No Not defined. Sibling Select/Input Field both support Error — must be added for validation flows (e.g. "Enter a valid birth date").
Pressed No No Not defined. Touch feedback expected on both platforms.
Open Issues
  • Layer naming inconsistent with sibling fields. Trigger frame is named Select Field and its inner container / text-container are generic — but the component itself is Date Picker. The Date Picker - Group popover has layer names like row, Monday...Saturday (day-of-week labels as layer names rather than semantic roles). Normalize to kebab-case semantic names. C1 · Layer Structure & Naming
  • isDisabled uses Yes/No, isFilled uses False/True. Should be lowercase true/false for direct Swift Bool / Kotlin Boolean mapping. Inconsistent casing between the two booleans also breaks Code Connect naming uniformity. C2 · Variant & Property Naming
  • Axis design produces invalid combinations. State × isFilled × isDisabled is a 2×2×2=8 matrix but only 5 combinations exist because Disabled collapses State. Siblings (Input Field, Select Field) use a single State enum including Disabled — Date Picker should follow that pattern. C2 · Variant & Property Naming
  • Calendar popover is composed inline, not as an overlay. Date Picker - Group is nested inside the trigger's stack and positioned absolutely — it cannot be swapped for a sheet or presented by the native date dialog. SwiftUI DatePicker and Compose DatePickerDialog both expect the trigger and the picker to be separable. C4 · Native Mappability
  • No Error state. Input Field, Select Field, and Dropdown all support Error; Date Picker does not. Birth-date and expiry flows routinely need inline validation ("Must be 18+", "Date can't be in the past"). C5 · Interaction State Coverage
  • No Pressed state. Standard tap feedback (iOS highlight, Android ripple) is missing. Consumers improvise it per-screen. C5 · Interaction State Coverage
  • Calendar glyph is a raster shape_full image. The trigger icon pulls from an MCP asset URL rather than a vector icon instance. Won't scale cleanly, won't inherit state tokens, can't be swapped. C6 · Asset & Icon Quality
  • Code Connect mappings not registered. Blocked by C1/C2/C4/C5/C6 above. C7 · Code Connect Linkability
Design Recommendations
  • Family — Unify the two cell primitives. Date Picker - Item (day cells) and Month and Year Picker - Item (month/year cells) are both selectable cells with identical state semantics (Default/Today/Selected/Disabled). Only size + typography differ. Propose one Picker Cell component with axes kind: day | month | year + state: default | today | selected | disabled. Reduces 10 variants across 2 components to 1 component with 2 clean axes. Family
  • Composition — Wrap the native date pickers instead of redrawing them. iOS: DatePicker(selection:, displayedComponents:) with .datePickerStyle(.graphical|.compact|.wheel|.field). Android: Material 3 DatePicker + DatePickerDialog. The DS EBDatePicker should be a thin wrapper that tokenizes the trigger and cells to match brand — not a from-scratch calendar redraw in Figma. Keeps accessibility, localization, and leap-year logic native. Composition
  • Field-trigger consistency — Unify with Input Field as type: .date. Structurally this is an Input Field with a date value display + calendar glyph. Matches SwiftUI TextField(value:, format: .date) / Compose OutlinedTextField(readOnly=true) + DatePickerDialog. Consolidates a fragmented field family and inherits Input Field's Error state, label slot, and helper text. Family
  • Collapse State × isFilled × isDisabled into a single state enum. Target schema: state: default | active | error | disabled + isFilled: true | false. Matches Input Field (8 variants from 4×2). Removes the 3 invalid combinations in the current matrix and aligns with the rest of the Form Elements family. Property
  • Separate the calendar panel from the trigger. Move Date Picker - Group to a true overlay component (top-anchored, dismissible, shadow-elevated) rather than an inline child of the trigger. Lets the trigger be used alone (e.g. inline text input mode) and lets the panel be presented by either a dropdown or a sheet depending on context. Composition
  • Add Error and Pressed states. Error: red border + subtext (mirror Input Field tokens). Pressed: slight bg tint for tap feedback. Required for form accessibility and parity with the rest of Form Elements. State
  • Replace the calendar raster with a vector icon instance. Use the existing DS calendar icon (if present) or add one to the icon library. Color-bind to selected-field/color/{state}/icon so it dims with Disabled automatically. Asset
  • Rename booleans to lowercase true/false. isDisabled=Yes/No to isDisabled=true/false; isFilled=False/True to isFilled=false/true. Consistent with the naming convention adopted by Input Field. Rename
Types
Default
DES DEV

Calendar trigger field. Flip the State, Filled, and Disabled controls to walk through every variant.

Properties
State
Filled
Disabled
Properties
State Default
Filled false
Disabled No
Colors
Field bg #FFFFFF
Field border #D7E0EF
Placeholder #90A8D0
Value #0A2757
Icon #005CE5
Layout
Height 48
Padding H 12
Border radius 6
Border 1px solid
Icon size 20 × 20
Typography
Label style Primary/Label/Light/Small
Label font Proxima Soft Semibold · 14 / 14 · +0.25
Value style Primary/Label/Light/Small
Value font Proxima Soft Semibold · 14 / 14 · +0.25
Colors by State

Trigger colors reuse the selected-field token family (shared with Dropdown and Select Field). Calendar panel uses dedicated date-picker tokens.

Role Token DEFAULTACTIVEDISABLED
Trigger border selected-field/color/{state}/border #D7E0EF #005CE5 (2px) hidden
Trigger bg selected-field/color/{state}/bg #FFFFFF #FFFFFF #EEF2F9
Value (filled) selected-field/color/{state}/value #0A2757 #0A2757 #90A8D0
Placeholder selected-field/color/{state}/placeholder #90A8D0 #90A8D0
Calendar icon selected-field/color/{state}/icon #005CE5 #005CE5 #9BC5FD
Header label formgroup-header/color/label #0A2757 #0A2757 #0A2757
Panel bg date-picker/month-header/color/bg #FFFFFF
Panel border date-picker/month-header/color/border #E5EBF4
Panel shadow elevation/app/shadow 0 6px 12px rgba(2,14,34,.16)
Month label date-picker/month-header/color/label #0A2757
Month chevron date-picker/month-header/color/icon #005CE5
Weekday label date-picker/week-header/color/label #0A2757
Day (unselected) date-picker/day/color/unselected/label #0A2757
Day (today border) border/color-border-primary #005CE5 (1.5px)
Day (prev/next month) text/color-text-disabled #C2CFE5
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:date-picker:1.0.0")
}

Import

import EastBlueDS  // SwiftUI
import com.eastblue.ds.datepicker.*  // Compose

Package not yet published. These are the planned distribution paths. The recommendation is to wrap native date pickers rather than redraw them.

Property Mapping
Figma PropertySwiftUICompose
State = Default
State = Active isPresented: Binding<Bool> showPicker: Boolean
isFilled (False/True) selection: Binding<Date?> selectedDate: LocalDate?
isDisabled (Yes/No) .disabled(true) enabled = false
label (formgroup-header) label: String label: String
subtext (optional) helperText: String? helperText: String?
SwiftUI
ios/Components/DatePicker/EBDatePicker.swift
Jetpack Compose
android/components/datepicker/EBDatePicker.kt
Usage Snippets Planned API
Default
EBDatePicker("Date of Birth", selection: $birthDate)
EBDatePicker(
    label = "Date of Birth",
    selectedDate = birthDate,
    onDateSelected = { birthDate = it }
)
With Helper Text
EBDatePicker("Date of Birth", selection: $birthDate)
    .ebHelperText("You must be 18 or older")
EBDatePicker(
    label = "Date of Birth",
    selectedDate = birthDate,
    onDateSelected = { birthDate = it },
    helperText = "You must be 18 or older"
)
Disabled
EBDatePicker("Date of Birth", selection: $birthDate)
    .disabled(true)
EBDatePicker(
    label = "Date of Birth",
    selectedDate = birthDate,
    onDateSelected = { birthDate = it },
    enabled = false
)
Accessibility
RequirementiOSAndroid
Minimum touch target 44 × 44 pt 48 × 48 dp
Accessibility label .accessibilityLabel("Date of Birth") contentDescription = "Date of Birth"
Selected value .accessibilityValue(formattedDate) semantics { stateDescription }
Role VoiceOver announces as date picker TalkBack announces via Role.Button + expanded state
Localization Native DatePicker respects locale calendar Native DatePickerDialog respects locale calendar
Usage Guidelines

Do

Wrap the native DatePicker / DatePickerDialog so accessibility, localization, and leap-year logic are handled by the OS.

Don't

Redraw the calendar grid from scratch — the Figma calendar is a visual spec, not a reimplementation target.

Do

Pair the trigger with a visible label above it. Use helper text to clarify expected range ("Pick a future date").

Don't

Use the trigger as a free-text date field — it's for picking from the panel, not typing. If free-entry is required, use Input Field with a date format.

Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Needs Refinement Trigger reuses Select Field / container / text-container. Day cell rows use weekday names (MondaySaturday) as layer names.
C2 Variant & Property Naming Requires Rework isDisabled=Yes/No, isFilled=False/True. Inconsistent casing. Axis design yields 5 of 8 combinations.
C3 Token Coverage Ready All colors bound to selected-field and date-picker tokens. Spacing and radius tokens consistent.
C4 Native Mappability Requires Rework Inline calendar panel blocks mapping to native DatePicker / DatePickerDialog. Must be separable.
C5 Interaction State Coverage Requires Rework No Error state (siblings have it). No Pressed state.
C6 Asset & Icon Quality Requires Rework Calendar glyph is a raster shape_full image. Month chevrons use shape_full images as well.
C7 Code Connect Linkability Needs Refinement Blocked by C1/C2/C4/C5/C6.
Code Connect
Aspect Status Notes
Property naming Requires Rework isDisabled/isFilled need lowercase boolean values
Axis design Requires Rework Collapse to single state enum matching Input/Select Field pattern
Composition Requires Rework Calendar panel must be separable to map native dialog/sheet
Asset quality Requires Rework Calendar glyph + chevrons must be vector icons
State coverage Requires Rework Error and Pressed states missing
Native component file Needs Refinement EBDatePicker.swift / EBDatePicker.kt not yet created
Variants Inventory (5 total)

Declared as a 2×2×2 matrix (State × isFilled × isDisabled) but only 5 combinations exist — Disabled collapses both State and isFilled=false into a single variant.

StateisFilledisDisabledNode ID
DefaultfalseNo12879:49784
DefaulttrueNo12890:42872
ActivefalseNo12879:49827
ActivetrueNo13342:9932
DefaulttrueYes13342:10148
1.0.0 — April 2026Major
Initial Assessment · node 12879:49826
Component assessed — 5 usable variants documented across State × isFilled × isDisabled. Field-shaped trigger with inline calendar panel on Active. Lead component of the Date Picker family. Documented
Initial
Axis design produces invalid combinations — 2×2×2 matrix yields only 5 of 8 possible variants. Should collapse to a single state enum like Input/Select Field. Open
C2 Open
Boolean casing inconsistentisDisabled=Yes/No and isFilled=False/True. Both need lowercase true/false for Code Connect. Open
C2 Open
Calendar panel nested in trigger — Date Picker - Group is composed inline rather than being a separate overlay. Blocks mapping to native DatePicker / DatePickerDialog. Open
C4 Open
No Error or Pressed state — Siblings (Input Field, Select Field, Dropdown) have Error. Pressed is required for touch feedback parity. Open
C5 Open
Calendar glyph is a raster imageshape_full image reference, not a vector icon instance. Month chevrons also use raster shape_full assets. Open
C6 Open
Code Connect mappings — Not registered. Blocked by C1/C2/C4/C5/C6. Open
C7 Open