メインコンテンツへスキップ
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
はじめにWebReactSwiftUIJetpack ComposeFlutterReact Nativeプリセット自分のデザインシステムを作るAI エージェントと Tanrei を使うブランドデザイントークンデザインシステムとカスタマイズTheme Studioカラータイポグラフィアイコン形状エレベーションモーションインタラクション状態振る舞いレイアウトアクセシビリティ言葉カラートークンタイプスケールトークン形状トークンエレベーショントークンモーショントークン状態トークンスペースと寸法のトークンコンポーネントボタンメインアクションボタンセグメントボタンボタングループメニュー付きボタンツールバーコピーボタンチェックボックスラジオボタンスイッチチップメニュースライダースライダーフィールドカレンダーDateTimePickerフィールドグループラジオグループチェックボックスグループセレクトセレクトフィールドメニューバーファイルアップロードファイルピッカー 上級者向けドロップゾーンテキスト入力欄 上級者向けテキストフィールドチップフィールドフィールドコンボボックス 上級者向けコンボボックスフィールド数値入力欄 上級者向け数値フィールドカラー入力欄 上級者向けカラーフィールドワンタイムコード入力欄 上級者向けワンタイムコードフィールド埋め込みテキスト入力欄 上級者向けチャットコンポーザーカード区切り線モーダルレイヤー 上級者向けダイアログリストリスト行ボトムシートサイドシートカルーセルコードブロックテーブルポップオーバーステップアコーディオン説明リスト図アプリシェルページヘッダーツリービュータイムライン画像プレビューハンドルサイズ変更できる枠スクロールエリアバッジプログレスインジケーターメーターナンバーティッカーローディングインジケータースナックバーツールチップキーボードショートカットタグインラインコードコールアウトアバター空の状態タググループスケルトンファイル項目色見本マーキーテキストタブタブビューアプリバーナビゲーションバーナビゲーションドロワー検索リンクページネーション前後のリンクステッパーパンくずリスト目次リンクカードFAB メニューステータスTanrei の作り方コントリビューションライセンスと帰属
ホームコンポーネントカレンダー

カレンダー

1 か月を日のグリッドで表示し、そこから日付を 1 つ、または日付の期間を 1 つ選べるようにします。ページの中にもピッカーの中にも置け、月は週が 1 列に連なったものとして、スクロールやドラッグに追従して動きます。

content_copyMarkdown で表示
カテゴリ
選択
トークン
comp.calendar
レベル
基礎
使用先
DateTimePicker

概要

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

生成元 docs/status/status.json, specs/calendar.spec.json

カレンダーは、ページの中に置く月のグリッドです。自前のサーフェス、パディング、エレベーションを持たないので、予約フォームやレポートの絞り込みのように、ほかの内容と並べてページに置けます。DateTimePicker の中でも、そのまま使われます。日付を 1 つ選ぶ(mode が single)か、連続した日付の期間を 1 つ選びます(mode が range)。

現在は Web のみ、ほかは予定

Web コンポーネントは、このページの説明どおりにカレンダーを実装しています。月は週が 1 列に連なったものとして、スクロールやドラッグに追従します。デモはそのコンポーネントです。SwiftUI、Compose、Flutter、React Native はまだ作っていません。これらのパッケージには廃止した日付ピッカーのコードが残っていますが、このページでは説明しません。

日付を 1 つ。日をスクロールするかドラッグするか、ボタンを使うと月が変わります

使い方

利用者が日を選ぶと、カレンダーは自分で value(または start と end)を設定し、そのあとで change を発火します。プロダクト側から設定することもできます。表示中の月の外の日付を設定すると、その月が表示されます。

<tanrei-calendar id="check-in" label="チェックイン日" value="2026-09-17" min="2026-09-01"></tanrei-calendar>

<!-- 期間。空いたマスに前後の月の日も表示します。 -->
<tanrei-calendar id="stay" mode="range" label="宿泊" 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');
  // リストでは書けない規則はプロパティで渡します。ISO の日付を受け取り、真偽値を返す関数です。
  checkIn.isDateDisabled = (iso) => bookedDays.has(iso);
  // change が発火した時点で、要素はすでに新しい日付を持っています。
  checkIn.addEventListener('change', (e) => save(e.detail.value));

  // 期間では選ぶたびに change が発火します。開始日だけのとき、end は "" です。
  document.querySelector('#stay').addEventListener('change', (e) => show(e.detail.start, e.detail.end));
</script>
import { Calendar } from '@tanrei/react';

<Calendar
  label="チェックイン日"
  value={checkIn}
  min="2026-09-01"
  isDateDisabled={(iso) => bookedDays.has(iso)}
  onChange={(e) => setCheckIn(e.detail.value)}
/>

<Calendar
  mode="range"
  label="宿泊"
  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 })}
/>

SwiftUI、Compose、Flutter、React Native の例はありません。これらのプラットフォームは、まだカレンダーを作っていないからです。各プラットフォームでの名前は、仕様のプラットフォーム対応表にあります。

日付ピッカーの移り先

日付と時刻は 2 つのコンポーネントで選びます。このカレンダーと、それを収める DateTimePicker です。

以前 現在
日付ピッカー(モーダル、ドック) date モードの DateTimePicker(modal または docked)
ページに置く月のグリッド カレンダー
期間を選ぶ日付ピッカー mode を range にしたカレンダー、または range を付けた DateTimePicker
日付ピッカーの文字入力フォーム type を date にしたテキストフィールド。そのピッカーボタンから DateTimePicker を開きます
日付ピッカーのトークン グリッドは comp.calendar.*、サーフェス・ヘッダー・アクションは comp.date-time-picker.*

Web では、日付を選ぶ以前の要素はなくなりました。そのグリッドをページに描いていた箇所は、<tanrei-calendar> に置き換えます。value、min、max、disabled-dates、today、locale、first-day-of-week はそのまま使えます。ピッカーとして開いていた箇所は、<tanrei-date-time-picker> に置き換えます。

Material 3 と Apple の名前との対応

  • Material 3 のドック型とモーダルの date picker は、date モードの DateTimePicker です。date range picker は、それに range を付けたものです。m3-like プリセットは、Material のコンテナと角丸で描き、月を横にめくり、アクションを末尾寄せのテキストボタンにします。
  • ページに置くカレンダーは、Flutter の CalendarDatePicker や、SwiftUI の graphical スタイルの日付ピッカーにあたります。SwiftUI、Compose、Flutter では TanreiCalendar、React Native では Calendar です。
  • Material の、文字入力に切り替える鉛筆とキーボードのボタンに相当するものはありません。日付はテキストフィールドで入力します。
  • Apple の graphical な日付ピッカーは、軸を横にしたカレンダーです。cupertino プリセットがそう設定しています。

仕様

構成要素

パーツ 説明
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 · 任意 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.

プロパティ

プロパティ 型 既定値 説明
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.

イベント

イベント 説明
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.

アイテム

このコンポーネントが繰り返すアイテムの種類です。フィクスチャはアイテムを順に並べ、各プラットフォームはそれぞれの流儀で種類ごとに名前を付けます。

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.

プロパティ 型 既定値 説明
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.
仕様 Web SwiftUI Jetpack Compose Flutter React Native
アイテム renderDay TanreiCalendarDay TanreiCalendarDay TanreiCalendarDay CalendarDay
date · プロパティ date date date date date
disabled · プロパティ disabled disabled disabled disabled disabled
adjacent · プロパティ adjacent isAdjacent adjacent adjacent adjacent
備考 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.

プロパティ 型 既定値 説明
year number 0 The year.
仕様 Web SwiftUI Jetpack Compose Flutter React Native
アイテム renderYear TanreiCalendarYear TanreiCalendarYear TanreiCalendarYear CalendarYear
year · プロパティ year year year year year
備考 A button in the shadow root with part="year", rendered by renderYear.

状態

振る舞い

EARS 記法による要件です。各プラットフォームの実装は、この ID に対応するテストで検証されます。要件の原文は仕様ファイルのものをそのまま掲載しています。

ID 要件
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.

プラットフォーム別の名前対応

パリティは API が同一であることを意味しません。同じ契約を、各プラットフォームの流儀で表現します。

仕様 Web SwiftUI Jetpack Compose Flutter React Native
コンポーネント tanrei-calendar TanreiCalendar TanreiCalendar TanreiCalendar Calendar
mode · プロパティ mode range mode mode mode
value · プロパティ value selection value value value
start · プロパティ start range start start start
end · プロパティ end range end end end
min · プロパティ min minDate min firstDate min
max · プロパティ max maxDate max lastDate max
disabledDates · プロパティ disabledDates disabledDates disabledDates disabledDates disabledDates
today · プロパティ today today today currentDate today
firstDayOfWeek · プロパティ firstDayOfWeek firstDayOfWeek firstDayOfWeek firstDayOfWeek firstDayOfWeek
locale · プロパティ locale locale locale locale locale
startView · プロパティ startView startView startView startView startView
showAdjacentDays · プロパティ showAdjacentDays showsAdjacentDays showAdjacentDays showAdjacentDays showAdjacentDays
label · プロパティ label label label label accessibilityLabel
startLabel · プロパティ startLabel startLabel startLabel startLabel startLabel
endLabel · プロパティ endLabel endLabel endLabel endLabel endLabel
previousMonthLabel · プロパティ previousMonthLabel previousMonthLabel previousMonthLabel previousMonthLabel previousMonthLabel
nextMonthLabel · プロパティ nextMonthLabel nextMonthLabel nextMonthLabel nextMonthLabel nextMonthLabel
yearViewLabel · プロパティ yearViewLabel yearViewLabel yearViewLabel yearViewLabel yearViewLabel
todayLabel · プロパティ todayLabel todayLabel todayLabel todayLabel todayLabel
change · イベント change selection onChange onChanged onChange
備考 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.

トークン

トークン 参照先 解決値(ライト)
--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

フィクスチャ

基準となる描画パターンです。全プラットフォームが同じ ID でスナップショットを撮り、結果を比較できるようにしています。

生成元 specs/calendar.spec.json, tokens/comp/calendar.tokens.json, design/tokens/

ガイドライン

使う場面

選ぶ日付の前後が意味を持つときに使います。宿泊、予約、レポートの期間、曜日が大事な日付などです。選ぶことがそのページの作業なら、カレンダーをページに置きます。日付がほかの項目と並ぶ 1 項目にすぎないなら、type を date にしたテキストフィールドを使い、そのピッカーボタンから DateTimePicker を開きます。日付がわかっている人には、探すより入力するほうが速いからです。

選び方

  • 日付を 1 つ。 single モードでは、選べる日を押すか Enter で value になり、change を発火します。
  • 期間。 range モードでは、最初に選んだ日が開始日、次に選んだ日が終了日です。開始日より前の日を選ぶか、両端が決まったあとに選ぶと、選び直しになります。change は選ぶたびに発火し、開始日だけのときは end が空です。そのため、選びかけの期間もプロダクト側で表示できます。開始日と終了日が同じなら 1 日の期間です。
  • 値 は ISO 8601 の暦日(2026-09-17)で、時刻もタイムゾーンも持ちません。today は、設定しなければ端末の今日の日付です。会場のタイムゾーンやテストのために設定できます。

期間。開始日、終了日の順に選びます。3 回目に選ぶと選び直しです

期間は、開始日から終了日までの 1 本の帯です。週の行の端で切れ、次の行に続きます。

選べない日

選べない日は、min、max、disabledDates と、各プラットフォームの isDateDisabled 述語で決まります。述語は、毎週末のようにリストでは書けない規則のためのものです。選べない日は控えめに描かれ、選べませんが、矢印キーではたどれるので、その日付が存在することは伝わります。前後のボタンは min の月と max の月で止まり、スクロールとドラッグもそこで止まります。

選べない日をまたぐ期間は、その日を含んだままです。プロダクトがそれを受け付けられない場合は、change のあとでそう伝えます。

9 月 7 日から 10 月 20 日まで。9 月 21 日と 22 日は埋まっています

月の移動

ナビゲーションの行には、先頭にビューの切り替え(「2026年9月」とシェブロン)、末尾に前後のボタンがあります。月を動かす手段は 4 つあり、どれも同じものを動かします。そのため、ある手段の途中で別の手段に移っても、表示が飛ぶことはありません。

  • ボタン と キー(Page Up と Page Down、または月の外に出る矢印キー)は、短いモーションで月を切り替えます。
  • トラックパッドやホイールで日の上を スクロール すると、スクロールした分だけ日が動き、続けているあいだ何か月でも進みます。止まると、カレンダーはどちらかの月に落ち着きます。隣の月まで 4 分の 1 以上進んでいれば隣の月、そうでなければ元の月です。段階で報告されるホイールは、1 段で 1 か月動きます。
  • 指、ペン、マウスで日を ドラッグ しても同じです。離すと同じ 4 分の 1 で落ち着き、フリングしたときはその向きの月になります。動かさずに離した押下は、これまでどおりその日を選びます。

既定のシステムでは、月は縦の軸に沿って並び、週が途切れずに連なった 1 本の列になっています。1 か月は、その列に開いた 6 行の窓です。2 つの月にまたがる週は 1 つの行で、一方の月での位置からもう一方での位置へ移動します。見えてくる日はフェードインし、隣の月の日は、週が動くにつれてその月の日の色に変わります。月のグリッドを 2 つ別々に描くことはなく、どの月でもカレンダーの高さは変わりません。

動きを減らす設定では、ボタンやキーは月をすぐに切り替えます。スクロールとドラッグは利用者自身の手が動かすものなので、その場合も日は追従し、終わった時点ですぐに月の位置に収まります。

軸はプロパティではなく、プロダクトの設定 comp.calendar.navigation.axis です。そのため、プロダクトの中のどのカレンダーも同じ向きに動きます。

軸 ボタン 月 スクロールとドラッグ
vertical(既定) 上下の矢印 週が連なった 1 列 縦の操作で日が動き、横の操作はページに渡ります
horizontal 前後の矢印 横に並ぶ 2 ページ。あとの月が末尾側 横の操作で日が動き、縦の操作はページに渡ります

キー操作は軸によって変わりません。

横の軸:月はページになります

m3-like で表示

ポインターが日の上にあるあいだ、軸に沿ったスクロールはカレンダーのものです。日が止まる最初の月と最後の月でも同じです。同じ向きにスクロールするページでは、そこからはページがスクロールしません。長いページでは、カレンダーの横やまわりに、スクロールがページに届く場所を残してください。曜日の行、ナビゲーションの行、カレンダーのまわりでは、いつもどおりページがスクロールします。

年のビュー

ビューの切り替えを押すと、年のビューになります。min の年から max の年まで(どちらもなければ 1900 年から 2100 年まで)の、スクロールするリストです。年を選ぶと、その年の同じ月を表示し、選択は変わりません。誕生日のように今日から遠い日付には、startView を year にして年のビューから始めます。

誕生日のために、年から始める

前後の月の日

showAdjacentDays を付けると、グリッドの空いたマスに前後の月の日を控えめに表示します。選べる日ならそのまま選べ、期間の端にもできます。ポインターで選ぶと、その日を選んだうえでその月を表示し、そこではその月の日として描かれます。付けなければ、それらのマスは空で、押しても何も起こりません。

前後の月の日を表示:10 月 1 日を選ぶと 10 月になります

ロケール

曜日名、月の見出し、数字はロケール(locale、または各プラットフォームのロケール)に従います。firstDayOfWeek は、曜日を指定しなければロケールに従います。暦はどのロケールでもグレゴリオ暦です。ロケールのデータではない言葉(ナビゲーションボタンの名前、表示切り替えの説明、期間の開始と終了)は、プロダクトのものです。Tanrei は言葉を同梱しません。カレンダーに書くか、このページがしているように、プロダクト全体に、言語ごとに一度だけ渡します。

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

setWords(
  {
    previousMonthLabel: '前の月',
    nextMonthLabel: '次の月',
    yearViewLabel: '年を選択',
    todayLabel: '今日',
    startLabel: '開始日',
    endLabel: '終了日',
  },
  { lang: 'ja' },
);

この呼び出しは、このページが実際に実行しているものです。ページ上のカレンダーはすべてこの言葉で名前が付き、サンプルにはどの言葉も書いていません。ほかと違う言葉を使うカレンダーは、自分の属性で渡します。下の英語のカレンダーは、4 つの言葉を属性で渡しています。属性は常に優先されます。どれも渡さなくても、ナビゲーションボタンは動作しますが名前を持たず、デバッグビルドが足りない言葉をそれぞれ報告します。渡さないときにブラウザーのロケールデータから取るのは、今日を表す言葉だけです。

月曜始まりの週と、英語のロケール

推奨

  • ページに複数のカレンダーがあるときや、近くに何の日付かを示すものがないときは、label を付けます(「チェックイン日」など)。
  • min と max は、実際に選べる日付に合わせます。そうすれば、選べる範囲の端でナビゲーションも止まります。
  • わかっている日付のために、カレンダーの横や代わりにテキストフィールドを用意します。
  • ボタンをいつでも使えるようにしておきます。スクロールとドラッグは近道で、だれもが使えるわけではありません。

非推奨

  • 軸を画面ごとに変えない。プロダクト全体で 1 つに決めるものです。
  • 選べない日を隠さない。選べないものとして表示すれば、グリッドが欠けずに保たれます。
  • 縦の軸のカレンダーを、縦にスクロールするページの幅いっぱいに置かない。日の上で始めたスクロールが、ページに届かなくなります。
  • 月が見えてきたことを、選択として扱わない。スクロールとドラッグが変えるのは表示だけで、value は変わらず、イベントも発火しません。

アクセシビリティ

ロール
group
ラベル付け
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.
最小ターゲット
見た目のサイズが小さくても、ヒット領域は最小ターゲットサイズを満たします。 minimum 32px · pointer 44px · spacing 8px · touch 48px

キーボード操作

キー 動作
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.

備考

  • 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.

生成元 specs/calendar.spec.json, tokens/sys/measure.tokens.json

コンポーネントが担うこと

  • カレンダーは label を名前とするグループです。グリッドは表示中の月を名前とし、列見出しは曜日の正式な名前です。各日はその日付を省略せずに名前とし、選択中か、選べないか、今日かを伝えます。期間の両端は startLabel と endLabel で説明します。
  • タブ順には、アクティブな日が 1 つだけ入ります。矢印キーで 1 日または 1 週、Home と End で週の端、Page Up と Page Down で 1 か月、Shift を併用すると 1 年動き、必要なら月も変わります。Enter か Space で選びます。
  • ビューの切り替えは、表示中の月と年を名前とするボタンで、yearViewLabel で説明され、年のビューが開いているかどうかを伝えます。年のリストもタブ順に 1 つだけ入り、矢印キーで 1 年または 1 行、Home と End で両端に動きます。
  • 月が変わると、一度だけ控えめに読み上げます。スクロールが何か月も進むあいだ、見出しは通った月を順に示しますが、読み上げるのは止まった月だけです。元の月に戻ったスクロールやドラッグは、何も読み上げません。
  • ドラッグで日が 2 つの月のあいだにあるとき、カレンダーが伝える内容は何も変わりません。見出し、アクティブな日、フォーカスはそのままで、見えてくる日は、その月が表示中の月になるまでフォーカスも押下も受けません。
  • スクロールとドラッグは近道です。ボタンと Page Up・Page Down でも同じことができるので、ジェスチャーに頼る操作はありません。
  • どのマスも最小ターゲット以上で、文字を大きくしてもそれを保ちます。ターゲット 7 つ分より狭い場所では、マスが等しく狭まり、数字の大きさは変わりません。そのため、カレンダーが与えられた幅より広くなることはありません。ナビゲーションボタンは Tanrei のボタンです。
  • 右から左のレイアウトでは、グリッド、ナビゲーション、左右の矢印、次のページがある側が反転します。週の縦のスクロールは反転せず、ドラッグでは、日は常にポインターの動く向きに動きます。

プラットフォームごとの既知の制約:

  • SwiftUI、Compose、Flutter、React Native は、まだカレンダーを作っていません。各プラットフォームは、追従するときにこの仕様から作ります。
  • Web:年の選択状態は、ボタンの aria-current で伝えます。ボタンには選択状態そのものがないためです。
  • Web:ブラウザーはスクロールの終わりも慣性も知らせないので、カレンダーは、スクロールが短いあいだ途切れたことか減速から、終わりを判断します。ブラウザーが数ピクセルずつ報告するホイールマウスをゆっくり回すと、1 段ごとに元の月へ戻ります。
  • Web:指でのドラッグは、まだ実機で確認していません。また、Firefox と WebKit で失敗するテストがあります。

利用者側で行うこと

  • label を設定し、カレンダーが使う言葉(previousMonthLabel、nextMonthLabel、yearViewLabel、todayLabel、startLabel、endLabel)を渡します。カレンダーに書くか、setWords() でプロダクト全体に一度だけ渡します。Tanrei は言葉を同梱しません。これらがなければ、ナビゲーションボタンは動作しますが名前を持たず、期間の両端には説明が付かず、それぞれ開発時に報告されます。ブラウザーのロケールデータから取るのは、今日を表す言葉だけです。プロダクトの言葉を参照してください。
  • type を date にしたテキストフィールドのように、日付を入力する手段を用意します。多くの人にとって、それがいちばん直接的な方法です。
  • 選べない日をまたぐ期間を受け付けられない場合は、change のあとでそう伝えます。