LilyDesignSystem.Blazor.DateTimePicker 0.3.0

dotnet add package LilyDesignSystem.Blazor.DateTimePicker --version 0.3.0
                    
NuGet\Install-Package LilyDesignSystem.Blazor.DateTimePicker -Version 0.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LilyDesignSystem.Blazor.DateTimePicker" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LilyDesignSystem.Blazor.DateTimePicker" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="LilyDesignSystem.Blazor.DateTimePicker" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LilyDesignSystem.Blazor.DateTimePicker --version 0.3.0
                    
#r "nuget: LilyDesignSystem.Blazor.DateTimePicker, 0.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package LilyDesignSystem.Blazor.DateTimePicker@0.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LilyDesignSystem.Blazor.DateTimePicker&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=LilyDesignSystem.Blazor.DateTimePicker&version=0.3.0
                    
Install as a Cake Tool

DateTimePicker (Blazor helper)

A headless Blazor 10 date/time-picking form control: a text field plus an icon button (πŸ“…) that opens a WAI-ARIA APG Date Picker Dialog β€” a month grid with a full keyboard contract. Collects a date, a time, or both, and is locale-correct by construction.

Ships no CSS. The single source of truth is spec/index.md. This file is the human-readable guide.

Install

Add a project reference to LilyDesignSystem.Blazor.DateTimePicker.csproj, or the published LilyDesignSystem.Blazor.DateTimePicker NuGet package.

<ProjectReference Include="path/to/LilyDesignSystem.Blazor.DateTimePicker.csproj" />

Quick start

@using LilyDesignSystem.Blazor.Helpers

<DateTimePicker Label="Appointment date"
                Labels="@Labels"
                @bind-Value="_appointmentDate" />

@code {
    private string _appointmentDate = "";

    private static readonly DateTimePickerLabels Labels = new()
    {
        PreviousYear = "Previous year",
        PreviousMonth = "Previous month",
        PreviousWeek = "Previous week",
        PreviousDay = "Previous day",
        NextDay = "Next day",
        NextWeek = "Next week",
        NextMonth = "Next month",
        NextYear = "Next year",
        Confirm = "OK",
        Cancel = "Cancel",
    };
}

Value is ISO: YYYY-MM-DD for the default Mode="Date", HH:MM for Mode="Time", YYYY-MM-DDTHH:MM for Mode="DateTime". Sortable as a string, unambiguous across locales, and identical to what <input type="date"> posts.

Locale is derived, not chosen

Month names, weekday names, first day of week, numeric field order, and the 12/24-hour clock all come from Locale (a BCP 47 tag) via .NET's CultureInfo / DateTimeFormatInfo β€” never a hardcoded English table. Leave Locale unset and the runtime default culture drives everything; set it and the whole dialog follows, including which day the week starts on and whether 03/04/2026 means 3 April or 4 March.

<DateTimePicker Label="Dyddiad" Labels="@WelshLabels" Locale="cy-GB" @bind-Value="_value" />

Override any single derived default without overriding the rest: FirstDayOfWeek (0=Sunday…6=Saturday), Hour12 (true forces a 12-hour clock regardless of locale).

Constraining selection

<DateTimePicker Label="Appointment date"
                Labels="@Labels"
                Min="@DateTimePicker.FormatIsoDate(new CivilDate(2026, 1, 1))"
                Max="@_maxBookableDate"
                IsDateDisabled="@(iso => DateTimePicker.WeekdayOf(iso) is 0 or 6)"
                @bind-Value="_value" />

Min/Max are inclusive. IsDateDisabled vetoes individual dates β€” here, a weekends-closed clinic. A vetoed day renders aria-disabled="true" plus data-disabled β€” not the disabled attribute β€” so it stays focusable and a screen reader announces it as unavailable rather than going silent; activation is simply refused. Style vetoed days with [data-disabled] or [aria-disabled="true"], not :disabled. The keyboard cursor can still cross a vetoed day inside the range (so arrowing across a blocked week works), but it cannot leave the Min/Max window: there is nothing out there to navigate to.

The full civil-date arithmetic (AddDays, AddMonths, WeekdayOf, ParseIsoDate, …) is public static on DateTimePicker, exported for exactly this reason: wiring Min/Max/IsDateDisabled is date maths too, and the alternative is reaching for a zoned DateTime and reintroducing the local-midnight bug civil dates exist to avoid.

Shortcuts

<DateTimePicker Label="Follow-up date" Labels="@Labels"
                Shortcuts="@Shortcuts" @bind-Value="_value" />

@code {
    private static readonly DateTimeShortcut[] Shortcuts =
    {
        new() { Id = "today", Label = "Today", Days = 0 },
        new() { Id = "two-weeks", Label = "+2 weeks", Days = 14 },
        new() { Id = "one-month", Label = "+1 month", Months = 1 },
    };
}

A shortcut that resolves to a blocked date (outside Min/Max, or vetoed by IsDateDisabled) does nothing rather than landing near it β€” a "+4 weeks" that quietly means "+27 days" is a booking error.

Time and datetime

<DateTimePicker Label="Appointment time" Labels="@TimeLabels"
                Mode="DateTimeMode.Time" MinuteStep="15" Hour12="true"
                @bind-Value="_time" />

Mode="Time" needs Labels.Hour and Labels.Minute; a 12-hour clock (by locale default or Hour12="true") additionally needs Labels.Meridiem. Mode="DateTime" renders both the grid and the time selects and refuses to commit a date with no time, or the reverse β€” half a timestamp is a different truth, not a smaller one.

Typed input

The text field accepts typed dates as well as picker selection. Resolution is tried on blur or Enter, in order: a ParseInput you supply (if any), ISO YYYY-MM-DD, a numeric form whose field order follows the locale, or a form with a written month matched against the locale's own month names (three-character prefix match, so "Sept" finds September, case- and diacritic-insensitively). Two-digit years pivot at 70.

Text that will not parse, or that parses to a blocked date, stays exactly as typed and sets aria-invalid="true", firing OnInvalidInput β€” it is never silently snapped to a nearby legal date the user did not type. Escape in the field discards a pending typed edit, restoring the committed display and clearing the invalid state without committing anything; when nothing is pending the key is left alone.

Supply Labels.Invalid to have the refusal announced as well as marked: a role="status" live region (class hook date-time-picker-status, present-but-empty while the field is valid) fills with your message and is wired to the field via aria-errormessage plus an appended aria-describedby. Without it, aria-invalid flips silently and a screen-reader user who has already left the field never learns their date was refused.

<DateTimePicker Label="Date of birth" Labels="@Labels"
                OnInvalidInput="@(text => _dobError = $"Could not read \"{text}\" as a date")"
                @bind-Value="_dob" />

Clear button

<DateTimePicker Label="Appointment date" Labels="@ClearableLabels" @bind-Value="_value" />

@code {
    private static readonly DateTimePickerLabels ClearableLabels = new()
    {
        PreviousYear = "Previous year", PreviousMonth = "Previous month",
        PreviousWeek = "Previous week", PreviousDay = "Previous day",
        NextDay = "Next day", NextWeek = "Next week",
        NextMonth = "Next month", NextYear = "Next year",
        Confirm = "OK", Cancel = "Cancel",
        Clear = "Clear date",
    };
}

The clear button renders only when Labels.Clear is supplied β€” there is no default, because a default would be a hardcoded English string.

Dialog keyboard help

Supply Labels.Instructions to render keyboard help inside the dialog (class hook date-time-picker-instructions, the dialog's first child) and have the dialog reference it via aria-describedby, so a screen reader speaks it once on open β€” the APG date-picker example ships exactly this affordance. It is visible by default; hide it with your own CSS if you want it screen-reader-only. Like every other label, it renders nothing when not supplied.

Custom glyph

ChildContent replaces the πŸ“… glyph inside the trigger button and receives a DateTimePickerContext of { Value, Open, Display }:

<DateTimePicker Label="Appointment date" Labels="@Labels" @bind-Value="_value">
    <span class="my-icon" aria-hidden="true">@(context.Open ? "β–²" : "πŸ“…")</span>
</DateTimePicker>

Parameters

Full table in spec/index.md Β§4.1. Required: Label, Labels.

Static helpers

Member Purpose
DateTimePicker.Calendar The default glyph, "πŸ“…οΈŽ" (U+1F4C5 + U+FE0E).
DateTimePicker.NextDateTimePickerId() Mint a stable, prerender-safe id prefix.
DateTimePicker.ParseIsoDate / FormatIsoDate ISO date parsing/formatting.
DateTimePicker.AddDays / AddMonths Civil-date arithmetic (AddMonths clamps the day).
DateTimePicker.WeekdayOf / IsoWeek Day-of-week and ISO-8601 week number.
DateTimePicker.ParseDateInput / ParseTimeInput The typed-input parsers, directly callable.
DateTimePicker.FirstDayOfWeekFor(locale) The locale's first weekday.

Full list in spec/index.md Β§4.4.

Accessibility

  • Follows the WAI-ARIA APG Date Picker Dialog pattern: role="dialog", aria-modal="true", role="grid", roving tabindex, full keyboard contract, a real focus trap.
  • Closing the dialog returns focus to whichever element opened it β€” the trigger button after a click, the text field after Alt+ArrowDown β€” per the APG dialog pattern. Click-outside (which includes the component's own text field, honouring aria-modal) closes without moving focus.
  • Vetoed days are aria-disabled, never disabled, so the roving cursor can land on them with real focus and a screen reader announces them as unavailable instead of going silent.
  • The glyph is aria-hidden; the trigger's and dialog's accessible name both come from Label.
  • Tradeoff: a hand-rolled grid has weaker assistive-technology support than <input type="date">, which is the right default for many services. See docs/accessibility.md.

Styling

Class hooks: .date-time-picker (root), .date-time-picker-field, .date-time-picker-input, .date-time-picker-button, .date-time-picker-icon, .date-time-picker-tooltip, .date-time-picker-status (only with Labels.Invalid), .date-time-picker-dialog, .date-time-picker-instructions (only with Labels.Instructions), .date-time-picker-header, .date-time-picker-previous-year / -previous-month / -previous-week / -previous-day / -next-day / -next-week / -next-month / -next-year, .date-time-picker-period, .date-time-picker-time-zone / -time-zone-label / -time-zone-select (only with Labels.TimeZone), .date-time-picker-calendar, .date-time-picker-week-heading, .date-time-picker-weekday, .date-time-picker-week, .date-time-picker-day, .date-time-picker-time, .date-time-picker-time-label, .date-time-picker-hour / -minute / -meridiem, .date-time-picker-shortcuts, .date-time-picker-shortcut, .date-time-picker-footer, .date-time-picker-clear / -cancel / -confirm.

The package ships no CSS and no positioning for the dialog β€” without positioning CSS from the consumer, it renders in normal document flow rather than as an overlay.

Tests

From ../tests/LilyDesignSystem.Blazor.Helpers.Tests:

dotnet test

65 cases for this package, one or more per Β§7 clause.


Lilyβ„’ and Lily Design Systemβ„’ are trademarks.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on LilyDesignSystem.Blazor.DateTimePicker:

Package Downloads
LilyDesignSystem.Blazor.GanttChart

Lily Design System Blazor Gantt chart: a keyboard-accessible Gantt chart composing the headless GanttTable family plus the sibling DateTimePicker helper (twice per edit session) for start/end date editing. Row hierarchy, milestones, percent-complete, today marker, finish-to-start dependency data. Headless, SSR-safe, no CSS.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.0 35 10/5/2026
0.2.1 49 10/1/2026
0.2.0 98 9/16/2026
0.1.0 104 9/2/2026