Saltar al contenido
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
Primeros pasosWebReactSwiftUIJetpack ComposeFlutterReact NativePreajustesTu propio sistema de diseñoUsar Tanrei con agentes de IAMarcaTokens de diseñoSistemas de diseño y personalizaciónTheme StudioColorTipografíaIconosFormaElevaciónMovimientoEstados de interacciónComportamientoDiseño de páginaAccesibilidadTextosTokens de colorTokens de la escala tipográficaTokens de formaTokens de elevaciónTokens de movimientoTokens de estadoTokens de espacio y de medidaComponentesBotónBotón de acción principalBotón segmentadoGrupo de botonesBotón con menúBarra de herramientasBotón de copiarCasilla de verificaciónBotón de opciónInterruptorChipMenúControl deslizanteCampo de control deslizanteCalendarioDateTimePickerGrupo de camposGrupo de botones de opciónGrupo de casillas de verificaciónSelectCampo de selecciónBarra de menúsSubida de archivosSelector de archivos AvanzadoZona de soltar archivosEntrada de texto AvanzadoCampo de textoCampo de chipsCampoCuadro combinado AvanzadoCampo de cuadro combinadoEntrada numérica AvanzadoCampo numéricoEntrada de color AvanzadoCampo de colorEntrada de código de un solo uso AvanzadoCampo de código de un solo usoEntrada de texto incrustada AvanzadoCuadro de redacción de chatTarjetaDivisorCapa modal AvanzadoDiálogoListaFila de listaHoja inferiorHoja lateralCarruselBloque de códigoTablaPopoverPasosAcordeónLista de descripcionesFiguraEstructura de la aplicaciónCabecera de páginaVista de árbolLínea de tiempoVista previa de imágenesTirador de tamañoMarco redimensionableÁrea de desplazamientoInsigniaIndicador de progresoMedidorContador animadoIndicador de cargaSnackbarTooltipAtajo de tecladoTagCódigo en líneaCalloutAvatarEstado vacíoGrupo de tagsEsqueletoElemento de archivoMuestraTexto en marquesinaPestañasVista de pestañasBarra de la aplicaciónBarra de navegaciónPanel de navegaciónBúsquedaEnlacePaginaciónEnlaces anterior y siguienteIndicador de pasosRuta de navegaciónTabla de contenidoTarjeta de enlaceMenú FABEstadoCómo se construye TanreiCómo contribuirLicencia y atribución
InicioComponentesCalendario

Calendario

Muestra un mes como una cuadrícula de días, en la página o dentro de un selector, permite elegir en ella una fecha o un intervalo de fechas, y pasa de un mes a otro como una sola columna de semanas que sigue al desplazamiento y al arrastre.

content_copyVer en Markdown
Categoría
Selección
Tokens
comp.calendar
Nivel
Base
Se usa en
DateTimePicker

Descripción general

Web
tanrei-calendar
schedule
SwiftUI
TanreiCalendar
radio_button_unchecked
Jetpack Compose
TanreiCalendar
radio_button_unchecked
Flutter
TanreiCalendar
radio_button_unchecked
React Native
Calendar
radio_button_unchecked

Generado a partir de docs/status/status.json, specs/calendar.spec.json

Un calendario es una cuadrícula de mes que se coloca en línea. No tiene superficie, margen interior ni elevación propios, así que se sitúa en una página junto a otro contenido, como un formulario de reserva o el filtro de un informe, o dentro de un DateTimePicker, que lo compone sin cambios. Elige una fecha (mode single) o un intervalo continuo de fechas (mode range).

Hoy en la web; planificado en las demás plataformas

El componente web implementa el calendario tal como lo describe esta página: los meses son una sola columna de semanas que sigue al desplazamiento y al arrastre. Las demostraciones son ese componente. SwiftUI, Compose, Flutter y React Native todavía no lo han construido. Sus paquetes conservan el código del selector de fecha retirado, que esta página no documenta.

Una fecha. Desplaza o arrastra los días, o usa los botones, para cambiar de mes

Uso

El calendario define su propio value (o start y end) cuando una persona elige un día, y después emite change. También puedes definirlos tú: una fecha fuera del mes que está en pantalla trae su mes a la vista.

<tanrei-calendar id="check-in" label="Fecha de entrada" value="2026-09-17" min="2026-09-01"></tanrei-calendar>

<!-- Un intervalo, con los días de los meses vecinos en las celdas sobrantes. -->
<tanrei-calendar id="stay" mode="range" label="Estancia" show-adjacent-days
  disabled-dates="2026-09-21 2026-09-22"></tanrei-calendar>

<script type="module">
  import '@tanrei/web/components/calendar';
  const checkIn = document.querySelector('#check-in');
  // Una regla que ninguna lista puede contener es una propiedad: una función que recibe una fecha ISO y devuelve un booleano.
  checkIn.isDateDisabled = (iso) => bookedDays.has(iso);
  // El elemento ya contiene la nueva fecha cuando se emite change.
  checkIn.addEventListener('change', (e) => save(e.detail.value));

  // Un intervalo emite change después de cada elección; end es "" cuando solo hay inicio.
  document.querySelector('#stay').addEventListener('change', (e) => show(e.detail.start, e.detail.end));
</script>
import { Calendar } from '@tanrei/react';

<Calendar
  label="Fecha de entrada"
  value={checkIn}
  min="2026-09-01"
  isDateDisabled={(iso) => bookedDays.has(iso)}
  onChange={(e) => setCheckIn(e.detail.value)}
/>

<Calendar
  mode="range"
  label="Estancia"
  start={stay.start}
  end={stay.end}
  showAdjacentDays
  disabledDates={['2026-09-21', '2026-09-22']}
  onChange={(e) => setStay({ start: e.detail.start, end: e.detail.end })}
/>

No hay ejemplos de SwiftUI, Compose, Flutter ni React Native, porque esas plataformas no han construido el calendario. Los nombres que tendrá en ellas están en la correspondencia entre plataformas, en Especificaciones.

Adónde fue a parar el selector de fecha

Las fechas y las horas se eligen con dos componentes: este calendario y el DateTimePicker que lo contiene.

Antes Ahora
Selector de fecha, modal o acoplado Un DateTimePicker en modo date, modal o docked
Una cuadrícula de mes colocada en la página Un calendario
Selector de fecha con un intervalo Un calendario con mode range, o un DateTimePicker con range
La forma de introducción de texto del selector de fecha Un campo de texto de type date, cuyo botón de selector abre un DateTimePicker
Los tokens del selector de fecha comp.calendar.* para la cuadrícula, comp.date-time-picker.* para la superficie, la cabecera y las acciones

En la web, el elemento anterior que elegía una fecha ya no existe. Una página que dibujaba su cuadrícula en la propia página usa <tanrei-calendar> con los mismos value, min, max, disabled-dates, today, locale y first-day-of-week; una página que lo abría usa <tanrei-date-time-picker>.

Los nombres de Material 3 y de Apple

  • Los selectores de fecha acoplado y modal de Material 3 son un DateTimePicker en modo date. Su selector de intervalo de fechas es lo mismo con range. El preajuste m3-like los dibuja con los contenedores y las esquinas de Material, pasa los meses hacia los lados como páginas y pone acciones de texto al final.
  • Un calendario colocado en una página es el CalendarDatePicker de Flutter y el estilo gráfico del selector de fecha de SwiftUI: TanreiCalendar en SwiftUI, Compose y Flutter, y Calendar en React Native.
  • Los conmutadores de lápiz y de teclado de Material para pasar a la introducción de texto no tienen equivalente: una fecha se escribe en el campo de texto.
  • El selector de fecha gráfico de Apple es un calendario con un eje horizontal, que es lo que define el preajuste cupertino.

Especificaciones

Anatomía

Parte Descripción
1 navigation-row The top row: the view toggle at the start, and the previous and next buttons at the end.
2 view-toggle The displayed month and year with a chevron (expand_more). Pressing it switches between the day view and the year view. It is drawn with the unselected condition's colors.
3 navigation The previous-month and next-month buttons: Tanrei icon-only Buttons of the text variant whose arrows follow the axis the months move along. They are hidden in the year view. A scroll of a wheel or a trackpad, a drag with a finger, a pen or a mouse, and Page Up and Page Down move the month too.
4 weekday-row The narrow names of the seven weekdays, from the first day of the week.
5 day One day in the grid (an item the calendar generates): its number on a round container that carries the day's condition and its state answer. The containers of the days inside a range join into one band. On the vertical axis the days are one unbroken column of weeks: a week two months share is one row, which a month change carries from its place in the one view to its place in the other.
6 year · opcional One year of the year view (an item the calendar generates), on a container that carries the year's condition and its state answer.
7 focus-ring Gap-then-ring indicator around the container of the day, year or view toggle that has keyboard focus.

Propiedades

Propiedad Tipo Valor predeterminado Descripción
mode single | range "single" single: one date, in value. range: one contiguous span of dates, from start to end inclusive.
value string "" The chosen date in single mode, as an ISO 8601 calendar date ("2026-09-17"); empty when none is chosen. The calendar sets it when the person chooses a day and emits change; the product may also set it. A calendar date has no time and no time zone.
start string "" The first date of the chosen range in range mode, as an ISO 8601 calendar date; empty when none is chosen.
end string "" The last date of the chosen range in range mode, as an ISO 8601 calendar date, never before start; empty while only start is chosen.
min string "" The earliest date that can be chosen, as an ISO 8601 calendar date; empty for no limit.
max string "" The latest date that can be chosen, as an ISO 8601 calendar date; empty for no limit.
disabledDates strings Dates inside min and max that cannot be chosen (booked, closed), as ISO 8601 calendar dates. Every platform also takes a predicate (isDateDisabled) for a rule no list can hold, such as every weekend; a day is unavailable when either says so.
today string "" The date drawn as today, as an ISO 8601 calendar date. Empty uses the device's current date in its own time zone. Set it when the product's today is another (a venue's time zone, a server's date) and in tests.
firstDayOfWeek locale | sunday | monday | tuesday | wednesday | thursday | friday | saturday "locale" The weekday in the first column. locale takes it from the locale (Sunday in the United States and Japan, Monday in most of Europe).
locale string "" A BCP 47 language tag ("en-US", "ja-JP") for month and weekday names, digits and the first day of the week. Empty uses the platform's current locale. The calendar is the Gregorian calendar in every locale.
startView day | year "day" Whether the calendar first shows the days of a month or the list of years. year suits a date far from today, such as a birthday.
showAdjacentDays boolean false Fills the grid's spare cells with the days of the previous and next month, drawn quieter than the month's own. Such a day can be chosen when it is available, and a range may start or end on it; choosing one also shows its month.
label string "" The accessible name of the whole calendar ("Check-in date"). Needed when a page has more than one calendar, or when nothing near it says what the date is for. A picker that holds the calendar names it with its own label.
startLabel string "" Range mode: the description of the start day, in the product's language ("Start date"). The product's: Tanrei has no words for it. Empty reads the product's words.
endLabel string "" Range mode: the description of the end day, in the product's language ("End date"). The product's: Tanrei has no words for it. Empty reads the product's words.
previousMonthLabel string "" The accessible name of the previous-month button, in the product's language ("Previous month"). The product's: Tanrei has no words for it. Empty reads the product's words. Required: a glyph alone is not a name.
nextMonthLabel string "" The accessible name of the next-month button, in the product's language ("Next month"). The product's: Tanrei has no words for it. Empty reads the product's words. Required: a glyph alone is not a name.
yearViewLabel string "" The description of the view toggle, whose name is the month and year it shows, in the product's language ("Choose a year"). The product's: Tanrei has no words for it. Empty reads the product's words.
todayLabel string "" Read after today's date and the current year, where the platform has no property that says so (the web's aria-current), in the product's language ("Today"). The product's: Tanrei has no words for it. Empty reads the product's words. With neither, the platform's own word for the present day in the locale, where it has one.

Eventos

Evento Descripción
change The person chose a day that changes the choice. Carries value in single mode, or start and end in range mode (end empty after a start alone). The calendar has already set the props it carries.

Elementos

Los tipos de elemento que el componente repite. Un fixture enumera sus elementos en orden; cada plataforma nombra cada tipo según su propio estilo.

day

One day in the grid. The calendar generates the days from the month, the bounds, the disabled dates and showAdjacentDays; a product and a fixture never list them.

Propiedad Tipo Valor predeterminado Descripción
date string "" The day, as an ISO 8601 calendar date.
disabled boolean false Whether the day is unavailable: before min, after max, in disabledDates or named by isDateDisabled.
adjacent boolean false Whether the day belongs to the previous or next month, shown because showAdjacentDays is true.
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento renderDay TanreiCalendarDay TanreiCalendarDay TanreiCalendarDay CalendarDay
date · Propiedad date date date date date
disabled · Propiedad disabled disabled disabled disabled disabled
adjacent · Propiedad adjacent isAdjacent adjacent adjacent adjacent
Notas A gridcell button in the shadow root with part="day", data-date and, for a day of the neighbouring month, data-adjacent, rendered by renderDay.

year

One year of the year view, from min's year to max's year. The calendar generates them; a product and a fixture never list them.

Propiedad Tipo Valor predeterminado Descripción
year number 0 The year.
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento renderYear TanreiCalendarYear TanreiCalendarYear TanreiCalendarYear CalendarYear
year · Propiedad year year year year year
Notas A button in the shadow root with part="year", rendered by renderYear.

Estados

Comportamiento

Requisitos en formato EARS. Cada implementación de plataforma se prueba contra estos identificadores. El texto de los requisitos se muestra tal como está en la especificación, en inglés.

ID Requisito
CAL-1 THE calendar SHALL lay out, from top to bottom, the navigation row, the weekday row and six rows of seven cells, each cell a square of comp.calendar.cell.size but never smaller than sys.target.touch for touch input and sys.target.pointer for fine pointers (a platform that cannot tell the input type SHALL use sys.target.touch), because the cells are the days' hit areas and cannot extend over each other; the first day of the displayed month SHALL sit in the column of its weekday, and all six rows SHALL be kept when the month needs fewer, so that the calendar keeps its height when the month changes. The navigation row and the weekday row are each one cell tall. THE calendar SHALL draw no surface and no padding of its own: it is as wide as its seven columns, and the page or the picker that holds it gives it its background and inset.
CAL-2 THE calendar SHALL draw each day from exactly one condition, by precedence: selected for the date of single mode and for a range's start and end; in-range for a day strictly between a range's start and end; today for the date today names (or the device's current date) when it is neither; adjacent for a shown day of the previous or next month (CAL-15) when it is none of these; otherwise unselected. It SHALL draw each year of the year view from selected for the displayed year, today for the current year when it is not displayed, and otherwise unselected, and the view toggle from unselected. WHILE a day is unavailable THE calendar SHALL draw it from the disabled tokens of its condition.
CAL-3 THE calendar SHALL draw a day's number in the locale's digits, centred in its cell, in comp.calendar.day.label.typescale and the label color of its condition (comp.calendar.unselected.label.color, and so on), on a container of side comp.calendar.day.container.size centred in the cell, with comp.calendar.day.container.shape corners eased by sys.shape.corner-smoothing, in the container color of its condition, with an outline of comp.calendar.outline.width in the outline color of its condition. The container of an in-range day SHALL instead span the full width of its cell at the same height, so that the containers of consecutive in-range days join into one band that breaks at the ends of a row with square ends; WHILE a range's start and end differ each of them SHALL also draw, behind its own container, the half of its cell toward the other in comp.calendar.in-range.container.color, so that the band runs into it. The band SHALL cover every day the grid shows between start and end, and no empty cell.
CAL-4 THE calendar SHALL draw each weekday's narrow name from the locale ("S", "M" in English; "日", "月" in Japanese) centred in its column, in comp.calendar.weekday.typescale and comp.calendar.weekday.color, and SHALL expose it as a column header named by the weekday's full name; weekday names SHALL take no focus.
CAL-5 THE calendar SHALL draw the navigation row with the view toggle at the start and, in the day view, the previous-month and next-month buttons at the end, comp.calendar.navigation.gap apart, each a Tanrei Button of the text variant and the medium size with an icon and no label, named previousMonthLabel and nextMonthLabel. WHILE comp.calendar.navigation.axis is vertical their glyphs SHALL be arrow_upward and arrow_downward; WHILE it is horizontal they SHALL be arrow_back for the previous month and arrow_forward for the next in every layout direction, pointing toward the start and the end: in a right-to-left layout the icon helper mirrors both, because the icon set marks them as mirrored, and the calendar SHALL NOT exchange the two names or mirror a glyph itself. The previous button SHALL be disabled while the displayed month is min's month and the next button while it is max's month, and in the year view both SHALL be hidden. THE calendar SHALL NOT change the buttons' variant, size or tokens.
CAL-6 THE calendar SHALL draw the view toggle as a container at least comp.calendar.view-toggle.container.height tall with comp.calendar.view-toggle.container.shape corners eased by sys.shape.corner-smoothing, padded by comp.calendar.view-toggle.container.padding-inline at each end, holding the displayed month and year in the locale's form ("September 2026", "2026年9月") in comp.calendar.view-toggle.label.typescale and, comp.calendar.view-toggle.gap after it, the expand_more glyph at comp.calendar.view-toggle.icon.size with the family and fill of an unselected icon, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, both in comp.calendar.unselected.label.color on comp.calendar.unselected.container.color; the glyph SHALL be turned half a turn while the year view is shown, over comp.calendar.motion.state-duration with comp.calendar.motion.state-easing (at once where motion is reduced). WHERE the view toggle is shorter than the minimum target it SHALL extend its hit area to sys.target.touch for touch input and sys.target.pointer for fine pointers without changing its visual size. WHEN it is activated it SHALL switch between the day view and the year view.
CAL-7 WHEN the year view is shown THE calendar SHALL draw, in the space of the weekday row and the six rows of days, the years from min's year to max's year (1900 to 2100 when min or max is empty) in rows of three cells, each a third of the grid's width and comp.calendar.cell.size tall, each year centred in its cell in the locale's digits in comp.calendar.year.label.typescale on a container of comp.calendar.year.container.width by comp.calendar.year.container.height with comp.calendar.year.container.shape corners, colored and outlined as a day of its condition is; the list SHALL scroll, and SHALL open scrolled so that the displayed year's row is the third row in view, or as near to it as the list's ends allow. WHEN the user chooses a year THE calendar SHALL show the day view of the month with the displayed month's number in that year, clamped between min's and max's months, move focus to its active day and SHALL NOT change the choice; WHEN the view toggle is activated in the year view it SHALL show the day view unchanged.
CAL-8 WHILE mode is single, WHEN the user chooses an available day by pointer, touch, Enter or Space THE calendar SHALL set value to it and, if it differs from the value before, emit change once with it.
CAL-9 WHILE mode is range, WHEN the user chooses an available day THE calendar SHALL make it the start and clear the end when no start is chosen, when both are chosen, or when the day is before the start, and otherwise SHALL make it the end (a start and end on the same day make a one-day range); after each choice it SHALL set start and end and emit change once with both, end empty after a start alone. A range SHALL be one contiguous span from start to end inclusive; unavailable days between them SHALL stay inside it, drawn and exposed as unavailable (a product that cannot accept such a span says so after change).
CAL-10 THE calendar SHALL give the day grid one tab stop, the active day: WHEN a month is shown the active day SHALL be the chosen date (single mode) or start (range mode) when it lies in that month, otherwise today when it does, otherwise the first available day of the month, otherwise its first day; only the active day SHALL take focus from Tab, and the keys of CAL-11 move it.
CAL-11 WHILE focus is on a day THE calendar SHALL move the active day and focus with Right Arrow and Left Arrow one day in the direction the arrow points (the next and the previous day left to right, the previous and the next right to left), with Down Arrow and Up Arrow one week later and earlier, with Home and End to the first and last day of its week row, with Page Down and Page Up to the same day of the next and previous month (the last day of that month when it is shorter), and with Shift+Page Down and Shift+Page Up to the same day of the next and previous year, whatever comp.calendar.navigation.axis is; WHEN the new active day lies in another month THE calendar SHALL show that month; it SHALL NOT move the active day before min or after max; unavailable days SHALL be reachable but SHALL NOT be chosen; Enter and Space SHALL choose the active day.
CAL-12 WHILE focus is in the year view THE calendar SHALL give the list one tab stop, the displayed year, and SHALL move focus with Right Arrow and Left Arrow one year in the direction the arrow points, with Down Arrow and Up Arrow one row, and with Home and End to the first and last year, scrolling the focused year into view; Enter and Space SHALL choose the focused year (CAL-7).
CAL-13 WHEN the previous-month or next-month button is activated, a step of a detented wheel moves the month (CAL-33), a scroll or a drag ends on another month than the one shown when it began (CAL-34, CAL-31) or a day of a neighbouring month is chosen (CAL-15), THE calendar SHALL show that month, keep focus where it was, make the active day the same day number of the new month (its last day when the month is shorter; the chosen day, WHEN a day was chosen) and announce the new month and year once, politely; WHEN a year is chosen in the year view it SHALL announce the month and year it shows the same way. A month that a scroll only passes through is shown without an announcement (CAL-34).
CAL-14 WHEN the displayed month changes by a button, a key, a step of a detented wheel (CAL-33) or a chosen day THE calendar SHALL play the change over comp.calendar.motion.page-duration with comp.calendar.motion.page-easing: WHILE comp.calendar.navigation.axis is vertical as one continuous scroll of the weeks, every frame of it being the in-between look of CAL-29 at the progress the easing gives, so that the two months are never drawn as two separate grids and a week they share is neither drawn twice nor replaced; WHILE the axis is horizontal as two pages, the old month's grid sliding out toward the start and the new one in from the end for a later month, and the reverse for an earlier one. Every way of moving the month SHALL move the same thing — on the vertical axis the one progress of CAL-29, on the horizontal one the place of the two pages — and none SHALL have a look of its own: a scroll of a wheel or a trackpad is followed as CAL-33 says and settles as CAL-34 says, and a drag is followed as CAL-30 says and settles as CAL-31 says. WHEN one of them begins while the days are still moving — a month change is playing, or the days are settling after a scroll or a drag — THE calendar SHALL carry on from what is on screen, without a jump and without waiting for the motion to end. WHEN it switches between the day view and the year view it SHALL cross-fade them over the same duration and easing; WHERE motion is reduced it SHALL make all of these changes at once. The weekday row, the navigation row and the calendar's size SHALL NOT move.
CAL-15 WHILE showAdjacentDays is true THE calendar SHALL fill the cells before the displayed month's first day and after its last with the days of the previous and next month, in order, drawn from the adjacent condition unless a higher one applies (CAL-2), and treated as any day for availability (CAL-16), choosing (CAL-8, CAL-9) and the range's band (CAL-3); WHEN the user chooses such a day by pointer or touch THE calendar SHALL choose it as any day, emitting change as CAL-8 and CAL-9 say, and SHALL then show that day's month (CAL-13) with the motion of CAL-14, where the chosen day is one of the month's own and the active day, taking focus if focus was in the grid; a range's start and its end SHALL both do so. A press on an unavailable one SHALL choose nothing and show no other month. WHILE showAdjacentDays is false those cells SHALL be empty, take no press and be hidden from assistive technology.
CAL-16 THE calendar SHALL treat as unavailable every day before min, after max, listed in disabledDates or named by the platform's isDateDisabled predicate. WHILE a day is unavailable THE calendar SHALL render its container, label, outline and opacity from the disabled tokens of its condition (comp.calendar.unselected.container.color at rest, and so on), SHALL NOT make it the chosen date, start or end, SHALL expose it to assistive technology as unavailable (disabled), and SHALL keep it reachable by the arrow keys. WHEN value, start or end names an unavailable day THE calendar SHALL still draw it as chosen, from the disabled tokens of the selected condition, until the person chooses another.
CAL-17 WHEN the calendar is first shown THE calendar SHALL show the month of value or start, otherwise of today, clamped between min's month and max's month, or the year view WHERE startView is year; WHEN the product sets value, start or end to a date outside the displayed month THE calendar SHALL show that date's month, without the motion of CAL-14.
CAL-18 WHILE a day, a year or the view toggle is hovered, keyboard-focused or pressed THE calendar SHALL render its container, label, outline and opacity from the component tokens of its condition in the highest-precedence active state, with precedence pressed over focused over hovered; focus gained by pointer or touch SHALL NOT select the focused state. WHEN these colors change between states THE calendar SHALL animate them over comp.calendar.motion.state-duration with comp.calendar.motion.state-easing, and SHALL change them at once where motion is reduced.
CAL-19 WHEN a day, a year or the view toggle receives keyboard focus THE calendar SHALL show the focus ring around its container — around the circle of comp.calendar.day.container.size for every day, an in-range day included — without animation; focus gained by pointer or touch SHALL NOT show it.
CAL-20 WHEN the calendar's day, year or view toggle is pressed by pointer, touch, Enter or Space THE calendar SHALL show the pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner.
CAL-21 WHILE pressed THE calendar's day, year or view toggle SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole control and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; label, outline, opacity and shape still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.calendar.motion.feedback-duration with comp.calendar.motion.state-easing, fades over comp.calendar.motion.state-duration with comp.calendar.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced.
CAL-22 THE calendar's day, year and view toggle SHALL ease their container corners by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0) and show their keyboard focus as sys.behavior.focus-style names (ring or glow); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README.
CAL-23 THE calendar SHALL expose itself as a group named by label while label is not empty, the day grid as a grid named by the displayed month and year with the weekday names as its column headers, and each shown day as a cell that can be activated, named by its full date in the locale ("Thursday, September 17, 2026") followed by todayLabel for today where the platform has no property for the current date, with its selected state (true for the chosen date and for every day of a range), described by startLabel or endLabel for a range's start and end, and with its disabled state while unavailable; empty cells SHALL be hidden. It SHALL expose the view toggle as a button named by its visible month and year, described by yearViewLabel, with its expanded state (true while the year view is shown), and each year as a button with its selected state and todayLabel for the current year.
CAL-24 THE calendar SHALL take month and weekday names, digits and the locale's first day of the week from locale, or from the platform's current locale while locale is empty, and SHALL use firstDayOfWeek instead of the locale's while it is not locale; WHERE the platform has no week data for the locale it SHALL use the first days of CLDR's weekData, which the platform package embeds. Every date SHALL be a Gregorian calendar date without a time or a time zone, and WHILE today is empty THE calendar SHALL read the device's current date in its own time zone WHEN it is first shown and WHEN the date changes while it is shown.
CAL-25 THE calendar's days, years, weekday names and view toggle SHALL grow with enlarged text rather than clip it: a day's container SHALL grow to hold its number. WHERE the space given to the calendar is narrower than seven minimum targets, the cells SHALL narrow equally so that the grid fits it, keeping their numbers at their size, and the calendar SHALL never be wider than the space it is given.
CAL-26 WHEN the layout direction is right-to-left THE calendar SHALL mirror its layout — the first day of the week in the rightmost column, the view toggle at the right and the navigation buttons at the left, and, on the horizontal axis, the slide of CAL-14 and the side each neighbouring month lies on (the next month toward the left, so that a drag toward the right brings it, CAL-30) — and SHALL NOT mirror the vertical scroll of the weeks, a vertical drag or the vertical arrows. A drag SHALL move the days where the pointer goes, and a scroll of a wheel or a trackpad SHALL move them where it moves the content of a scrolled list, in every layout direction.
CAL-27 WHEN mode, startView or firstDayOfWeek names a value the calendar does not define THE calendar SHALL behave as its default value and in debug builds report the unknown value; WHEN value, start, end, min, max, today or an entry of disabledDates is not an ISO 8601 calendar date, end is before start, value is set in range mode or start or end in single mode THE calendar SHALL ignore it and in debug builds report it; WHEN min is later than max THE calendar SHALL ignore both and report it in debug builds.
CAL-28 THE calendar SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the calendar is shown THE calendar SHALL draw itself for the new direction at once, without a motion of its own, keeping the displayed month, the selection and its focus.
CAL-29 WHILE comp.calendar.navigation.axis is vertical THE calendar SHALL treat the days as one unbroken column of week rows, in which a month's view is the six rows from the row of its first day, and SHALL draw every look between the views of two neighbouring months from one progress, 0 at the one and 1 at the other, with no threshold. The rows SHALL be offset along the column by the progress times the distance between the two views' first rows (four or five rows, each comp.calendar.cell.size tall) and clipped to the grid's six rows, so that a week the two views share is one row that travels from its place in the one to its place in the other. Every day SHALL be drawn at the linear interpolation by the progress between what the one view draws for it and what the other draws: its container, label and outline colors between the tokens of the adjacent condition and those of the unselected condition (comp.calendar.adjacent.label.color and comp.calendar.unselected.label.color, and so on, in the state the day is in) WHERE it is a neighbouring month's day in the one view and the month's own in the other (CAL-2), and its opacity between 1 where a view draws it and 0 where a view does not — a row outside that view's six, or a cell that view leaves empty because showAdjacentDays is false — so that a day coming into view fades in and a day leaving it fades out as far as the weeks have moved. A day that both views draw alike SHALL NOT change, and the band of a range (CAL-3) SHALL follow the same rule, each piece with its day. WHERE the two months are not neighbours (a step of a year, CAL-11; a date the keys reach in a further month) the distance SHALL be six rows, as if the later month's view began where the earlier one's ends.
CAL-30 WHILE the day view is shown, WHEN the user presses the day grid with touch, a pen or the primary button of a mouse and drags past the platform's touch slop (8 points where the platform names none, for every kind of pointer), further along the axis comp.calendar.navigation.axis names than across it, THE calendar SHALL take the drag: the press SHALL choose no day when it ends (CAL-8, CAL-9), the day under it SHALL leave its pressed state as it does when a press ends (CAL-20), and from then until the drag ends THE calendar SHALL move the days by the distance the pointer has travelled along the axis since the press, in the frame the pointer moves and without a motion of its own — toward the next month for a drag toward the top (vertical) or the start (horizontal), toward the previous month for the reverse — never further than the view of the month next to the displayed one, and never past min's or max's month, where the days SHALL stop. WHILE the axis is vertical THE calendar SHALL draw the in-between look of CAL-29 at the progress that travel gives (the travel over the distance between the two views), so that the scroll of the weeks, the fading in of the days coming into view and the change of a neighbouring month's day into one of the month's own all follow the pointer continuously; WHILE the axis is horizontal the two pages of CAL-14 SHALL travel with the pointer, side by side. A press that ends before it has passed the slop SHALL choose the day under it as CAL-8 and CAL-9 say, with every kind of pointer, so that a click of a mouse on a day still chooses it. WHILE a drag the calendar has taken lasts, THE calendar SHALL follow the pointer wherever it goes, outside the grid and the calendar included; no day SHALL be drawn hovered; nothing the calendar draws SHALL be selected as text or picked up by the platform's drag and drop; and a fine pointer SHALL show the platform's cursor for something held and dragged (grabbing) until the drag ends. At rest the cursor over a day SHALL stay the one a pressable day shows: the grid does not advertise the drag, which is a shortcut. A press of another mouse button SHALL take no drag. A drag that is further across the axis than along it when it passes the slop SHALL be left to the scrolling ancestor for its whole length; a scroll of a wheel or a trackpad is followed as CAL-33 says.
CAL-31 WHEN the user releases a drag the calendar has taken THE calendar SHALL choose one month: the neighbouring month the days have moved toward WHERE they are a quarter or more of the distance from the displayed month's view toward its view, and otherwise the displayed month — the same quarter that ends a scroll (CAL-34) — or, WHEN the release is a fling along the axis, the one of the two the fling is toward; a fling is a release faster than the platform's minimum fling velocity (Android's ViewConfiguration.getScaledMinimumFlingVelocity, Flutter's kMinFlingVelocity) or, where the platform names none (the web, SwiftUI, React Native), 0.5 points per millisecond, with every kind of pointer. A release's velocity is the pointer's over its last moves: WHERE the pointer has not moved for longer than the time after which the platform's velocity tracker takes a pointer as stopped (40 milliseconds in Android's VelocityTracker, Jetpack Compose's VelocityTracker and Flutter's VelocityTracker) or, where the platform names none (the web, SwiftUI, React Native), 40 milliseconds before the release THE calendar SHALL take the release as having no velocity, so that a drag that stops where it is wanted and is let go there is no fling, however fast it moved before it stopped. WHERE the chosen month is not the displayed one THE calendar SHALL show it as CAL-13 says and bring the days from where the release left them to its view over comp.calendar.motion.page-duration with comp.calendar.motion.page-easing; WHERE it is the displayed month THE calendar SHALL return them over the same motion and announce nothing. The quarter decides only what a release chooses; nothing drawn changes at it. The release SHALL NOT also choose the day under the pointer, and a drag SHALL never change value, start or end, nor emit change.
CAL-32 WHEN a drag the calendar has taken is cancelled — the platform cancels the pointer (a system gesture, a lost capture), Escape is pressed while it lasts, the product sets value, start or end (CAL-17), or the year view is shown — THE calendar SHALL return the days to the view of the displayed month (the month the product's date has just named, where it named one) over comp.calendar.motion.page-duration with comp.calendar.motion.page-easing and announce nothing; WHEN the product sets value, start or end, or the year view is shown, while a scroll is being followed THE calendar SHALL do the same and SHALL ignore what is left of that scroll (CAL-34). WHILE a drag or a scroll holds the days between two views THE calendar SHALL NOT change the month and year that the view toggle and the grid's name show, the active day, keyboard focus or what it exposes to assistive technology: the days of the month coming into view SHALL be hidden from assistive technology and take no focus and no press until that month is the displayed one, which it becomes by the release that chooses it (CAL-31), by the end of the scroll that chooses it or by the scroll reaching its view (CAL-34). WHERE motion is reduced THE calendar SHALL still move the days with the pointer and with the scroll and draw the in-between look, which the person's own hand moves, and SHALL put them at the chosen month's view at once when the drag is released or cancelled or the scroll ends.
CAL-33 WHILE the day view is shown, WHEN a wheel or a trackpad scrolls over the day grid further along the axis comp.calendar.navigation.axis names than across it, THE calendar SHALL take the scroll and move the days by the distance the scroll reports, one point for each point and in the frame it arrives, as the content of a scrolled list moves — toward the next month for a scroll toward the bottom (vertical) or toward the end (horizontal), toward the previous month for the reverse — without a motion of its own and with no least amount: the slightest scroll moves the days by that much and no further. WHILE the axis is vertical THE calendar SHALL draw the in-between look of CAL-29 at the progress that travel gives; WHILE it is horizontal the two pages of CAL-14 SHALL travel side by side. A scroll that goes on SHALL go on through month after month: WHEN the days reach the view of the neighbouring month THE calendar SHALL make that month the displayed one (CAL-34) and carry on toward the month after it with the distance that is left, in the same frame, so that a scroll never waits for an earlier one to end and no part of it is dropped. The days SHALL stop at the view of min's or max's month; a scroll along the axis that can move them no further SHALL still be the calendar's and SHALL NOT be passed on to a scrolling ancestor. A scroll further across the axis than along it SHALL be left to the scrolling ancestor, and so SHALL a scroll that zooms (a pinch on a trackpad, a wheel turned with the platform's zoom key). WHERE the platform reports a scroll as steps of a detented wheel and not as a distance (a delta in lines or pages on the web; a mouse wheel's notch where the platform tells it from a trackpad), each step SHALL instead move one month, as a press of the previous or next button does (CAL-13, CAL-14), one step after another without waiting for the motion to end.
CAL-34 THE calendar SHALL take a scroll it follows (CAL-33) as ended WHEN the platform says that it has ended, its momentum included, where the platform reports that; where it does not (the web), WHEN no scroll event has arrived for 150 milliseconds, or WHEN the scroll has slowed below 0.1 points per millisecond, measured over its events of the last 100 milliseconds — the tail of a momentum scroll, which need not be waited out: THE calendar SHALL then ignore the events that follow for as long as they are no faster than that, until a faster one arrives or 150 milliseconds pass without any. WHEN a scroll ends with the days between two views THE calendar SHALL choose one of the two — the neighbouring month WHERE the days are a quarter or more of the distance from the displayed month's view toward its view, and otherwise the displayed month, so that a slight scroll returns and a deliberate one need not reach halfway — and SHALL bring the days to the chosen view over comp.calendar.motion.page-duration with comp.calendar.motion.page-easing (at once where motion is reduced, CAL-32). The displayed month SHALL be the month whose view the days last rested on or reached: WHEN a scroll that goes on reaches a neighbouring month's view THE calendar SHALL show that month in the view toggle and the grid's name at once, make its days the ones that take focus and presses, and move the active day and a focused day as CAL-13 says, but SHALL announce nothing yet; the quarter is then counted from that view. THE calendar SHALL announce the month and year once, politely, WHEN a scroll has ended and the days have been given their view, and only WHERE that month is not the one that was displayed when the scroll began; a scroll that returns SHALL announce nothing. WHEN a faster scroll event arrives, a drag is taken or a button or a key moves the month while the days are settling THE calendar SHALL carry on from what is on screen (CAL-14). A scroll SHALL never change value, start or end, nor emit change.
CAL-35 THE calendar SHALL have no words of its own: it SHALL take startLabel, endLabel, previousMonthLabel, nextMonthLabel, yearViewLabel and todayLabel from the instance's prop and, WHILE that is empty, from the product's words of the same name; WHILE neither gives todayLabel it SHALL take the word from the platform's locale data WHERE the platform has the locale's own word for the present day, and it SHALL take no other word from anywhere. IF neither gives a navigation button its name, or a range's start or end its description, THE calendar SHALL still draw the part and keep it working, and in debug builds SHALL report the missing words once, naming the prop. WHILE neither gives yearViewLabel the view toggle SHALL have no description: its name and its expanded state (CAL-23) still say what it shows.
CAL-36 WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE calendar SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on its group, which it SHALL be WHILE it has a name: its label, or one given so — an id reference by the elements it names in the host's tree or a tree around it, a description after its own. WHILE label is not empty it is the group's name, over a name given so. WHEN the product removes the aria- attribute or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE calendar SHALL take it off that element.

Correspondencia de nombres por plataforma

Paridad no significa API idénticas. Cada plataforma expresa el mismo contrato con su propio estilo.

Especificación Web SwiftUI Jetpack Compose Flutter React Native
Componente tanrei-calendar TanreiCalendar TanreiCalendar TanreiCalendar Calendar
mode · Propiedad mode range mode mode mode
value · Propiedad value selection value value value
start · Propiedad start range start start start
end · Propiedad end range end end end
min · Propiedad min minDate min firstDate min
max · Propiedad max maxDate max lastDate max
disabledDates · Propiedad disabledDates disabledDates disabledDates disabledDates disabledDates
today · Propiedad today today today currentDate today
firstDayOfWeek · Propiedad firstDayOfWeek firstDayOfWeek firstDayOfWeek firstDayOfWeek firstDayOfWeek
locale · Propiedad locale locale locale locale locale
startView · Propiedad startView startView startView startView startView
showAdjacentDays · Propiedad showAdjacentDays showsAdjacentDays showAdjacentDays showAdjacentDays showAdjacentDays
label · Propiedad label label label label accessibilityLabel
startLabel · Propiedad startLabel startLabel startLabel startLabel startLabel
endLabel · Propiedad endLabel endLabel endLabel endLabel endLabel
previousMonthLabel · Propiedad previousMonthLabel previousMonthLabel previousMonthLabel previousMonthLabel previousMonthLabel
nextMonthLabel · Propiedad nextMonthLabel nextMonthLabel nextMonthLabel nextMonthLabel nextMonthLabel
yearViewLabel · Propiedad yearViewLabel yearViewLabel yearViewLabel yearViewLabel yearViewLabel
todayLabel · Propiedad todayLabel todayLabel todayLabel todayLabel todayLabel
change · Evento change selection onChange onChanged onChange
Notas Attributes are kebab-case (disabled-dates is a space-separated list of ISO dates, first-day-of-week, start-view, show-adjacent-days, start-label, …); isDateDisabled is a property only (a function from an ISO date string to a boolean). value, start and end are set before change fires, as a native control does. The navigation buttons are <tanrei-button variant="text"> elements with a slotted glyph and no label, in the shadow root. The calendar is not form-associated: the product's field is. A vertical axis reads wheel events with deltaY and a horizontal axis deltaX: a delta in pixels (deltaMode 0) is a distance and is followed point for point (CAL-33), a delta in lines or pages is a step of one month; an event with ctrlKey is a zoom and is left alone. The web reports neither the end of a scroll nor its momentum, so a scroll ends by the 150 ms of silence or the slow-down of CAL-34, and a mouse wheel that the browser reports in pixels is followed as a distance like a trackpad (the web cannot tell them apart). Drags of touch, a pen and a mouse's primary button are followed along the axis (pointer events with setPointerCapture once the drag is taken; touch-action: pan-x on a vertical grid and pan-y on a horizontal one, so the page keeps its pan across it); while a drag is taken the grid shows cursor: grabbing, draws no hover, and the click that follows the release chooses nothing. On the vertical axis the day grid is one keyed list of week rows — the rows of the displayed month and, while a change plays or a drag lasts, those of the month it moves toward — offset by one custom property for the progress (CAL-29), which a drag and a scroll write in each frame and a button's change and a settle transition; a day's colors are mixed by that progress with color-mix() and its opacity follows it, so a shared week is one element throughout. The rows coming into view are inert and aria-hidden until their month is the displayed one. The slop is 8 points for every pointer and a fling 0.5 points per millisecond, as the tabs' and the sheets'. The axis is read from the resolved comp.calendar.navigation.axis custom property once per system. Locale data comes from Intl.DateTimeFormat and Intl.Locale's week info where the browser has it; otherwise the embedded CLDR table. Without locale it reads the nearest lang attribute, then navigator.language. Snapshot harness: the element on a 400 × 440 stage of the surface color (CAL-1's verify). Words: the element has no words of its own. start-label, end-label, previous-month-label, next-month-label, year-view-label and today-label left empty read the product's words, set once with setWords() from @tanrei/web; the element draws again when they change. The present day's word, where neither the instance nor the product gives one, is Intl.RelativeTimeFormat(locale, { numeric: "auto" }).format(0, "day"). Planned. A view: TanreiCalendar(selection: Binding<DateComponents?>, …) for a date and TanreiCalendar(range: Binding<TanreiDateRange>, …) for a range, where TanreiDateRange holds start and end as DateComponents?; the initializer is the mode. Dates are DateComponents with year, month and day in the Gregorian calendar, so no time zone enters. Writing the binding is change. minDate, maxDate, today and disabledDates (Set<DateComponents>) take DateComponents; isDateDisabled is a closure. The words are fields of a TanreiCalendarLabels passed as labels:, whose field names are the spec's; label is the view's own. locale defaults to the \.locale environment. Arrow keys through .onKeyPress; the drag of CAL-30 through a DragGesture(minimumDistance:) on the axis whose predictedEndTranslation gives the fling, which a mouse drives on macOS and iPadOS as a finger does; on macOS the scroll through an NSEvent monitor, whose phase and momentumPhase say when a scroll ends (CAL-34) and whose hasPreciseScrollingDeltas tells a trackpad's distance from a wheel's steps (CAL-33). On the vertical axis the weeks are one column offset by an animatable progress (animatableData), not two pages (CAL-29). The navigation buttons are TanreiButtons with an icon and no label. Built from the date picker's calendar view, which the DateTimePicker also shows. The product's words are read from the \.tanreiWords environment value. Planned. TanreiCalendar(mode, value, start, end, onChange, min, max, disabledDates, isDateDisabled, today, firstDayOfWeek, locale, startView, showAdjacentDays, label, labels, modifier). Dates are TanreiDate(year, month, day), the date picker's value class (API 23 has no java.time.LocalDate). onChange receives a TanreiDateSelection(value, start, end); the caller keeps the state (hoisting). The other words are fields of a TanreiCalendarLabels. Compose has no grid role: the grid sets collectionInfo (7 columns) and each day collectionItemInfo, Role.Button, selected and disabled(). The drag of CAL-30 through pointerInput on the axis with a VelocityTracker for the fling, for touch, a stylus and a mouse alike; a scroll (CAL-33) through PointerEventType.Scroll, a mouse wheel's being steps. On the horizontal axis a HorizontalPager with one page a month is the likely primitive; on the vertical axis the weeks are one column offset by a progress with lerp for the colors (CAL-29), which a pager cannot draw, since it lays out two pages. Arrow keys through onPreviewKeyEvent. The navigation buttons are TanreiButtons with an icon and no label. The product's words are read from LocalTanreiWords. Planned. A widget, as Flutter's own CalendarDatePicker is an inline calendar: TanreiCalendar(mode:, value:, start:, end:, onChanged:, firstDate:, lastDate:, currentDate:, …); firstDate, lastDate and currentDate keep Flutter's names. Dates are DateTimes of which only the year, month and day are read; isDateDisabled is a function. onChanged receives a TanreiDateSelection. Month and weekday names come from TanreiDateLocalizations (English built in, no intl dependency), the first day of the week from the embedded CLDR table. On the horizontal axis paging through a PageView; on the vertical axis the weeks are one column driven by an AnimationController whose value is the progress of CAL-29 (Color.lerp for the colors), moved by a GestureDetector's vertical drag callbacks, with DragEndDetails.primaryVelocity for the fling, for every PointerDeviceKind; a trackpad's scroll (CAL-33) arrives as PointerPanZoomUpdateEvents, whose end event ends it (CAL-34), and a mouse wheel's PointerScrollEvent is a step. The navigation buttons are TanreiButtons with an icon and no label. The product's words are read from the TanreiWords inherited widget, which the app fills from its own localizations. Planned. Controlled: value (or start and end) and onChange({ value, start, end }); the caller moves them. Dates are ISO 8601 strings, never Date objects, so no time zone enters. isDateDisabled is a function prop. The grid has the grid role; days the button role with accessibilityState selected and disabled. The drag of CAL-30 through a PanResponder on the axis (the release's velocity gives the fling) and, on the web renderer, onWheel read as the web package reads it (CAL-33, CAL-34); on the vertical axis the weeks are one column moved by an Animated.Value that is the progress of CAL-29, with interpolate for the colors and the opacity. Locale data from Hermes's Intl.DateTimeFormat; the first day of the week from the embedded CLDR table. The navigation buttons are the package's Button with an icon and no label. The product's words are read from the TanreiWordsProvider context.

Tokens

Token Alias de Valor resuelto (claro)
--tanrei-comp-calendar-adjacent-container-color — #000000 · 0%
--tanrei-comp-calendar-adjacent-disabled-container-color — #000000 · 0%
--tanrei-comp-calendar-adjacent-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-calendar-adjacent-disabled-outline-color — #000000 · 0%
--tanrei-comp-calendar-adjacent-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-calendar-adjacent-focused-container-color — #4b4b4b · 10%
--tanrei-comp-calendar-adjacent-focused-label-color — #4b4b4b
--tanrei-comp-calendar-adjacent-focused-outline-color — #000000 · 0%
--tanrei-comp-calendar-adjacent-focused-state-layer-color — #4b4b4b
--tanrei-comp-calendar-adjacent-hovered-container-color — #4b4b4b · 8%
--tanrei-comp-calendar-adjacent-hovered-label-color — #4b4b4b
--tanrei-comp-calendar-adjacent-hovered-outline-color — #000000 · 0%
--tanrei-comp-calendar-adjacent-hovered-state-layer-color — #4b4b4b
--tanrei-comp-calendar-adjacent-label-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-calendar-adjacent-outline-color — #000000 · 0%
--tanrei-comp-calendar-adjacent-pressed-container-color — #4b4b4b · 10%
--tanrei-comp-calendar-adjacent-pressed-label-color — #4b4b4b
--tanrei-comp-calendar-adjacent-pressed-outline-color — #000000 · 0%
--tanrei-comp-calendar-adjacent-pressed-state-layer-color — #4b4b4b
--tanrei-comp-calendar-adjacent-state-layer-color comp.calendar.adjacent.label.color #4b4b4b
--tanrei-comp-calendar-cell-size — 48px
--tanrei-comp-calendar-day-container-shape sys.shape.corner.full 9999px
--tanrei-comp-calendar-day-container-size — 40px
--tanrei-comp-calendar-disabled-opacity — 1
--tanrei-comp-calendar-focused-opacity — 1
--tanrei-comp-calendar-hovered-opacity — 1
--tanrei-comp-calendar-in-range-container-color sys.color.surface-dim #f1f1f1
--tanrei-comp-calendar-in-range-disabled-container-color — #f1f1f1
--tanrei-comp-calendar-in-range-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-calendar-in-range-disabled-outline-color — #000000 · 0%
--tanrei-comp-calendar-in-range-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-calendar-in-range-focused-container-color — #dcdcdc
--tanrei-comp-calendar-in-range-focused-label-color — #1b1b1b
--tanrei-comp-calendar-in-range-focused-outline-color — #000000 · 0%
--tanrei-comp-calendar-in-range-focused-state-layer-color — #1b1b1b
--tanrei-comp-calendar-in-range-hovered-container-color — #e0e0e0
--tanrei-comp-calendar-in-range-hovered-label-color — #1b1b1b
--tanrei-comp-calendar-in-range-hovered-outline-color — #000000 · 0%
--tanrei-comp-calendar-in-range-hovered-state-layer-color — #1b1b1b
--tanrei-comp-calendar-in-range-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-calendar-in-range-outline-color — #000000 · 0%
--tanrei-comp-calendar-in-range-pressed-container-color — #dcdcdc
--tanrei-comp-calendar-in-range-pressed-label-color — #1b1b1b
--tanrei-comp-calendar-in-range-pressed-outline-color — #000000 · 0%
--tanrei-comp-calendar-in-range-pressed-state-layer-color — #1b1b1b
--tanrei-comp-calendar-in-range-state-layer-color comp.calendar.in-range.label.color #1b1b1b
--tanrei-comp-calendar-motion-feedback-duration sys.motion.role.feedback 450ms
--tanrei-comp-calendar-motion-page-duration sys.motion.duration.medium1 250ms
--tanrei-comp-calendar-motion-page-easing sys.motion.easing.standard cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-calendar-motion-state-duration sys.motion.role.state.compact 200ms
--tanrei-comp-calendar-motion-state-easing sys.motion.easing.state cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-calendar-navigation-axis — 0
--tanrei-comp-calendar-navigation-gap sys.space.space100 8px
--tanrei-comp-calendar-outline-width sys.border.hairline 1px
--tanrei-comp-calendar-pressed-opacity — 1
--tanrei-comp-calendar-selected-container-color sys.color.primary #1b1b1b
--tanrei-comp-calendar-selected-disabled-container-color — #1b1b1b · 12%
--tanrei-comp-calendar-selected-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-calendar-selected-disabled-outline-color — #000000 · 0%
--tanrei-comp-calendar-selected-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-calendar-selected-focused-container-color — #323232
--tanrei-comp-calendar-selected-focused-label-color — #ffffff
--tanrei-comp-calendar-selected-focused-outline-color — #000000 · 0%
--tanrei-comp-calendar-selected-focused-state-layer-color — #ffffff
--tanrei-comp-calendar-selected-hovered-container-color — #2d2d2d
--tanrei-comp-calendar-selected-hovered-label-color — #ffffff
--tanrei-comp-calendar-selected-hovered-outline-color — #000000 · 0%
--tanrei-comp-calendar-selected-hovered-state-layer-color — #ffffff
--tanrei-comp-calendar-selected-label-color sys.color.on-primary #ffffff
--tanrei-comp-calendar-selected-outline-color — #000000 · 0%
--tanrei-comp-calendar-selected-pressed-container-color — #323232
--tanrei-comp-calendar-selected-pressed-label-color — #ffffff
--tanrei-comp-calendar-selected-pressed-outline-color — #000000 · 0%
--tanrei-comp-calendar-selected-pressed-state-layer-color — #ffffff
--tanrei-comp-calendar-selected-state-layer-color comp.calendar.selected.label.color #ffffff
--tanrei-comp-calendar-today-container-color — #000000 · 0%
--tanrei-comp-calendar-today-disabled-container-color — #000000 · 0%
--tanrei-comp-calendar-today-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-calendar-today-disabled-outline-color — #1b1b1b · 38%
--tanrei-comp-calendar-today-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-calendar-today-focused-container-color — #1b1b1b · 10%
--tanrei-comp-calendar-today-focused-label-color — #1b1b1b
--tanrei-comp-calendar-today-focused-outline-color — #1b1b1b
--tanrei-comp-calendar-today-focused-state-layer-color — #1b1b1b
--tanrei-comp-calendar-today-hovered-container-color — #1b1b1b · 8%
--tanrei-comp-calendar-today-hovered-label-color — #1b1b1b
--tanrei-comp-calendar-today-hovered-outline-color — #1b1b1b
--tanrei-comp-calendar-today-hovered-state-layer-color — #1b1b1b
--tanrei-comp-calendar-today-label-color sys.color.primary #1b1b1b
--tanrei-comp-calendar-today-outline-color sys.color.primary #1b1b1b
--tanrei-comp-calendar-today-pressed-container-color — #1b1b1b · 10%
--tanrei-comp-calendar-today-pressed-label-color — #1b1b1b
--tanrei-comp-calendar-today-pressed-outline-color — #1b1b1b
--tanrei-comp-calendar-today-pressed-state-layer-color — #1b1b1b
--tanrei-comp-calendar-today-state-layer-color comp.calendar.today.label.color #1b1b1b
--tanrei-comp-calendar-unselected-container-color — #000000 · 0%
--tanrei-comp-calendar-unselected-disabled-container-color — #000000 · 0%
--tanrei-comp-calendar-unselected-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-calendar-unselected-disabled-outline-color — #000000 · 0%
--tanrei-comp-calendar-unselected-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-calendar-unselected-focused-container-color — #1b1b1b · 10%
--tanrei-comp-calendar-unselected-focused-label-color — #1b1b1b
--tanrei-comp-calendar-unselected-focused-outline-color — #000000 · 0%
--tanrei-comp-calendar-unselected-focused-state-layer-color — #1b1b1b
--tanrei-comp-calendar-unselected-hovered-container-color — #1b1b1b · 8%
--tanrei-comp-calendar-unselected-hovered-label-color — #1b1b1b
--tanrei-comp-calendar-unselected-hovered-outline-color — #000000 · 0%
--tanrei-comp-calendar-unselected-hovered-state-layer-color — #1b1b1b
--tanrei-comp-calendar-unselected-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-calendar-unselected-outline-color — #000000 · 0%
--tanrei-comp-calendar-unselected-pressed-container-color — #1b1b1b · 10%
--tanrei-comp-calendar-unselected-pressed-label-color — #1b1b1b
--tanrei-comp-calendar-unselected-pressed-outline-color — #000000 · 0%
--tanrei-comp-calendar-unselected-pressed-state-layer-color — #1b1b1b
--tanrei-comp-calendar-unselected-state-layer-color comp.calendar.unselected.label.color #1b1b1b
--tanrei-comp-calendar-view-toggle-container-height — 36px
--tanrei-comp-calendar-view-toggle-container-padding-inline sys.space.space150 12px
--tanrei-comp-calendar-view-toggle-container-shape sys.shape.corner.full 9999px
--tanrei-comp-calendar-view-toggle-gap sys.space.space50 4px
--tanrei-comp-calendar-view-toggle-icon-size sys.icon.small 20px
--tanrei-comp-calendar-weekday-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-calendar-year-container-height — 36px
--tanrei-comp-calendar-year-container-shape sys.shape.corner.full 9999px
--tanrei-comp-calendar-year-container-width — 72px
--tanrei-comp-calendar-day-label-typescale sys.typescale.body 400 14px/1.5714
--tanrei-comp-calendar-view-toggle-label-typescale sys.typescale.label 500 14px/1.4286
--tanrei-comp-calendar-weekday-typescale sys.typescale.label-small 500 12px/1.3333
--tanrei-comp-calendar-year-label-typescale sys.typescale.body 400 14px/1.5714

Fixtures

Representaciones canónicas. Todas las plataformas capturan cada fixture con el mismo identificador para poder comparar los resultados.

Generado a partir de specs/calendar.spec.json, tokens/comp/calendar.tokens.json, design/tokens/

Pautas

Cuándo usarlo

Usa un calendario cuando importan las fechas que rodean a la que se elige: una estancia, una reserva, un intervalo para un informe, una fecha cuyo día de la semana cuenta. Colócalo en la página cuando la elección forma parte de la tarea de la página. Cuando una fecha es un campo entre otros, usa un campo de texto de type date y abre un DateTimePicker desde su botón de selector. Quien conoce la fecha la escribe antes de lo que tarda en encontrarla.

Cómo se elige

  • Una fecha. En el modo single, una pulsación o Enter sobre un día disponible lo convierte en value y emite change.
  • Un intervalo. En el modo range, la primera elección es el inicio y la segunda, el fin. Un día anterior al inicio, o una elección cuando ya están definidos los dos extremos, empieza de nuevo. change se emite después de cada elección, con end vacío cuando solo hay inicio, para que el producto pueda mostrar el intervalo a medio elegir. Un inicio y un fin en el mismo día forman un intervalo de un día.
  • Los valores son fechas de calendario ISO 8601 (2026-09-17), sin hora y sin zona horaria. today es la fecha actual del dispositivo salvo que la definas tú, para la zona horaria de un recinto o para las pruebas.

Un intervalo. Elige un inicio y después un fin; una tercera elección empieza de nuevo

El intervalo es una sola banda que va del inicio al fin. Se interrumpe al final de la fila de una semana y continúa en la siguiente.

Días no disponibles

Los días no disponibles salen de min, max, disabledDates y del predicado isDateDisabled de la plataforma, pensado para una regla que ninguna lista puede contener, como todos los fines de semana. Se dibujan más discretos, no se pueden elegir y siguen siendo accesibles con las teclas de flecha, para que la persona sepa que la fecha existe. Los botones de mes anterior y siguiente se detienen en los meses de min y de max, y lo mismo hacen el desplazamiento y el arrastre.

Un intervalo que abarca un día no disponible lo conserva dentro; si tu producto no puede aceptarlo, dilo después de change.

Del 7 de septiembre al 20 de octubre, con el 21 y el 22 de septiembre ocupados

Moverse entre meses

La fila de navegación contiene el conmutador de vista («septiembre de 2026» y un chevron) al inicio y los botones de mes anterior y siguiente al final. Cuatro cosas mueven el mes, y todas mueven lo mismo, de modo que cualquiera puede tomar el relevo de otra sin saltos:

  • Los botones y las teclas (Re Pág y Av Pág, o una tecla de flecha que sale del mes) reproducen el cambio como un movimiento breve.
  • Un desplazamiento con un panel táctil o una rueda sobre los días los mueve tanto como se desplace, mes tras mes mientras dure. Cuando se detiene, el calendario se asienta en un mes: el vecino si los días ya han recorrido una cuarta parte del camino, y si no, el mes del que salió. Una rueda que informa de pasos enteros mueve un mes por paso.
  • Un arrastre de los días con un dedo, un lápiz o un ratón hace lo mismo. Al soltar, se asienta según esa misma cuarta parte, o hacia el lado de un lanzamiento. Una pulsación que no se mueve sigue eligiendo el día que tiene debajo.

En el sistema predeterminado, los meses se disponen a lo largo de un eje vertical y son una sola columna ininterrumpida de semanas. Un mes es una ventana de seis filas sobre esa columna. Una semana que comparten dos meses es una única fila que viaja de su lugar en uno a su lugar en el otro, los días que entran en la vista aparecen con un fundido y un día de un mes vecino se convierte en uno de los propios del mes a medida que las semanas se mueven. Nunca se dibujan dos cuadrículas de mes separadas, y el calendario conserva su altura sea cual sea el mes.

Cuando el movimiento está reducido, un botón o una tecla cambian el mes de inmediato. Los días siguen acompañando al desplazamiento y al arrastre, que mueve la propia mano de la persona, y se colocan de inmediato cuando terminan.

El eje es un ajuste del producto, comp.calendar.navigation.axis, no una propiedad, de modo que todos los calendarios de un producto se mueven igual:

Eje Botones Meses Desplazamiento y arrastre
vertical (el predeterminado) Flechas hacia arriba y hacia abajo Una sola columna de semanas Los verticales mueven los días; los horizontales pasan a la página
horizontal Flechas hacia atrás y hacia delante Dos páginas una junto a otra, con el mes posterior hacia el fin Los horizontales mueven los días; los verticales pasan a la página

Las teclas nunca cambian con el eje.

Un eje horizontal: los meses son páginas

Con m3-like

Un desplazamiento a lo largo del eje pertenece al calendario mientras el puntero esté sobre los días, también en el primer mes y en el último, donde los días se detienen. En una página que se desplaza en la misma dirección, la página no se desplaza desde ahí: en una página larga, deja espacio junto al calendario o a su alrededor, donde un desplazamiento llegue a la página. La fila de los días de la semana, la fila de navegación y todo lo que rodea al calendario desplazan la página como de costumbre.

La vista de años

El conmutador de vista cambia a la vista de años, una lista desplazable que va del año de min al de max (de 1900 a 2100 sin ellos). Al elegir un año se muestra el mismo mes de ese año y no cambia ninguna elección. startView year abre primero ahí, para una fecha lejana de hoy, como una fecha de nacimiento.

Abrir por los años, para una fecha de nacimiento

Días de los meses vecinos

showAdjacentDays llena las celdas sobrantes de la cuadrícula con los días de los meses vecinos, dibujados más discretos. Se pueden elegir cuando están disponibles, y un intervalo puede empezar o terminar en uno de ellos. Al elegir uno con un puntero, se elige y después se muestra su mes, donde es uno de los días propios del mes. Sin esta propiedad, esas celdas quedan vacías y no admiten pulsaciones.

Con los días de los meses vecinos: al elegir el 1 de octubre se muestra octubre

Configuración regional

Los nombres de los días de la semana, el título del mes y las cifras son los de la configuración regional (locale, o la de la plataforma). firstDayOfWeek es el de la configuración regional salvo que indiques un día de la semana. El calendario es gregoriano en todas las configuraciones regionales. Las palabras que no son datos de la configuración regional (los nombres de los botones de navegación, la descripción del conmutador de vista, el inicio y el fin del intervalo) son tuyas: Tanrei no incluye textos propios. Dalas en el calendario, o una sola vez para todo el producto, por idioma, como hace esta página:

import { setWords } from '@tanrei/web';

setWords(
  {
    previousMonthLabel: 'Mes anterior',
    nextMonthLabel: 'Mes siguiente',
    yearViewLabel: 'Elegir año',
    todayLabel: 'Hoy',
    startLabel: 'Fecha de inicio',
    endLabel: 'Fecha de fin',
  },
  { lang: 'es' },
);

Esa llamada es la que ejecuta esta página: todos los calendarios que hay en ella reciben su nombre de esas palabras, y los ejemplos no llevan ninguna. Un calendario dice las suyas donde son distintas: el japonés de abajo da sus cuatro como atributos, y un atributo siempre tiene prioridad. Sin ninguna de ellas, los botones de navegación siguen funcionando y no tienen nombre, y una compilación de depuración notifica cada palabra que falta. Solo la palabra para el día de hoy procede de los datos de configuración regional del navegador cuando no das ninguna.

Una semana que empieza en lunes, y la configuración regional japonesa

Recomendado

  • Dale al calendario un label cuando una página tenga más de uno, o cuando nada cerca de él diga para qué es la fecha («Fecha de entrada»).
  • Limita min y max a las fechas que de verdad se pueden elegir, para que la navegación se detenga donde lo hace la elección.
  • Ofrece un campo de texto junto al calendario, o en su lugar, para las fechas que la persona ya conoce.
  • Mantén los botones al alcance: el desplazamiento y el arrastre son atajos, y no todo el mundo puede usarlos.

Evita

  • No definas el eje por pantalla. Es una sola decisión para todo el producto.
  • No ocultes los días no disponibles. Muéstralos como no disponibles, para que la cuadrícula del calendario siga entera.
  • No pongas un calendario de eje vertical de borde a borde en una página que se desplaza en vertical. Un desplazamiento que empiece sobre sus días nunca llegaría a la página.
  • No trates como una elección el hecho de que un mes entre en la vista. El desplazamiento y el arrastre cambian lo que se muestra, nunca value, y no emiten nada.

Accesibilidad

Rol
group
Etiquetado
The calendar is a group named by label when it has one. The grid is named by the month and year it shows and its column headers are the weekdays' full names. Each day is named by its full date in the locale, with todayLabel after today's where the platform has no current-date property, and exposes its selected and disabled states; a range's start and end are described by startLabel and endLabel. The view toggle is named by its visible month and year, described by yearViewLabel, and exposes whether the year view is expanded. The navigation buttons are named by previousMonthLabel and nextMonthLabel.
Objetivo mínimo
El área de pulsación cumple el tamaño mínimo de objetivo aunque el tamaño visual sea menor. minimum 32px · pointer 44px · spacing 8px · touch 48px

Teclado

Teclas Acción
Tab / Shift+Tab Moves through the view toggle, the previous and next buttons and the active day (or the displayed year).
Arrow keys Move the active day by one day (Left and Right, in the direction the arrow points) or one week (Up and Down), changing the month as needed, whatever axis the months move along; in the year view, by one year or one row.
Home / End The first or last day of the week row; the first or last year.
Page Up / Page Down The same day of the previous or next month.
Shift+Page Up / Shift+Page Down The same day of the previous or next year.
Enter / Space Choose the active day or the focused year; activate the focused button.

Notas

  • The cells are the days' hit areas and are never smaller than the minimum target (CAL-1); the view toggle extends its hit area (CAL-6). The navigation buttons are Tanrei Buttons, which meet the target themselves (BTN-5).
  • Unavailable days stay reachable by the arrow keys, as the WAI-ARIA date picker dialog pattern recommends, so a screen-reader user learns that a date exists and cannot be chosen.
  • Scrolling and dragging to move the month are pointer and touch shortcuts, with a finger, a pen, a mouse, a wheel or a trackpad. The buttons and Page Up / Page Down do the same, so nothing depends on a gesture, and a drag is never the only way (WCAG 2.5.7). While a drag lasts nothing the calendar exposes changes; while a scroll goes on through several months the title follows each month it reaches, but the month is announced once, when the scroll or the release has chosen it (CAL-31, CAL-32, CAL-34), so a screen reader is not read every month that passes.
  • A scroll along the calendar's axis is the calendar's while the pointer is over the day grid (CAL-33): a page that scrolls the same way does not scroll from there. The weekday row, the navigation row and everything around the calendar scroll the page as usual, and a calendar with a horizontal axis leaves the page's vertical scroll alone.
  • A day of a neighbouring month that is chosen by pointer brings its month into view (CAL-15), announced as any month change is. The keys never need it: the arrow keys show a day's month before Enter can choose it (CAL-11).
  • Typing a date is the Text field's job, not the calendar's: a product that wants both puts a Text field beside it, or uses a DateTimePicker opened from the field's trailing button. Typing is the fastest route for many people and the most direct one for screen reader users.
  • The calendar has no words of its own. Month and weekday names, digits and date forms are the platform's locale data, in the locale the calendar was given. Every other word it shows or announces is the product's: a prop of this instance, or the product's words set once.
  • The web exposes the grid with role grid, rows, column headers and gridcells with aria-selected and aria-disabled, and today with aria-current="date" (so todayLabel is not added there). The platforms without a grid role expose the rows and columns through their collection semantics where they have them (Compose's collection info), and otherwise each day as a button in reading order.
  • How a state looks is a value decision of the active design system; components read the state's tokens and never compute the effect themselves.

Generado a partir de specs/calendar.spec.json, tokens/sys/measure.tokens.json

De qué se encarga el componente

  • El calendario es un grupo que recibe su nombre de label. La cuadrícula recibe su nombre del mes que muestra, los encabezados de sus columnas son los nombres completos de los días de la semana, y cada día recibe su nombre de su fecha completa y expone si está seleccionado, no disponible o si es hoy. Los extremos de un intervalo se describen con startLabel y endLabel.
  • Un solo día es el día activo en el orden de tabulación. Las teclas de flecha lo mueven un día o una semana; Inicio y Fin, a los extremos de la semana; Re Pág y Av Pág, un mes, y con Mayús, un año, cambiando de mes cuando hace falta. Enter o Espacio lo eligen.
  • El conmutador de vista es un botón que recibe su nombre del mes y el año que muestra, que se describe con yearViewLabel y que dice si la vista de años está abierta. La lista de años también es una sola parada de tabulación: las teclas de flecha mueven un año o una fila, y Inicio y Fin van a sus extremos.
  • El mes se anuncia una vez, de forma no intrusiva, cuando cambia. Mientras un desplazamiento recorre varios meses, el título sigue a cada uno, pero solo se anuncia el mes en el que termina, y un desplazamiento o un arrastre que vuelve al punto de partida no anuncia nada.
  • Mientras un arrastre mantiene los días entre dos meses, nada de lo que expone el calendario cambia: el título, el día activo y el foco permanecen, y los días que entran en la vista no reciben el foco ni pulsaciones hasta que su mes es el que se muestra.
  • El desplazamiento y el arrastre son atajos: los botones y Re Pág y Av Pág hacen lo mismo, así que nada depende de un gesto.
  • Cada celda mide al menos el objetivo mínimo, y lo sigue midiendo con el texto ampliado. En un espacio más estrecho que siete objetivos, las celdas se estrechan por igual y los números conservan su tamaño, de modo que el calendario nunca es más ancho que el espacio que se le da. Los botones de navegación son Buttons de Tanrei.
  • Los diseños de derecha a izquierda reflejan la cuadrícula, la navegación, las flechas horizontales y el lado en el que queda la página siguiente. Un desplazamiento vertical de las semanas no se refleja, y un arrastre siempre mueve los días hacia donde va el puntero.

Carencias conocidas por plataforma:

  • SwiftUI, Compose, Flutter y React Native no han construido el calendario: cada una lo construirá a partir de esta especificación cuando se ponga al día.
  • Web: el estado seleccionado de un año es aria-current en su botón, que no tiene un estado seleccionado propio.
  • Web: el navegador no informa del final de un desplazamiento ni de su inercia, así que el calendario deduce el final de un silencio breve o de una desaceleración. Un ratón de rueda del que el navegador informa unos pocos píxeles cada vez, girado despacio, vuelve al mes del que salió después de cada muesca.
  • Web: el arrastre todavía no se ha comprobado con un dedo en un dispositivo, y algunas pruebas fallan en Firefox y WebKit.

Qué debes hacer tú

  • Define label y da las palabras que usa el calendario (previousMonthLabel, nextMonthLabel, yearViewLabel, todayLabel, startLabel, endLabel): en el calendario, o una sola vez para todo el producto con setWords(). Tanrei no incluye ninguna. Sin ellas, los botones de navegación funcionan y no tienen nombre, los extremos de un intervalo no tienen descripción, y cada una se notifica en desarrollo; solo la palabra para el día de hoy procede de los datos de configuración regional del navegador. Consulta Los textos del producto.
  • Ofrece una forma de escribir la fecha, como un campo de texto de type date. Para muchas personas es el camino más directo.
  • Si tu producto no puede aceptar un intervalo que abarque un día no disponible, dilo después de change.