The calendar surface itself. Three modes — a day grid, a month list and a year list — assembled entirely from Date Picker - Cell and Date Picker - Header instances.
Type setting. This one is built from Date Picker - Cell and Date Picker - Header instances, the chevrons are icon instances, and Mode = Day | Year | Month uses the same words the native APIs do. It is badged Needs Refinement for the scroll indicator it draws itself and the touch targets it inherits from the cell.Appears as an overlay above the field once a date picker is opened. Day mode is the default; tapping the month or year chip in its header switches to the corresponding list, and the back chevron returns.
Header → Content, and the rows are Row in every mode. The centre of the header names its own role — Container when it holds the chips, Title when it holds static text.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Mode=Day | N/A | N/A | 312 × 336 | Weekday row plus six rows of seven cells. Header pages through months. |
| Mode=Month | N/A | N/A | 312 × 296 | Twelve cells in a three-column grid. Header shows a back chevron and a title. |
| Mode=Year | N/A | N/A | 312 × 296 | Eighteen cells, scrollable. Header shows a back chevron and a title. |
| Header chevrons | N/A | N/A | Chevron Left / Right | Previous and next month in Day mode; back in Month and Year. |
- The row-count setting is named for what it does. It was
week 6— lowercase, with a space, and named as a noun rather than a state. It is nowhasWeek6, matchinghasCareton Header Trigger. A dropdown of 4, 5 and 6 was considered and rejected: Figma has no enum that is not a variant, so it would have turned three components into nine. The boolean stays, and the native side counts rows instead. C2 · Variant & Property Naming - The Title frame no longer paints twice. The Month and Year header carried a white fill on an already-white card and an
#E5EBF4stroke switched off. Neither drew anything, but both read as surfaces when inspecting the layers. Cleared in Figma during this run — the fourth and last of these leftovers in the family. C1 · Layer Structure & Naming - The calendar is assembled, not redrawn. Every cell in every mode is a Date Picker - Cell instance, and the weekday strip is seven Date Picker - Header instances. The old surface drew its own cells, which is why cell states were undefined on the grid. C1 · Layer Structure & Naming
- Layers are named by role, not by weekday. Day cells were previously named after the weekday they happened to fall on, which broke as soon as the month started on a different day. Rows are now
Rowand cells arePicker Cellinstances throughout. C1 · Layer Structure & Naming - The mode setting uses native words.
Type = Date | Year | MonthbecameMode = Day | Year | Month. "Date" described the whole component rather than the view; "Day" matches what both platforms call it. C2 · Variant & Property Naming - The chevrons are icon instances. They were raster
shape_fullassets that could not recolour or scale. Both are nowChevron LeftandChevron Righticon instances, and Month gained the previous-chevron it was missing entirely. C6 · Asset & Icon Quality - Cell states are defined by the cell. The grid used to have no way to express a pressed, selected or disabled day. Those now come from Date Picker - Cell's own settings, so the surface does not need to know about them. C5 · Interaction State Coverage
- The header centre names its own role. Day mode's centre is a
Containerholding two chips; Month and Year use aTitleholding static text. Reading the layer name now tells you which of the two header behaviours you are looking at. C4 · Native Mappability - The grid content is correct. The day grid previously repeated the 21st across two rows and filled its last row with cells reading "0". It now runs 1 to 31 with the trailing row showing the next month's days in the Prev-Next treatment. C1 · Layer Structure & Naming
- Take the row, not the cell, for range continuity. v2.1: Closed — decided against, matching the same call on Date Picker - Cell. The strip bleeds past the cell on purpose so the highlight meets its neighbour and reads as one bar across the row; moving it to the row would mean the row deciding which days are in range, which is the cell's job. Composition
- Expose the grid as a grid to assistive tech. v2.1: Applied — Accessibility carries both halves of the ask: the grid is announced as a grid so a screen reader user can move by week as well as by day, and the decorative weekday strip is hidden. A11y
- Document that the chevrons change meaning by mode. v2.1: Applied — Accessibility spells both behaviours out for each platform: "Previous month" and "Next month" in Day, "Back" in Month and Year. Month and Year carry no right chevron at all, which the Style tab now renders correctly. Docs
- Drop the drawn scroll indicator before handoff. v2.1: Applied — a Usage Guideline now pairs "let the platform draw scroll indicators in Year mode" with "don't build the drawn scrollbar from the Figma file", and the scorecard records that the Scrollbar is a shared component instance mapping to the platform indicator, not a rectangle to reproduce. Docs
- Rename the component to
Date Picker - Calendarin Figma. v2.1: Applied — node6769:105110readsDate Picker - Calendar. The recommendation also asked that the field trigger be renamed after this one; that is still outstanding ondate-picker(node7201:112099) and carries over to its own review. Rename
The calendar paints only three things of its own — the card, its border and the two chevrons. Everything inside is a Date Picker - Cell, - Header or - Header Trigger instance carrying its own colours; the scrollbar in Year mode is a shared Scrollbar component. No states: the surface never changes.
| Role | Token | Value |
|---|---|---|
| Card surface | bg/color-bg-primary-inverse | #FFFFFF |
| Card border | border/color-border-weak | #E5EBF4 |
| Chevron Left / Right | border/color-border-primary | #005CE5 |
| Scrollbar thumb — Year | text/color-text | #0A2757 |
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>.
Two properties. Everything else on this component is a nested instance rather than a setting: the header chevrons, the two Header Trigger chips, the weekday row and every date cell are Date Picker instances that carry their own properties. hasWeek6 is the one place Figma and the platform disagree in shape — Figma toggles a row, the platform counts them, because a February that starts on the first day of the week needs four.
| Figma Property | SwiftUI | Compose |
|---|---|---|
| Mode — Day, Month, Year | mode: EBDatePickerCalendarMode | mode: EBDatePickerCalendarMode |
| hasWeek6 — true, false | weeks: Int = 6 — the boolean is a row count on both platforms, clamped to 4...6 | weeks: Int = 6 — same, clamped to 4..6 |
EBDatePickerCalendar( mode: .day, weeks: 6 )
EBDatePickerCalendar( mode = EBDatePickerCalendarMode.Day, weeks = 6 )
EBDatePickerCalendar( mode: .month )
EBDatePickerCalendar( mode = EBDatePickerCalendarMode.Month )
EBDatePickerCalendar( mode: .year )
EBDatePickerCalendar( mode = EBDatePickerCalendarMode.Year )
| Requirement | iOS | Android |
|---|---|---|
| The grid is announced as a grid | .accessibilityElement(children: .contain) with row and column traits | Modifier.semantics { collectionInfo = CollectionInfo(rows, 7) } |
| Weekday strip is hidden | .accessibilityHidden(true) — each cell names its own weekday | Modifier.clearAndSetSemantics {} |
| Mode changes are announced | UIAccessibility.post(.screenChanged) on switching mode | LiveRegionMode.Polite on the header title |
| Chevrons are labelled by what they do | "Previous month" and "Next month" in Day; "Back" in Month and Year | Same, via contentDescription |
| Dynamic type | Supported, scales to AX5 — the 312pt card is fixed, so the grid keeps its columns and the cells truncate rather than reflow | sp units; the 312dp card behaves the same way |
Do
Let the platform's calendar supply locale, first-day-of-week and date arithmetic.
Don't
Don't reimplement the calendar maths to match this Figma grid exactly.
Do
Open Day mode first and treat Month and Year as drill-downs from the header.
Don't
Don't expose Mode as a user-facing toggle — it is navigation state.
Do
Let the platform draw scroll indicators in Year mode.
Don't
Don't build the drawn scrollbar from the Figma file.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Header → Content → Row, with every cell an instance. Grid content is correct. |
| C2 | Variant & Property Naming | Ready | Mode = Day | Year | Month matches native terminology. |
| C3 | Token Coverage | Ready | All four roles the calendar paints itself are bound and confirmed by design: the card surface, its border, the two chevrons and the Year scrollbar. Everything inside carries the tokens of the instance that draws it. |
| C4 | Native Mappability | Ready | The card maps to a calendar view on both platforms. The scrollbar is a shared Scrollbar component, not a drawn rectangle, and maps to the platform indicator. hasWeek6 is the one shape difference: Figma toggles a row, the platform counts them, so it maps to weeks: Int clamped to 4...6. |
| C5 | Interaction State Coverage | Ready | Every state comes from the cell, which is the only interactive part. The 32 × 32 cell and the 24px header chip are deliberate visual sizes with tap targets expanded natively to 44 pt / 48 dp, as settled on those components. |
| C6 | Asset & Icon Quality | Ready | Chevrons are icon instances; the rasters are gone. |
1 Mode setting × 3 values. Everything else varies by the instances placed inside.
| Mode | Size | Content | Header centre | Node |
|---|---|---|---|---|
| Day | 312 × 336 | 7 weekday + 42 cells | 2 Header Trigger | 6769:105099 |
| Month | 312 × 296 | 12 cells | Title | 6769:105109 |
| Year | 312 × 296 | 18 cells + scroll indicator | Title | 6769:105100 |
week 6 became hasWeek6 — lowercase, a space, and a noun where a state belonged. A Weeks = 4, 5, 6 dropdown was weighed and rejected: Figma has no enum that is not a variant, so it would have taken the set from three components to nine. This is what makes the release a minor rather than a patch.#E5EBF4 stroke switched off. The fourth and last of these leftovers in the family, cleared in Figma during this run.variants; Mode is the driving property, so Day, Month and Year each get their own card. hasWeek6 is the only control left, on the Day card alone.Primary/Bold/Subheading, which is not in the database, alongside a font spec and a "Cell text: supplied by Date Picker - Cell" cross-reference. Each card now lists the text layers visible in that mode with the instance that owns them: Primary/Label/Base for the Title and header chips, Primary/Label/Small for the weekday row, Primary/Label/Light/Small for the cells.bg/color-bg-main, border/color-border-subtle, text/color-text-heading and icon/color-icon-link. The bindings are bg/color-bg-primary-inverse, border/color-border-weak, text/color-text and border/color-border-primary. The fifth was worse: the scroll indicator was recorded as #E5EBF4 with the token "annotation only — not built", when it is built and its thumb is #0A2757.Padding, Header height, Header to content, Row height, Row gap and Corner radius. All three cards now carry Height · Width · Radius · Padding H · Padding V · Gap · Alignment, with Day switching to 296px when hasWeek6 is off — the same height as Month and Year.#E5EBF4 at 120px against #0A2757 at 80px; and the card declared no font-family, so it rendered in the documentation font rather than Proxima Soft.gcash/east-blue-ios and com.gcash.eastblue:components:1.0.0, with no Import line. It now cites the family artifact com.eastblue.ds:date-picker:1.0.0 and imports com.eastblue.ds.datepicker.*.hasWeek6 was missing entirely. Two rows now, for the two real properties. hasWeek6 maps to weeks: Int clamped to 4...6: Figma toggles a row, the platform counts them, because a February starting on the first day of the week needs four.CalendarMode, mode: CalendarMode and EBDatePickerCalendarMode. One snippet per Mode now, built from the same definition as the spec cards and the live preview.getSnippet keeps the DEV code live.7201:112099 still takes the Date Picker name.Date Picker - Group. The calendar is now assembled from Date Picker - Cell and Date Picker - Header instances rather than redrawn.Type became Mode = Day | Year | Month, matching what both platforms call these views.Picker Cell instances inside Row frames; content-container and row were recased to Container and Row.Container when it holds the chips, Title when it holds static text.