Calendar
月表示と単一日付の選択を、WAI-ARIA の grid パターンに従った 11 個の anatomy パーツ(Root/Heading/PrevTrigger/NextTrigger/Table/…/DayTrigger)として提供する unstyled 部品です。「今日」は呼び出し側が明示的に渡す引数であり、内部で時刻取得 API を使わない決定的な設計です。矢印キー等でのキーボード操作は fandhe-frontend-wasm-full が配線します。
Themes 版(fandhe-frontend-pre-styled-ui)はこの構造へ既定 CSS を追加するだけの薄いラッパーであり、CSS は持ちません。スタイル済みの表示例は Calendar を参照してください。
日付の実在性検証以外の入力値検証・ロケール依存の日付整形・フォーム送信処理は本部品の責務外であり、利用側が担います。
関連 API: fandhe-frontend-headless-ui API
Demo
枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Features
- Root/Heading/PrevTrigger/NextTrigger/Table/TableHeader/TableRow/TableHeadCell/TableBody/TableCell/DayTrigger の 11 anatomy パーツを提供し、月表示・単一日付選択・min/max 範囲制約を持つ状態機械
Calendarを組み合わせる。 - 「今日」は
Calendar::newのtoday引数として呼び出し側が明示的に渡す。SystemTime/Instant/js_sys等の時刻取得 API を内部で一切使わない決定的な設計であり、同一入力から常に同一出力を返す。 tableにrole="grid"、table_rowにrole="row"、table_head_cellにrole="columnheader"、table_cellにrole="gridcell"+aria-selectedを付与し、WAI-ARIA APG の grid パターンに従う。day_triggerは選択日にdata-selected、今日にdata-today+aria-current="date"、表示月外の日付にdata-outside-month、min/max 範囲外の日付にdata-disabled+ ネイティブdisabled+aria-disabledを出力する。- 範囲選択(range mode)・複数月表示・年/月ビュー切替は本コンポーネントのスコープ外(単一日付の選択のみを扱う)。
- ark-ui/zag の
data-focus/data-view/data-weekend/data-unavailable/range 系属性は出力しない(DOM ローカル状態・ビュー概念なし・locale 依存・range mode スコープ外のため。#1625 参考サイト突合結果)。 - パート名
table-header(<thead>)/table-head-cell(<th>)は ark-ui(table-head/table-header)とはパート名の対応が入れ替わっている(本実装は要素の役割をそのまま表す命名)。 - キーボード操作(矢印キー等での日付移動)は
fandhe-frontend-wasm-fullの keynav 配線(#1074/#1161)が担う。本コンポーネント(SSR 単体)はネイティブ<button>の Tab / Enter / Space のみを提供する。
Anatomy
root
heading
prev-trigger
next-trigger
table
table-header
table-row
table-head-cell
table-body
table-cell
day-triggerAPI Reference
Arguments
| Name | Type | Default | Description |
|---|---|---|---|
view_year | i32 | (必須) | 表示する年(0000..=9999)。範囲外は DateError::OutOfRange。 |
view_month | u8 | (必須) | 表示する月(1..=12)。 |
today | PlainDate | (必須) | 「今日」として扱う日付。呼び出し側が明示的に渡す(内部で時刻取得 API を使わない決定的設計)。 |
selected | Option<PlainDate> | None | 現在選択中の日付。 |
min / max | Option<PlainDate> | None | 選択可能範囲の下限・上限。ともに Some で min > max なら DateError::InvalidDate。 |
week_start | Weekday | (必須) | 週の開始曜日。 |
Data Attributes
| Part | Attribute | Observed Values |
|---|---|---|
day-trigger | data-disabled | (値なし) |
day-trigger | data-outside-month | (値なし) |
day-trigger | data-selected | (値なし) |
day-trigger | data-today | (値なし) |
day-trigger | data-value | 2026-06-28, 2026-06-29, 2026-06-30, 2026-07-01, 2026-07-02, 2026-07-03, 2026-07-04, 2026-07-05, 2026-07-06, 2026-07-07, 2026-07-08, 2026-07-09, 2026-07-10, 2026-07-11, 2026-07-12, 2026-07-13, 2026-07-14, 2026-07-15, 2026-07-16, 2026-07-17, 2026-07-18, 2026-07-19, 2026-07-20, 2026-07-21, 2026-07-22, 2026-07-23, 2026-07-24, 2026-07-25, 2026-07-26, 2026-07-27, 2026-07-28, 2026-07-29, 2026-07-30, 2026-07-31, 2026-08-01 |
next-trigger | data-disabled | (値なし) |
prev-trigger | data-disabled | (値なし) |
Examples
範囲外の日付・前月移動の無効化
day_trigger の disabled と prev_trigger の disabled を true にすると、min/max 範囲外であることを示す data-disabled・ネイティブ disabled・aria-disabled が出力されます。
自前 CSS の最小例
headless-ui 自体はスタイルを持たないため、data-scope/data-part/data-selected/data-today/data-outside-month/data-disabled 属性セレクタで見た目を組み立てる最小例です。
利用者が data-scope / data-part / data-selected / data-today / data-outside-month / data-disabled 属性セレクタで自前 CSS を当てる最小例です。headless-ui 自体はスタイルを持ちません。
[data-scope="calendar"][data-part="table"] {
border-collapse: collapse;
}
[data-scope="calendar"][data-part="day-trigger"] {
width: 2rem;
height: 2rem;
border: none;
border-radius: 4px;
}
[data-scope="calendar"][data-part="day-trigger"][data-selected] {
background: #2563eb;
color: #fff;
}
[data-scope="calendar"][data-part="day-trigger"][data-today] {
font-weight: bold;
}
[data-scope="calendar"][data-part="day-trigger"][data-outside-month] {
color: #9ca3af;
}
[data-scope="calendar"][data-part="day-trigger"][data-disabled] {
opacity: 0.4;
}
[data-scope="calendar"][data-part="prev-trigger"]:focus-visible,
[data-scope="calendar"][data-part="next-trigger"]:focus-visible {
outline: 2px solid #2563eb;
outline-offset: 2px;
}
Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| Tab | prev_trigger/next_trigger/day_trigger はいずれもネイティブ <button type="button"> であり、ブラウザ既定のフォーカス順で到達する(無効化時は disabled によりフォーカス対象から除外される)。 |
| Space / Enter | フォーカス中の prev_trigger/next_trigger/day_trigger をブラウザ既定動作で押下する(day_trigger は fandhe-frontend-wasm-full の keynav 配線で選択・prev_trigger/next_trigger は月移動をディスパッチする)。 |
| ArrowLeft / ArrowRight | fandhe-frontend-wasm-full の keynav 配線(#1074/#1161)が担う。フォーカス中の日付を ±1 日移動する(data-disabled のセルはスキップ、行末で次行へ、非循環)。 |
| ArrowUp / ArrowDown | 同 keynav 配線が担う。フォーカス中の日付を ±7 日(1 週間)移動する。 |
| Home / End | 同 keynav 配線が担う。フォーカス中の行の先頭・末尾の非 disabled セルへ移動する(zag の実装に合わせた挙動。ark-ui サイトの「月初/月末」という文言とは異なる)。 |
| PageUp / PageDown | 同 keynav 配線が担う。prev_trigger/next_trigger への click 合成で月移動する(trigger が disabled のときは no-op)。 |
WAI-ARIA
| Attribute | Description |
|---|---|
| role | table に "grid"、table_row に "row"、table_head_cell に "columnheader"、table_cell に "gridcell" を固定付与する。 |
| aria-selected | table_cell へ選択状態を反映する(day_trigger の data-selected とセットで選択状態を二重に表現する、WAI-ARIA grid パターンの慣行)。 |
| aria-current | 今日の day_trigger へ "date"(AriaCurrent::Date)を付与する。 |
| aria-disabled | min/max 範囲外の day_trigger、範囲下限/上限に到達した prev_trigger/next_trigger へ "true" を付与する。 |
| aria-labelledby | table の labelledby が Some のとき、heading の id と対で関連付ける。 |
| aria-label | day_trigger は ISO 8601 表記(例: "2026-07-25")を固定付与する。prev_trigger/next_trigger は既定値を持たないため、アイコンのみを子に置く場合はアクセシブル名を呼び出し側が attrs で渡す(例: ("aria-label", "Previous month"))。 |