Articles
Date Picker Coding Interview: Date-Only Values, Disabled Days, and Keyboard Focus
Prepare a date-picker coding interview with date-only values, disabled-day and range rules, calendar keyboard focus, leap-year boundaries, and accessible tests.
A date picker can look correct in a screenshot and still fail a coding interview. The user chooses March 8, refreshes in another time zone, presses an arrow key across a month boundary, and lands on a disabled date. The hard part is keeping calendar date, selected value, keyboard focus, and booking rules consistent. A good solution writes those contracts before building the grid.
This guide uses the W3C WAI-ARIA Authoring Practices date-picker example for an illustrative keyboard pattern, the dialog pattern for modal focus behavior, and MDN's JavaScript Date reference for date/time pitfalls. The interview fixture and disabled-day policy below are original design choices, not candidate reports or universal employer requirements.
What value should a date-only picker store?
A booking check-in day is usually a civil calendar date: year, month, and day without an instant or time zone. If the product contract is date-only, a normalized string such as YYYY-MM-DD is a useful boundary value. A timestamp like 2026-03-08T00:00:00Z describes an instant. Displaying that instant in a negative UTC offset can show March 7, which is not what a user meant when they chose March 8 on a calendar.
MDN explains that JavaScript Date represents a timestamp, while the textual interpretation and display of dates can depend on parsing format and local time zone. Do not assume new Date(dateOnly).toLocaleDateString() will display the same civil day in every locale and zone. Instead, parse the three numeric components, validate that the day exists in that month, and format a user-facing label from those components or a date-only library. If the API needs an instant, make the time zone and time-of-day conversion a separate explicit step.
An interviewer may deliberately supply 2024-02-29, 2025-02-29, and 2026-13-01. The first is valid, the others are invalid. A regex can verify the shape but not the calendar. The component should reject impossible dates rather than normalizing them silently. If you use built-in Date for validation or arithmetic, use a deliberate UTC-component strategy and round-trip the result, with a stated supported year range; avoid local-midnight arithmetic across daylight-saving changes.
Define the picker state before rendering cells
For a single-date picker, keep at least three concepts separate: value is the committed selection, visibleMonth controls the grid currently shown, and focusedDay is the cell the keyboard is navigating. The user may open the dialog to March while the selected value remains February 20; an arrow key should move focus without changing the selected value until the user confirms, if that is the chosen interaction contract.
For a range picker, add start, end, and perhaps an in-progress anchor. Define whether selecting a new start clears the previous end, whether end may equal start, and whether the range may pass through a disabled date. None of those policies is imposed by JavaScript or ARIA. State one policy clearly for the exercise. Here we use an inclusive start/end pair, allow same-day ranges, and reject a range if any day inside it is disabled. A travel product might make a different choice for check-out semantics, so call that out rather than silently borrowing this rule.
A compact state contract helps prevent a common bug: keyboard focus moves to a neighboring month, but the component renders the old month because visibleMonth did not update. Another bug occurs when a parent changes the controlled value while the dialog remains open; the picker must decide whether focus follows the new selection or stays on the user's in-progress position. Make the component's controlled/uncontrolled behavior explicit.
How do you generate the calendar grid?
Compute the weekday of the first day of the displayed month, the number of days in that month, and the leading/trailing cells needed for full weeks. Do not hardcode four, five, or six rows. February can have 28 or 29 days, and its first weekday changes by year. A grid may show adjacent-month days for context, but every visible cell needs an unambiguous full date for click and keyboard logic.
One reliable helper contract is daysInMonth(year, month) with month 1–12, plus addCalendarDays(dateOnly, delta) that returns another normalized date-only value. Test 2024-02-28 + 1 = 2024-02-29, 2024-02-29 + 1 = 2024-03-01, and 2025-02-28 + 1 = 2025-03-01. Crossing December to January needs the year update too. If your helper uses milliseconds, explain why DST cannot change its result; using a local timestamp plus 86,400,000 milliseconds is a fragile shortcut.
Use a stable cell key based on the date string, not only the day number. If a grid includes both March 1 and April 1, key="1" is not unique. The displayed date, focus target, disabled check, and accessible name should all derive from that same full date. This is a small invariant that prevents several UI inconsistencies.
How should disabled-day rules compose?
A date can be disabled because it is before minDate, after maxDate, on a closed weekday, in a blackout interval, or disallowed by the current range anchor. Combine these in a single predicate isDisabled(dateOnly) so pointer selection, keyboard movement, visual styling, and validation agree. Do not let the click handler reject a date while the keyboard accepts it, or render a gray cell that still submits.
There is a product choice about keyboard focus on disabled days. In this exercise, navigation skips disabled days and wraps across month boundaries; a different accessible design could allow focus on disabled cells for discovery while blocking selection. The W3C example is illustrative, not a universal production mandate, and its page explicitly warns about assistive-technology support gaps. Whichever policy you choose, document it and test that a user can reach the next available day without being trapped.
A range introduces another issue: an endpoint may be enabled by itself while the inclusive span crosses a disabled day. Our contract rejects the whole candidate range in that case. A naive isDisabled(end) check misses it. Walk the date-only sequence from start to end or use a precomputed interval structure when the range is large. For a typical small booking calendar, correctness and clear feedback matter more than premature optimization.
| Candidate action | Our exercise policy | Why it matters |
|---|---|---|
| Select an enabled day | Commit single date or range endpoint | Value changes only on activation |
| Arrow onto disabled day | Skip to next enabled date | Focus remains usable |
| Click a disabled day | No selection; explain availability | Pointer and keyboard agree |
| Choose range crossing blackout day | Reject span with a message | Endpoint-only validation is insufficient |
Parent changes minDate after selection | Mark or clear invalid value per controlled contract | Stored value and rules cannot disagree silently |
What keyboard behavior should you implement?
The W3C APG example shows a dialog containing a calendar grid. It starts focus on the selected date when valid, otherwise a suitable current date, and its grid-navigation pattern uses arrow keys for adjacent days, Home/End for week boundaries, Page Up/Down for adjacent months, and Shift plus Page Up/Down for years. Enter or Space activates a date in its example. Describe this as the chosen APG-inspired pattern; do not claim every date picker must use exactly those keys.
A grid should have one active day in the Tab order, with the other day cells reachable by arrow-key navigation. This is commonly implemented by roving tabIndex and moving actual DOM focus to the active cell. An alternative composite-widget pattern can retain focus on the grid and manage aria-activedescendant, but it needs equally careful semantics and testing. Avoid putting 42 day buttons into the ordinary Tab sequence: that makes it exhausting to reach dialog controls.
The modal dialog pattern keeps Tab and Shift+Tab inside the dialog while it is open. Escape closes it, and focus generally returns to the invoking control. For this interview fixture, do not close the dialog on every arrow movement; only a confirmation or cancel action commits or dismisses. On close, if the trigger still exists, return focus there. If the trigger was removed by another state change, choose a sensible fallback target rather than focusing a detached node.
How would you make the calendar understandable to assistive technology?
Give the dialog an accessible name and the grid a month/year label. Each day needs a meaningful accessible name such as “Monday, March 9, 2026,” not simply “9.” Mark the selected date through the appropriate selected state for the chosen grid semantics, and communicate disabled state through the actual disabled behavior and accessible state. The focused day is conveyed by actual focus; it is not automatically the selected day.
When the visible month changes, announce the new month/year without reading the entire grid on every keystroke. The APG example uses an aria-live month heading and a short keyboard-instruction area. Keep instructions concise, and test in at least one real browser/screen-reader combination. APG warns that its examples are illustrative and may have support gaps. Passing a static ARIA attribute inspection is weaker evidence than observing the interaction.
If you use native <input type="date"> instead of a custom calendar, the browser supplies its own widget and behavior. That may be the better production choice when the product does not need custom availability visualization or a range flow. An interview asks for a custom calendar only if the prompt requires one. State the trade-off rather than rebuilding native behavior by reflex.
Which tests distinguish a strong solution?
Test date arithmetic separately from UI events. Cover leap day, non-leap rejection, month/year boundaries, a daylight-saving weekend, and a round trip between stored string and displayed label. Then test UI focus: opening with an existing value, arrowing across a month boundary, skipping a disabled run, selecting with Enter, canceling with Escape, and restoring focus. Add pointer selection to show parity with keyboard activation.
For a range, test same-day range, reversed endpoint attempt, a disabled interior day, and a rule change while the dialog is open. If the user selects March 7 and March 10 while March 9 is blacked out, our exercise should reject the span and keep the in-progress anchor visible with an explanation. It must not submit an invalid range simply because both endpoints are allowed.
Record both value and focus in tests. A test that only asserts the selected text can miss a focus trap; a test that only asserts focus can miss an unintended value commit. The interview advantage is the explanation: each fixture names the invariant it protects. Use representative browser and assistive-technology checks after unit tests because the keyboard pattern is a user interaction, not only a date utility.
A concise interview answer
“I would represent a date-only selection as a validated civil date, not as an implicit midnight timestamp. I would separate selected value, visible month, and keyboard-focused day. One disabled-date predicate would gate clicks, keyboard activation, and range validation. I would use an APG-inspired grid with one active day in the Tab order, move focus by arrow keys, change months at boundaries, and return focus to the trigger when the dialog closes. My tests would cover leap years, DST, disabled spans, month transitions, and keyboard-versus-pointer parity.”
The official sources establish Date behavior and provide an accessible example. The exact disabled-day and range policies here are editorial design choices for a reproducible interview exercise. Ask the interviewer for product rules when they are omitted.
Sources and further reading
- W3C APG: Date Picker Dialog Example — illustrative calendar keyboard and focus behavior, with its production-use caveat.
- W3C APG: Dialog (Modal) Pattern — modal focus loop and return-focus guidance.
- MDN: JavaScript
Date— timestamp model and parsing/display cautions. - MDN:
<input type="date">— native date-control alternative and value format.