Rask
Rask
v0.23.1-alpha.0.494

Components

Calendar

A month of days to pick from: one day, several, or a range, for scheduling and booking. The step that opens it says which, and the type it binds follows.

Drawn and behaving as Flux UI's calendar does, measured against its live docs.

Calendar

CalendarExample.cs
using System.Globalization;

namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarExample : Component
{
    private DateOnly _date;

    protected override Component? Render() =>
        Div.Class("space-y-3")[
            Ui.Calendar.Value(_date).OnChange(date => { _date = date; }).Class("w-fit"),
            Ui.Text.Data("testid", "ui-calendar-state")[
                _date == default ? "No date chosen." : "Chosen: " + _date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)
            ]
        ];
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa

No date chosen.

Basic usage

Bound to a DateOnly that starts on today, and written as a day is picked.

CalendarBoundExample.cs
using System.Globalization;

namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarBoundExample : Component
{
    private readonly Appointment _appointment = new();

    protected override Component? Render() =>
        Div.Class("space-y-3")[
            Ui.Calendar.Bind(() => _appointment.Date).Live().Class("w-fit"),
            Ui.Text["Booked for " + _appointment.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + "."]
        ];

    public sealed class Appointment
    {
        public DateOnly Date { get; set; } = DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date);
    }
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa

Booked for 2026-10-22.

Multiple dates

Several days that need not be next to each other, held in a collection.

CalendarMultipleExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarMultipleExample : Component
{
    private List<DateOnly> _dates = [];

    protected override Component? Render() =>
        Div.Class("space-y-3")[
            Ui.Calendar.Multiple.Values(_dates).OnChange(dates => { _dates = [.. dates]; }).Class("w-fit"),
            Ui.Text.Data("testid", "ui-calendar-days")[$"{_dates.Count} days off"]
        ];
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa

0 days off

Date range

Two months and a UiDateRange. The first click is drawn as the start and written nowhere; the second writes the whole range.

CalendarRangeExample.cs
using System.Globalization;

namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarRangeExample : Component
{
    private UiDateRange _range;

    protected override Component? Render() =>
        Div.Class("space-y-3")[
            Ui.Calendar.Range.Value(_range).OnChange(range => { _range = range; }).Class("w-fit"),
            Ui.Text.Data("testid", "ui-calendar-stay")[
                _range == default ? "No range chosen." : $"Range: {Iso(_range.Start)} to {Iso(_range.End)}"
            ]
        ];

    private static string Iso(DateOnly date) => date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa
November 2026
Su
Mo
Tu
We
Th
Fr
Sa

No range chosen.

Range configuration

A range of three to ten days: while it waits for its end, the days that would make it too short or too long are disabled.

CalendarRangeLimitsExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarRangeLimitsExample : Component
{
    protected override Component? Render() =>
        Ui.Calendar.Range.Value(default(UiDateRange)).MinRange(3).MaxRange(10).Class("w-fit");
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa
November 2026
Su
Mo
Tu
We
Th
Fr
Sa

Size

Xs, Sm, Lg, Xl and Xxl, for the layout around it.

CalendarSizeExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarSizeExample : Component
{
    protected override Component? Render() =>
        Ui.Calendar.Value(default(DateOnly)).Xl.Class("w-fit");
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa

Static

A calendar to look at, with no month steps.

CalendarStaticExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarStaticExample : Component
{
    protected override Component? Render() =>
        Ui.Calendar.Value(DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date)).Static().Xs.Navigation(false).Class("w-fit");
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

Min/max dates

Nothing after today. Flux's "today" shorthand is today's DateOnly.

CalendarMaxExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarMaxExample : Component
{
    protected override Component? Render() =>
        Ui.Calendar.Value(default(DateOnly)).Max(DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date)).Class("w-fit");
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa

Unavailable dates

Yesterday and tomorrow struck through: holidays, booked days.

CalendarUnavailableExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarUnavailableExample : Component
{
    private static readonly DateOnly Today = DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date);

    protected override Component? Render() =>
        Ui.Calendar.Value(default(DateOnly)).Unavailable([Today.AddDays(-1), Today.AddDays(1)]).Class("w-fit");
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa

With today shortcut

A button that comes back to this month, and picks today once there.

CalendarTodayExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarTodayExample : Component
{
    protected override Component? Render() =>
        Ui.Calendar.Value(default(DateOnly)).WithToday().Class("w-fit");
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa

Selectable header

The month and the year in the header are selects.

CalendarHeaderExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarHeaderExample : Component
{
    protected override Component? Render() =>
        Ui.Calendar.Value(default(DateOnly)).SelectableHeader().Class("w-fit");
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa

With inputs

A field over the month to type the day into, and a range's start and end over two. A whole date typed is chosen at once; a pick is written into the field.

CalendarInputsExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarInputsExample : Component
{
    protected override Component? Render() =>
        Div.Class("flex flex-wrap items-start gap-8")[
            Ui.Calendar.Value(default(DateOnly)).WithInputs().Class("w-fit"),
            Ui.Calendar.Range.Value(default(UiDateRange)).WithInputs().Class("w-fit")
        ];
}
Live result
//
October 2026
Su
Mo
Tu
We
Th
Fr
Sa
//
//
October 2026
Su
Mo
Tu
We
Th
Fr
Sa
November 2026
Su
Mo
Tu
We
Th
Fr
Sa

Fixed weeks

Always six weeks, so paging never changes the height.

CalendarFixedWeeksExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarFixedWeeksExample : Component
{
    protected override Component? Render() =>
        Ui.Calendar.Value(default(DateOnly)).FixedWeeks().Class("w-fit");
}
Live result
October 2026
Su
Mo
Tu
We
Th
Fr
Sa

Start day

The week starts on the locale's own first day unless this says otherwise.

CalendarStartDayExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarStartDayExample : Component
{
    protected override Component? Render() =>
        Ui.Calendar.Value(default(DateOnly)).StartDay(DayOfWeek.Monday).Class("w-fit");
}
Live result
October 2026
Mo
Tu
We
Th
Fr
Sa
Su

Open to

The month it opens on while nothing is chosen: next month, a year on.

CalendarOpenToExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarOpenToExample : Component
{
    private static readonly DateOnly Today = DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date);

    // The first of next month, a year on.
    private static readonly DateOnly NextYear = new DateOnly(Today.Year, Today.Month, 1).AddMonths(1).AddYears(1);

    protected override Component? Render() =>
        Ui.Calendar.Value(default(DateOnly)).OpenTo(NextYear).Class("w-fit");
}
Live result
November 2027
Su
Mo
Tu
We
Th
Fr
Sa

Force open to

ForceOpenTo opens on that month whatever is chosen: today is picked here, and a year on is shown.

CalendarForceOpenToExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarForceOpenToExample : Component
{
    private static readonly DateOnly Today = DateOnly.FromDateTime(TimeProvider.System.GetLocalNow().Date);

    private static readonly DateOnly NextYear = new DateOnly(Today.Year, Today.Month, 1).AddMonths(1).AddYears(1);

    protected override Component? Render() =>
        Ui.Calendar.Value(Today).OpenTo(NextYear).ForceOpenTo().Class("w-fit");
}
Live result
November 2027
Su
Mo
Tu
We
Th
Fr
Sa

Week numbers

CalendarWeekNumbersExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarWeekNumbersExample : Component
{
    protected override Component? Render() =>
        Ui.Calendar.Value(default(DateOnly)).WeekNumbers().Class("w-fit");
}
Live result
October 2026
#
Su
Mo
Tu
We
Th
Fr
Sa
40
41
42
43
44

Localization

The app's culture unless Locale names another: month and weekday names and the first day of the week.

CalendarLocaleExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CalendarLocaleExample : Component
{
    protected override Component? Render() =>
        Ui.Calendar.Value(default(DateOnly)).Locale("ja-JP").Class("w-fit");
}
Live result
2026年10月
日
月
火
水
木
金
土

Behaviour

The mode is the opening step

No step is Flux's default, one day: Ui.Calendar.Bind(() => m.Delivery) binds a DateOnly or a DateOnly?. Ui.Calendar.Multiple binds a collection of days and Ui.Calendar.Range a UiDateRange, so a range bound to a single day does not compile. A mode given as a value, Mode(Ui.CalendarMode.Range), has to agree with what is bound; when it does not, rendering throws and names both.

A second click on the chosen day clears it: default(DateOnly), or null through a nullable binding. Min and Max disable the days outside them, and a month step too once the whole month that way is out of reach.

A range is always whole

UiDateRange(Start, End) is written only once it has both ends. The first click is held by the calendar and drawn as the start, the stretch to the day under the pointer or the keyboard is drawn as it would be, and the second click, on that day or a later one, writes the range; a click before the start begins again from there. Count, Contains(day) and Between(a, b) read it, and default(UiDateRange) is nothing chosen. Flux's DateRange presets are UiDateRange.Of(preset, today, …), shown on the date picker's page.

The keyboard

One day is in the Tab order: the chosen one, else today, else the 1st. The keys and the focus are the runtime's (data-rask-contain-keys and data-rask-focus-follows on the grid, data-rask-focus-target on the tab stop), and none of them scrolls the page behind the grid.

Key
What happens
ArrowLeft, ArrowRightA day back or on, stepping over disabled days and into the next month; the focus goes with the day.
ArrowUp, ArrowDownA week back or on.
PageUp, PageDown, Home, EndPage a month and let the focus fall to the page, as Flux's do.
Enter, SpacePick the day.

On a phone

Under 640 px the calendar folds to one month whatever Months says, and its days grow to 44 px a thumb can press. Each cell is a gridcell with aria-selected, and its button carries the full date as its name ("Thursday, January 15, 2026").

Reference

Ui.Calendar

flux:calendar

A month of days to pick from. Ui.Calendar picks a day, Ui.Calendar.Multiple several, Ui.Calendar.Range a range.

Step
Takes
What it does
.Bind(…)() => what the mode bindsThe member it reads and writes: () => m.Day, a collection of days, or a UiDateRange. Or Value with OnChange.
.Value(…)a DateOnly, days, or a UiDateRangeWhat it holds, for a page that holds it itself. Values(days) for several.
.OnChange(…)a handler given the new valueRuns with the new value when a day is picked.
.AfterBind(…)a handler given the new valueRuns after Bind has written the model.
.Validate(…)a validatorA rule the value is checked by when it is written.
.Live()boolWrites the model as soon as a day is picked, not with the next action.
.Mode(…)
Ui.CalendarMode
Single
Multiple
Range

Each is a step of its own too, unless an HTML tag has its name.

What is picked, as a value. The opening step says it; this has to agree with what is bound.
.Multiple()boolFlux's multiple: the same as Mode(Ui.CalendarMode.Multiple).
.Min(…)a DateOnlyThe earliest day that can be picked. Days before it are disabled.
.Max(…)a DateOnlyThe latest day that can be picked.
.Unavailable(…)DateOnly days: [today.AddDays(1)]Days that cannot be picked: holidays, booked days.
.Size(…)
Ui.CalendarSize
Base
Xs
Sm
Lg
Xl
Xxl

Each is a step of its own too, unless an HTML tag has its name.

How large the day cells are. 44px when unset.
.StartDay(…)
DayOfWeek
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

Each is a step of its own too, unless an HTML tag has its name.

The day the week starts on. The locale's own when unset.
.Months(…)intHow many months are shown side by side. One, or two for a range, when unset.
.MinRange(…)intThe fewest days a range may hold.
.MaxRange(…)intThe most days a range may hold.
.OpenTo(…)a DateOnlyThe day it opens to while nothing is chosen.
.ForceOpenTo()boolOpens to OpenTo whatever is chosen.
.Navigation()boolFalse hides the month steps.
.Static()boolA calendar to look at: no day can be picked.
.WeekNumbers()boolShows each week's number.
.SelectableHeader()boolMakes the month and the year in the header selects.
.WithToday()boolA button that comes back to this month, and picks today once there.
.WithInputs()boolA field over the months to type the day into, or a range's start and end. None for several days.
.FixedWeeks()boolAlways six weeks, so paging never changes the height.
.Locale(…)stringThe locale the names and the week follow: "ja-JP". The app's culture when unset.
Confirm