Skip to content

FRD: Availability Picker

Document Summary

FieldDetails
Feature NameAvailability Picker
StatusDraft
OwnerDavid Holmes
ContributorsDesign, Engineering
Target Releasev2.0.0 (P2)
Related LinksRoadmap item #54
Last Updated2026-05-26

Introduction

Overview

Availability Picker is a scheduling widget that combines InlineCalendar with a time-slot grid to let users select one or more date-and-time combinations for availability or appointment scheduling. It produces a structured payload of selected slots that consumers can submit to a booking API. The existing InlineCalendar and ScheduleCalendar provide the date-selection foundation; this widget adds the time-slot grid, multi-day selection, and structured output.

Goals

  • Deliver an <AvailabilityPicker> component that pairs a calendar with a time-slot grid.
  • Support multi-day selection for indicating availability across multiple dates.
  • Produce a structured payload of selected date/time slots.
  • Compose existing InlineCalendar for date selection.
  • Ship Storybook stories with realistic scheduling scenarios.

Non-Goals

  • Backend booking API integration or availability checking.
  • Timezone conversion (consumer provides slots in the desired timezone).
  • Recurring availability rules (one-off selection only).
  • Integration with external calendar services (Google Calendar, Outlook, etc.).
  • Drag-to-select across multiple time slots (click-to-toggle only).

Scope

In Scope

AreaDescription
Component<AvailabilityPicker> combining calendar and time-slot grid
Date SelectionInlineCalendar composition for selecting dates
Time-Slot GridGrid of time slots (configurable interval) for each selected date
Multi-DayUsers can select slots across multiple days
Structured PayloadonChange fires with { date: string; time: string }[]
StatesLoading time slots, no available slots, error
StoriesStorybook stories for single-day, multi-day, and edge cases

Out of Scope

AreaReason
Backend availability checkingConsumer provides available slots; widget handles selection
Timezone conversionConsumer responsibility
Recurring availabilityOne-off selection; recurring patterns are ScheduleCalendar territory
Drag-to-selectClick-to-toggle is simpler and more accessible
External calendar syncApplication-layer concern

Users and Pain Points

User Groups

UserDescriptionNeeds
DevelopersEngineers building scheduling featuresA composable date+time picker using DS components
DesignersDesign-system consumersConsistent scheduling UX aligned with DS tokens
End UsersPeople selecting availability or booking timesIntuitive date and time selection across multiple days

Pain Points

UserPain PointImpact
DevelopersBuilding date+time selection requires wiring calendar, time grid, and state management from scratchComplex implementation, inconsistent UX
End UsersMany scheduling UIs require selecting one day at a time, requiring multiple round-tripsFrustrating multi-day availability indication

Definitions

TermDefinition
Availability PickerA widget combining a calendar and time-slot grid for selecting date/time combinations
Time SlotA selectable time option within a day (e.g., “9:00 AM”, “9:30 AM”)
Slot IntervalThe time gap between consecutive slots (e.g., 15 min, 30 min, 60 min)
Selected SlotsThe structured list of date/time combinations the user has chosen

Current State

Existing Behavior

InlineCalendar provides an always-visible month calendar with day selection and event dots. ScheduleCalendar provides a custody/assignment calendar with party-based day coloring. Neither component includes a time-slot selection layer or multi-day availability indication.

Current Limitations

  • No time-slot grid component exists in the design system.
  • InlineCalendar supports single-date selection only.
  • No structured payload for date+time combinations.
  • No multi-day selection pattern.

Existing Workarounds

  • Developers build custom time-slot grids with buttons or radio groups.
  • Multi-day availability requires multiple calendar interactions or external tools (Calendly, etc.).

Proposed Solution

Summary

Introduce <AvailabilityPicker> that renders an InlineCalendar on the left (or top on mobile) and a time-slot grid on the right (or bottom). When a user selects a date on the calendar, the time-slot grid shows available slots for that date. Users toggle slots on/off by clicking. Selected slots across all dates are tracked and emitted via onChange. A summary section below shows all selected date/time pairs.

Key Capabilities

  • Calendar-driven date selection via InlineCalendar.
  • Configurable time-slot grid (interval, start hour, end hour).
  • Multi-day selection: slots selected on one date persist when navigating to another date.
  • Structured onChange payload with all selected slots.
  • Summary display of selected slots with remove capability.
  • Available vs. unavailable slot states (consumer marks which slots are available).

User Experience

Users see a calendar alongside a time-slot grid. They click a date on the calendar, and the grid shows time slots for that day. Available slots are clickable; unavailable slots are greyed out. Clicking a slot toggles it. A summary below lists all selected slots across all dates. Users can remove a slot from the summary by clicking its remove button.

Developer Experience

<AvailabilityPicker
availableSlots={[
{ date: "2026-06-01", times: ["09:00", "09:30", "10:00", "10:30"] },
{ date: "2026-06-02", times: ["14:00", "14:30", "15:00"] },
]}
slotInterval={30}
onChange={(selected) => setSelectedSlots(selected)}
/>

Requirements

IDRequirementPriorityNotes
FR-001AvailabilityPicker renders a calendar alongside a time-slot gridMust-
FR-002Time-slot grid shows slots for the selected calendar dateMust-
FR-003Multi-day selection persists slots across date navigationMust-
FR-004onChange fires with structured { date, time }[] payloadMust-
FR-005Summary section displays all selected slots with removeShould-
FR-006Available vs. unavailable slot states are visually distinctMust-
FR-007Loading, empty (no slots), and error states are handledMust-

Priority Definitions

PriorityMeaning
MustRequired for this feature to ship.
ShouldImportant, but can be deferred if needed.
CouldNice to have. Not required for initial release.

Functional Requirements

IDRequirementUser BenefitPriority
FUNC-001availableSlots prop accepts { date: string; times: string[] }[] defining which slots are selectableConsumer controls availabilityMust
FUNC-002slotInterval prop configures the display interval (15, 30, or 60 minutes)Flexible for different use casesShould
FUNC-003Clicking an available slot toggles it selected/unselectedIntuitive toggle UXMust
FUNC-004Unavailable time slots (not in availableSlots) render as disabled/greyedClear distinction from availableMust
FUNC-005Navigating to a different calendar date shows that date’s time slots while preserving prior selectionsMulti-day workflowMust
FUNC-006Summary section lists all selected slots grouped by date with remove buttonsReview and edit selectionsShould
FUNC-007Calendar dates with available slots show event dotsVisual cue for availabilityShould
FUNC-008When no slots are available for a selected date, display “No available times” messageClear feedbackMust
FUNC-009onChange fires on every selection/deselection with the complete selected setReal-time payload updatesMust

Non-Functional Requirements

IDRequirementCategoryPriority
NFR-001Renders in under 50ms for 30 days with 20 slots eachPerformanceMust
NFR-002Time-slot grid and calendar are keyboard-navigableAccessibilityMust
NFR-003Works in light and dark themesThemingMust
NFR-004Responsive layout: side-by-side on desktop, stacked on mobileResponsivenessMust
NFR-005No new runtime dependencies beyond InlineCalendarMaintainabilityMust

API / Interface Requirements

Public API

NameTypeDescriptionRequired
availableSlotsAvailableDay[]Dates and their available time stringsYes
valueSelectedSlot[]Currently selected date/time pairs (controlled)No
onChange(selected: SelectedSlot[]) => voidSelection change callbackYes
slotInterval15 | 30 | 60Time slot interval in minutesNo (default: 30)
minDatestringEarliest selectable date (YYYY-MM-DD)No
maxDatestringLatest selectable date (YYYY-MM-DD)No
isLoadingbooleanShow loading state for time slotsNo
errorstringError messageNo
classNamestringAdditional CSS classesNo

Types

interface AvailableDay {
date: string; // YYYY-MM-DD
times: string[]; // HH:MM in 24h format
}
interface SelectedSlot {
date: string; // YYYY-MM-DD
time: string; // HH:MM
}

Example Usage

import { AvailabilityPicker } from "@/components/ui/availability-picker";
const [selected, setSelected] = useState<SelectedSlot[]>([]);
<AvailabilityPicker
availableSlots={[
{ date: "2026-06-01", times: ["09:00", "09:30", "10:00", "14:00", "14:30"] },
{ date: "2026-06-02", times: ["10:00", "10:30", "11:00"] },
]}
value={selected}
onChange={setSelected}
slotInterval={30}
/>

API Notes

  • Time strings use 24-hour format ("09:00", "14:30").
  • Date strings use YYYY-MM-DD format matching InlineCalendar.
  • value/onChange pattern enables controlled usage; uncontrolled is also supported (internal state).
  • Calendar event dots are auto-generated from availableSlots dates.

Accessibility Requirements

IDRequirementNotes
A11Y-001Time-slot grid uses role="grid" with aria-label including the selected date-
A11Y-002Each time slot is role="gridcell" and focusable with keyboardArrow keys navigate, Space/Enter toggles
A11Y-003Selected slots have aria-pressed="true"Toggle button pattern
A11Y-004Unavailable slots have aria-disabled="true" and are skipped by arrow-key navigation-
A11Y-005Summary section is a list with remove buttons having descriptive aria-labele.g., “Remove June 1 at 9:00 AM”
A11Y-006Calendar delegates to InlineCalendar’s existing accessibility-

Checklist

  • Keyboard support is defined.
  • Focus behavior is defined.
  • Screen reader behavior is defined.
  • Color contrast requirements are met.
  • Reduced motion behavior is considered.
  • Semantic HTML expectations are documented.
  • ARIA usage is defined only where needed.

Content and Documentation Requirements

IDRequirementLocationPriority
DOC-001Storybook docs page with overview and props tableStorybookMust
DOC-002”When to use / When not to use” guidanceStorybook docsMust
DOC-003Stories for single-day selection, multi-day selection, and fully booked datesStorybookMust
DOC-004Story demonstrating responsive layout (side-by-side and stacked)StorybookShould

Dependencies

DependencyTypeOwnerStatusNotes
InlineCalendarEngineeringDesign SystemReadyDate selection
ButtonEngineeringDesign SystemReadyTime slot toggle buttons and remove buttons
BadgeEngineeringDesign SystemReadySelected slot summary tags
Design tokensDesignDesign SystemReadyColor, spacing, typography

Risks and Tradeoffs

Risk / TradeoffImpactMitigation
No timezone handling may confuse users across timezonesSlots displayed without timezone contextDocument that consumer should display timezone label; add optional timezone display prop
Large availability sets (30 days x 48 half-hour slots) create many DOM nodesPerformance concernRender only the active date’s slots; summary uses virtual list if needed
Click-to-toggle (not drag) may be slow for selecting many consecutive slotsMore clicks requiredAdd “Select All Available” shortcut button for the active date
No recurring availabilityUsers indicating weekly patterns must select each day individuallyIntentionally one-off; recurring is ScheduleCalendar’s domain

Open Questions

IDQuestionOwnerStatusResolution
Q-001Should a “Select All” button be included for each date’s available slots?David HolmesOpen
Q-002Should the time-slot grid support a duration concept (e.g., 1-hour meeting vs. 30-min slot)?David HolmesOpen
Q-003Should a timezone label be displayed?David HolmesOpen
Q-004Should the calendar support range selection (start date to end date) in addition to multi-day?David HolmesOpen

Acceptance Criteria

IDCriteriaRelated Requirement
AC-001Calendar renders via InlineCalendar with event dots on dates with available slotsFR-001, FUNC-007
AC-002Selecting a date shows available time slots in the gridFR-002, FUNC-001
AC-003Clicking an available slot toggles it selected/deselectedFUNC-003
AC-004Unavailable slots render as disabled and non-interactiveFUNC-004
AC-005Navigating to another date preserves prior selectionsFR-003, FUNC-005
AC-006onChange fires with all selected slots on each toggleFR-004, FUNC-009
AC-007Summary section lists selected slots with working remove buttonsFR-005, FUNC-006
AC-008”No available times” message shows for dates without slotsFUNC-008
AC-009Layout is side-by-side on desktop and stacked on mobileNFR-004
AC-010All Storybook stories render without errorsDOC-003
AC-011Component passes axe accessibility auditNFR-002

LLM Handoff Instructions

Expected LLM Behavior

  • Follow the requirements and acceptance criteria in this document.
  • Do not expand scope beyond the In Scope section.
  • Respect the Out of Scope section.
  • Compose InlineCalendar for date selection; do not rebuild calendar UI.
  • Use Button with toggle styling for time slots.
  • Use Badge for selected-slot summary items.
  • Implement responsive layout with CSS media queries or container queries.
  • Only render time slots for the currently selected date; track all selections in state.
  • Place stories under the SaaS Widgets Storybook section.

LLM Should Not

  • Implement timezone conversion.
  • Add recurring availability rules.
  • Add drag-to-select interaction.
  • Modify existing InlineCalendar component.
  • Add external calendar service integration.

Decision Log

DateDecisionReasonOwner
2026-05-26Click-to-toggle rather than drag-to-selectMore accessible, simpler implementation, works on touchDavid Holmes
2026-05-26Consumer provides available slots rather than generating from rulesDecouples from backend logic; more flexibleDavid Holmes
2026-05-26Compose InlineCalendar rather than building a new calendarReuses tested calendar component; avoids duplicationDavid Holmes

Document History

DateAuthorChange
2026-05-26David HolmesInitial draft