Theme
Version
GitHub PyPI Discord
On this page

DateRange

Use CDateRange when users choose a start and end date together. It renders two usable native Date inputs first, then enhances them into one localized Popover and Calendar when JavaScript activates.

Choose one range

Use a fieldset and legend because DateRange submits two controls. It rejects composition inside CField rather than giving two Form fields one field-owned identity.

<fieldset>
  <legend>Travel dates</legend>
  <c-CDateRange start_name="arrival" end_name="departure" />
</fieldset>
Choose one date range
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class BasicDateRange(Component):
    template = """
      <fieldset>
        <legend>Travel dates</legend>
        <c-CDateRange start_name="arrival" end_name="departure" start="2026-08-19" end="2026-08-23" />
      </fieldset>
    """


preview = BasicDateRange()
preview  # noqa: B018

The first Calendar selection starts a draft and the second commits an ordered range. Selecting the same date twice commits a one-day range. Hover and focus preview a draft without changing submitted values.

Submit and reset both endpoints

start_name and end_name create separate canonical YYYY-MM-DD FormData entries. Both endpoints are empty or both are committed. Native input and change events fire only after an uncontrolled range commit.

Submit and reset a date range
Show code
# ruff: noqa: E501 - embedded example markup stays readable as authored

import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class DateRangeForm(Component):
    template = """
      <form x-data="{result:'Submit the form to inspect its canonical values.'}" @submit.prevent="result=JSON.stringify(Object.fromEntries(new FormData($event.target)))">
        <fieldset><legend>Conference stay</legend><c-CDateRange start_name="check_in" end_name="check_out" start="2026-09-14" end="2026-09-18" required /></fieldset>
        <div><button type="submit">Submit dates</button> <button type="reset">Reset dates</button></div>
        <output x-text="result">Submit the form to inspect its canonical values.</output>
      </form>
    """
    css = ":where(form,fieldset){display:grid;gap:.75rem;max-inline-size:32rem}"


preview = DateRangeForm()
preview  # noqa: B018

Without JavaScript the two labeled native Date inputs remain fully usable. Reset restores both initial endpoints and closes the enhanced surface.

Bound the whole interval

min and max are inclusive. unavailable_dates accepts at most 4096 unique dates, and a committed range may not cross any unavailable date. Recheck the same business rules on the server when processing a submission.

Constrain a date range
Show code
# ruff: noqa: E501 - embedded example markup stays readable as authored

import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ConstrainedDateRange(Component):
    template = """
      <fieldset>
        <legend>Available booking window</legend>
        <p id="range-help">August 20 and 24 are unavailable; a range cannot cross either date.</p>
        <c-CDateRange min="2026-08-10" max="2026-09-15" c-unavailable_dates="('2026-08-20','2026-08-24')" c-attrs="{'aria-describedby':'range-help'}" />
      </fieldset>
    """


preview = ConstrainedDateRange()
preview  # noqa: B018

Control value and popup visibility

Client value is either {start, end} or null. value and open are independent controlled channels; while supplied, requests call onValueChange or onOpenChange and wait for the owner to return accepted state through $c-props.

Control DateRange
Show code
# ruff: noqa: E501 - embedded example markup stays readable as authored

import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ControlledDateRange(Component):
    template = """
      <section x-data="{value:{start:'2026-08-19',end:'2026-08-23'},open:false,last:'No request yet'}">
        <p>Range: <strong x-text="value ? `${value.start} through ${value.end}` : 'empty'"></strong>; popup: <strong x-text="open ? 'open' : 'closed'"></strong></p>
        <c-CDateRange start="2026-08-19" end="2026-08-23" $c-props="{value,open,onValueChange:(next,detail)=>{last=`${detail.source}: ${JSON.stringify(next)}`;value=next},onOpenChange:(next,detail)=>{last=`${detail.reason}: ${next}`;open=next}}" />
        <div><button type="button" @click="value={start:'2026-08-25',end:'2026-08-29'}">Set August 25 through 29</button> <button type="button" @click="open=!open">Toggle popup</button></div>
        <output x-text="last">No request yet</output>
      </section>
    """
    css = ":where(section){display:grid;gap:.75rem;max-inline-size:32rem}:where(section p){margin:0}"


preview = ControlledDateRange()
preview  # noqa: B018

Omitting a client prop releases only that channel at its latest committed state.

Follow the active locale

The visible range, Calendar labels, endpoint descriptions, trigger name, and validation text follow the active i18n provider. The canonical Form values do not change when the locale changes.

Localize DateRange
Show code
# ruff: noqa: E501 - embedded example markup and CSS stay readable as authored

import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class DateRangeLocales(Component):
    template = """
      <section class="date-range-locales">
        <article><h3>Provider locale week</h3><c-CDateRange start="2026-08-19" end="2026-08-23" /></article>
        <article><h3>Explicit Monday start</h3><c-CDateRange start="2026-08-19" end="2026-08-23" c-first_day_of_week="1" /></article>
        <article lang="ar" dir="rtl"><h3>RTL scope</h3><c-CDateRange start="2026-08-19" end="2026-08-23" /></article>
      </section>
    """
    css = ":where(.date-range-locales){display:grid;grid-template-columns:repeat(auto-fit,minmax(16rem,1fr));gap:1rem}:where(.date-range-locales article){display:grid;align-content:start;gap:.5rem;padding:.75rem}:where(.date-range-locales h3){margin:0}"


preview = DateRangeLocales()
preview  # noqa: B018

first_day_of_week overrides only the week start. Generic browser parsing of localized date text is not part of this family; its transport always uses native canonical Date controls.

Compare states and presentation

Readonly keeps the range submitted and lets users inspect the Calendar but blocks commits. Disabled blocks interaction and Form participation. Required ranges cannot be cleared.

Compare DateRange states
Show code
# ruff: noqa: E501 - embedded example markup and CSS stay readable as authored

import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class DateRangeStates(Component):
    template = """
      <section class="date-range-states">
        <fieldset><legend>Optional</legend><c-CDateRange start="2026-08-19" end="2026-08-23" clearable /></fieldset>
        <fieldset><legend>Readonly</legend><c-CDateRange start="2026-08-19" end="2026-08-23" readonly variant="filled" size="sm" /></fieldset>
        <fieldset><legend>Disabled</legend><c-CDateRange start="2026-08-19" end="2026-08-23" disabled /></fieldset>
        <fieldset><legend>Invalid</legend><c-CDateRange start="2026-08-19" end="2026-08-23" invalid variant="plain" size="lg" /></fieldset>
      </section>
    """
    css = ":where(.date-range-states){display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:1rem}:where(.date-range-states fieldset){min-inline-size:0}"


preview = DateRangeStates()
preview  # noqa: B018

Use variant, size, the documented --cui-date-range-* variables, and stable data-citry-ui-part selectors for styling. Nested Calendar and Popover variables keep their own public contracts.

The Calendar uses its complete grid keyboard model. Manual screen-reader review remains important for range grids, especially on mobile.

API reference

Inputs

CDateRange server inputs

Server inputs are passed in a template through <c-CDateRange ... /> or in Python through CDateRange(...).

InputTypeDefaultEffect
startCDateRangeDate | None (CDateRangeDate)NoneSets the initial and reset canonical start date or empty range.
endCDateRangeDate | None (CDateRangeDate)NoneSets the initial and reset canonical end date or empty range.
start_namestr | NoneNoneSets the start native transport Form field name.
end_namestr | NoneNoneSets the end native transport Form field name and must differ from start_name.
formstr | NoneNoneAssociates both native transports with an external Form ID.
idstr | NonegeneratedSets the enhanced Button ID and owned ID prefix.
minCDateRangeDate | None (CDateRangeDate)NoneSets the inclusive minimum endpoint.
maxCDateRangeDate | None (CDateRangeDate)NoneSets the inclusive maximum endpoint.
unavailable_datesSequence[CDateRangeDate]()Rejects any range crossing one of at most 4096 unique dates.
requiredbool | NoneNoneRequires both endpoint transports.
disabledbool | NoneNoneBlocks opening selection clearing and Form participation; Form disabledness also wins.
readonlybool | NoneNoneKeeps the range focusable and submitted but blocks commits.
invalidbool | NoneNoneAdds application invalid state to revealed native validity.
clearableboolTrueShows a clear action for an optional non-empty writable range.
dismissibleboolTruePermits Escape outside and focus-outside close requests.
placementCPopoverPlacement (CPopoverPlacement)"bottom-start"Sets the preferred logical Popover placement.
match_widthboolTrueMakes the Popover at least as wide as the visible control.
first_day_of_weekLiteral[1, 2, 3, 4, 5, 6, 7] | NoneNoneOverrides locale week start using ISO Monday 1 through Sunday 7.
show_adjacent_daysboolTrueShows selectable neighboring-month dates in the Calendar.
fixed_weeksboolTrueUses six stable Calendar rows instead of the natural month row count.
placeholderstr"Choose dates"Supplies visible empty-state text when explicitly overridden.
range_labelstr"Choose date range"Names the group popup and empty trigger when explicitly overridden.
change_labelstr"Change date range, {start} to {end}"Formats a committed trigger name and must retain both placeholders when explicitly overridden.
start_labelstr"Start date"Names the native start input and Calendar start endpoint when explicitly overridden.
end_labelstr"End date"Names the native end input and Calendar end endpoint when explicitly overridden.
clear_labelstr"Clear date range"Names the clear Button when explicitly overridden.
unavailable_messagestr"Choose an available date range."Supplies native custom validity if a committed interval becomes unavailable.
variantCDateRangeVariant (CDateRangeVariant)"outline"Selects outline filled or plain field treatment.
sizeCDateRangeSize (CDateRangeSize)"md"Selects coordinated control and text sizing.
class_CClassValue | None (CClassValue)NoneAdds classes to the root and merges with attrs.
styleCStyleValue | None (CStyleValue)NoneAdds styles to the root and merges with attrs.
attrsMapping[str, object] | NoneNoneAdds copied allowed root attributes without replacing owned state identity or runtime markers.

CDateRange client inputs

Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CDateRange />.

InputTypeOmitted behaviorEffect
valueCDateRangeValue | null (CDateRangeValue)Releases control at the latest committed range.Controls both ordered selected and submitted endpoints while supplied.
openboolean | nullReleases control at the latest committed visibility.Controls popup visibility while supplied.
requiredbooleanUses server state.Controls two-endpoint native required validity.
disabledbooleanUses server or Form state.Controls interaction and Form participation.
readonlybooleanUses server or Form state.Controls focusable nonmutable state.
invalidbooleanUses server state.Controls application invalid state.
clearablebooleanUses the server input.Controls the optional clear action.
dismissiblebooleanUses the server input.Controls passive popup dismissal.
placementCPopoverPlacement (CPopoverPlacement)Uses the server input.Controls preferred logical placement.
matchWidthbooleanUses the server input.Controls trigger-width matching.
variantCDateRangeVariant (CDateRangeVariant)Uses the server input.Controls field presentation.
sizeCDateRangeSize (CDateRangeSize)Uses the server input.Controls coordinated sizing.
onValueChangefunctionNo semantic range callback.Receives Calendar clear native and reset range requests.
onOpenChangefunctionNo semantic visibility callback.Receives trigger selection dismissal reset and forced-close requests.

Slots

-

Events

Component events are callback inputs supplied through $c-props. Native browser events remain available through Alpine @... attributes.

CDateRange events

EventSignatureTrigger and timingDetailControlled and cancellation behavior
onValueChange(value: CDateRangeValue | null, detail: CDateRangeValueChangeDetail) => void (CDateRangeValue, CDateRangeValueChangeDetail)A completed Calendar pair clear reset or valid native pair requests another range.{value, previousValue, controlled, source, sourceEvent} (CDateRangeValueChangeDetail)Uncontrolled commits emit native input/change for endpoints that changed; controlled requests wait for the owner.
onOpenChange(open: boolean, detail: CDateRangeOpenChangeDetail) => void (CDateRangeOpenChangeDetail)Trigger selection clear reset Escape outside focus-outside native or forced layer changes request visibility.{reason, controlled, forced, source} (CDateRangeOpenChangeDetail)Uncontrolled requests commit before notification; controlled requests wait except forced safety closure.

Methods

-

CSS

CSS variables to theme the components. Set them on an ancestor or the component itself.

CDateRange CSS variables

Apply these variables to CDateRange or one of its ancestors.

VariableTypePurposeDefault
--cui-date-range-backgroundcolorVisible control clear and fallback input background.Canvas or variant-derived.
--cui-date-range-foregroundcolorText and icon color.CanvasText
--cui-date-range-border-colorcolorControl clear and native endpoint boundaries.Mixed CanvasText.
--cui-date-range-invalid-border-colorcolorRevealed invalid control boundary.Theme error.
--cui-date-range-focus-colorcolorFocus outlines and draft-range indication.Highlight
--cui-date-range-range-backgroundcolorCommitted and preview interval background.Highlight mixed with Canvas.
--cui-date-range-endpoint-backgroundcolorStart and end date background.Theme primary.
--cui-date-range-endpoint-foregroundcolorStart and end date foreground.white
--cui-date-range-radiuslengthControl clear and native input corner radius.0.625rem
--cui-date-range-min-block-sizelengthMinimum interactive control height.2.5rem
--cui-date-range-padding-inlinelengthVisible control and native endpoint inline inset.0.75rem
--cui-date-range-gaplengthEndpoint field and control content gap.0.5rem

Attributes

HTML attributes defined on the components that you can refer to for CSS, inspection, and testing. Read-only.

CDateRange attributes

AttributeElementTypeMeaning
data-emptyRootpresent | absentMarks no committed pair.
data-openRootpresent | absentMirrors effective popup visibility.
data-requiredRootpresent | absentMirrors effective requiredness.
data-disabledRootpresent | absentMirrors effective disabledness.
data-readonlyRootpresent | absentMirrors effective readonly state.
data-invalidRootpresent | absentMirrors application unavailable or revealed native invalidity.
data-variantRootCDateRangeVariant (CDateRangeVariant)Mirrors visual treatment.
data-sizeRootCDateRangeSize (CDateRangeSize)Mirrors coordinated sizing.
data-enhancedRootpresent | absentMarks completed custom control activation.
data-in-rangeCalendar daypresent | absentMarks selectable days within the committed or preview interval.
data-range-startCalendar daypresent | absentMarks the displayed interval start.
data-range-endCalendar daypresent | absentMarks the displayed interval end.
data-range-previewCalendar daypresent | absentMarks draft interval days before the second commit.

Selectors

Selectors for the DOM nodes in the components that you can use for CSS, inspection, and testing.

CDateRange selectors

SelectorElementPurpose
[data-citry-ui-part="date-range"]Root divState reflections root attrs and styling destination.
[data-citry-ui-part="fallback-group"]DivContains the two no-JavaScript and Form transport fields.
[data-citry-ui-part="start-input"]Native Date inputStart endpoint transport validity and reset owner.
[data-citry-ui-part="end-input"]Native Date inputEnd endpoint transport validity and reset owner.
[data-citry-ui-part="enhanced-control"]Layout divGroups the Popover activator and optional clear action.
[data-citry-ui-part="control"]Native ButtonFull-width popup activator and enhanced public focus target.
[data-citry-ui-part="value"]SpanDisplays the localized committed range or placeholder.
[data-citry-ui-part="calendar"]CCalendar rootOwns draft preview endpoint labels and final range selection.
[data-citry-ui-part="clear"]Native ButtonRequests an empty optional range.

Interfaces

Aliases and data shapes referenced above.

Input type aliases

InterfaceDefinition
CDateRangeDatedate | str
CDateRangeVariantLiteral["outline", "filled", "plain"]
CDateRangeSizeLiteral["sm", "md", "lg"]
CDateRangeValueChangeSourceLiteral["calendar", "clear", "reset", "native"]
CPopoverPlacementLiteral["top-start", "top", "top-end", "bottom-start", "bottom", "bottom-end"]
CClassValuestr | Mapping[str, bool] | Sequence[CClassValue]
CStyleValuestr | Mapping[str, object] | Sequence[CStyleValue]

CDateRangeValue

FieldTypeDefaultMeaning
startcanonical string-Inclusive ordered start endpoint.
endcanonical string-Inclusive ordered end endpoint.

CDateRangeValueChangeDetail

FieldTypeDefaultMeaning
valueCDateRangeValue | null (CDateRangeValue)-Requested ordered range or empty state.
previousValueCDateRangeValue | null (CDateRangeValue)-Effective range before the request.
controlledboolean-Whether client value owns the commit.
sourceCDateRangeValueChangeSource (CDateRangeValueChangeSource)-Calendar clear reset or native cause.
sourceEventobject | null-Native interaction event when one exists.

CDateRangeOpenChangeDetail

FieldTypeDefaultMeaning
reasontrigger | selection | clear | reset | escape | outside | focus-outside | native | ancestor | modal-Exact request or forced-close cause.
controlledboolean-Whether client open owns ordinary visibility commits.
forcedboolean-Whether ancestor or modal safety required closure.
sourceobject | null-Associated browser source when one exists.

Translation keys

Catalog keys used by this family. An explicit component input or slot listed in Override takes precedence over the catalog for that instance.

CDateRange translation keys

KeyPurposeVariablesOverrideBrowser updates
citry-ui-date-range-placeholderDisplays the empty enhanced control value.noneplaceholderParent i18n subscription calls tr() because the destination later displays formatted dates.
citry-ui-date-range-labelNames the group popup Calendar and empty trigger.nonerange_label or root attrs accessible name$c-tr updates stable HTML destinations; parent state forwards it into the composed Calendar and dynamic trigger.
citry-ui-date-range-changeNames a trigger with a committed range.`start: str` and `end: str` localized by `citry-ui-date-picker-display`change_label containing {start} and {end}Parent i18n subscription recomputes both formatted endpoints and calls tr().
citry-ui-date-range-start-labelNames the native start input and Calendar start endpoint.nonestart_label$c-tr updates stable native text; parent state forwards it into Calendar endpoint presentation.
citry-ui-date-range-end-labelNames the native end input and Calendar end endpoint.noneend_label$c-tr updates stable native text; parent state forwards it into Calendar endpoint presentation.
citry-ui-date-range-clearNames the optional clear Button.noneclear_label$c-tr updates the stable aria-label destination.
citry-ui-date-range-unavailableSupplies native custom validity when the committed interval becomes unavailable.noneunavailable_messagei18n.bind() updates the browser-owned validity message.