Timer
カウントダウン/カウントアップを表示する unstyled 部品です。時計 API を一切使わず、時間の前進は tick(デルタミリ秒)の明示的な注入のみで進む決定的な状態機械です。
Themes 版(fandhe-frontend-pre-styled-ui)はこの構造へ既定 CSS を追加するだけの薄いラッパーであり、CSS は持ちません。スタイル済みの表示例は Timer を参照してください。
実際の計時駆動(setInterval 相当)はクライアントランタイム側の責務であり、本部品は経過値の表示 anatomy と現在フェーズの表示状態のみを提供します。
キーボード操作
| キー | 対象 | 効果 |
|---|---|---|
| Tab | ActionTrigger | ネイティブ <button type="button"> へのフォーカス移動です。hidden な ActionTrigger(下記「参考サイトとの差分」参照)はフォーカス対象から外れます。 |
| Enter / Space | ActionTrigger | ネイティブ <button> の起動です。参考サイト(ark-ui / Zag.js)に専用の Keyboard Interactions 表はなく、本実装もブラウザ標準動作に委ねます。 |
参考サイトとの差分
一次ソース(zag.js packages/machines/timer/src/timer.connect.ts。chakra-ui v3 / Radix Primitives / Radix Themes に Timer は存在しない)と突合し、以下を是正しました。
areaの ARIA:role="timer"とaria-atomic="true"を無条件付与するよう是正しました(旧版は付与していませんでした)。aria-label(既定書式"{days} days {hh}:{mm}:{ss}")はTimer::area利便メソッド経由で注入されます(area自由関数自体はaria-labelを持ちません)。separatorの ARIA:aria-hidden="true"を無条件付与するよう是正しました(装飾用の区切り文字を支援技術に読み上げさせないため)。action_triggerのhidden導出: 第 2 引数に現在のTimerPhaseを受け取り、zag.js と同じ真偽式(running/pausedの 2 述語)でhidden属性の要否を導出するよう破壊的変更しました。TimerControl::Restart(5 値目): zag.js(validActions集合・restart: () => false)はstart/pause/resume/reset/restartの 5 値を持ちますが、是正前の本実装はrestartを欠いていました。zag.js に合わせて追加し、常に可視(hiddenを持たない)としました。
一方、以下は意図的に参考サイトへ合わせていません。
Completed状態: zag.js は完了到達時にidleへ戻りますが、本実装はdata-state="completed"で完了を表現し続ける拡張を維持します。可視性はrunning/pausedのいずれでもないためidleと同じです(Start/Restart のみ表示)。Startの任意フェーズ受理: zag.js のSTARTは idle 限定ですが、本実装は任意フェーズから running へ遷移できます。hiddenにより running/paused では UI 上到達不能なため実害はありません。itemのstyle="--value: N": zag.js は CSS カウンタ用のstyle属性を付与しますが、装飾・レイアウト計測の関心を headless 層へ持ち込まない方針(docs/policy/intentional-non-adoption.md§3.25)により非採用です。必要な場合は呼び出し側 CSS か Themes 層で対応してください。rootのdata-*:data-state/data-countdown/data-start-ms/data-target-ms/data-interval/data-elapsedは zag.js のrootプロパティ(idのみ)に対する本実装の superset です。クライアント配線層(wasm-full)と Themes 側 CSS がこれらへ依存します。id/dir/translations/asChild: 非対応です(translations.areaLabel相当の差し替えは呼び出し側attrsのaria-labelで代替できます)。
関連 API: fandhe-frontend-headless-ui API
自前 CSS の最小例
Themes 版を使わず本部品を直接使う場合、action-trigger に [hidden] { display: none; } を必ず併記してください(display を宣言する規則が先に一致すると、ブラウザ既定の [hidden] { display: none } を上書きしてしまいます)。
[data-scope="timer"][data-part="area"] {
display: flex;
align-items: center;
gap: 0.5rem;
}
[data-scope="timer"][data-part="item-value"] {
font-variant-numeric: tabular-nums;
font-size: 1.5rem;
}
[data-scope="timer"][data-part="root"][data-state="completed"] [data-part="item-value"] {
color: #2563eb;
}
[data-scope="timer"][data-part="action-trigger"] {
display: inline-flex;
padding: 0.5rem 0.875rem;
border: 1px solid #888;
border-radius: 6px;
}
/* [hidden] を上書きしないよう、display 宣言を持つ規則には必ず併記する。 */
[data-scope="timer"][data-part="action-trigger"][hidden] {
display: none;
}Demo
枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。
Features
- Root/Area/Item/ItemValue/ItemLabel/Separator/Control/ActionTrigger の 8 anatomy パーツと、idle/running/paused/completed の 4 値状態機械
Timerを提供する。 - 時計 API(
std::time・Instant・js_sys::Date)を一切使用しない。時間の前進はTimerAction::Tick(デルタミリ秒)の明示的注入のみで行われ、同一 tick 列を dispatch すれば常に同一の状態列に到達する決定的設計。 countdown/start_ms/target_ms/interval_ms/elapsed_msをdata-*としてそのまま出力し、クライアント配線層がこれらを読み取って tick 駆動・完了判定の表示反映を行う契約。- 実 tick 駆動(
setInterval相当)・ロケール依存の表示形式は本コンポーネントの責務外であり、ミリ秒の加算とゼロ埋め 2 桁整形のみを提供する(docs/policy/intentional-non-adoption.md§3.25)。
Anatomy
root
area
item
item-value
item-label
separator
control
action-triggerAPI Reference
Arguments
| Name | Type | Default | Description |
|---|---|---|---|
countdown | bool | (必須) | カウントダウン(true)かカウントアップ(false)か。 |
start_ms / target_ms | u64 | (必須) | カウントダウンの開始値・カウントアップの目標値(target_ms = 0 は無期限)。 |
interval_ms | u64 | 1000 | tick の間隔(表示上のヒント。実 tick 駆動はクライアント配線層の責務)。 |
elapsed_ms | u64 | 0 | 経過ミリ秒。data-elapsed へ反映される。 |
phase | TimerPhase | (必須) | data-state へ反映する現在フェーズ(idle/running/paused/completed)。 |
Data Attributes
| Part | Attribute | Observed Values |
|---|---|---|
action-trigger | data-action | pause, reset |
item | data-type | minutes, seconds |
item-label | data-type | minutes, seconds |
item-value | data-type | minutes, seconds |
root | data-countdown | (値なし) |
root | data-elapsed | 15000 |
root | data-interval | 1000 |
root | data-start-ms | 0 |
root | data-state | running |
root | data-target-ms | 60000 |
Examples
完了状態(Completed)
TimerPhase::Completed を渡すと data-state="completed" となります。action_trigger の可視性は TimerControl::is_hidden_in が zag.js と同じ真偽式で導出し、Completed は running/paused のいずれでもないため Idle と同じ可視性(Start/Restart のみ表示、Pause/Resume/Reset は hidden)になります。
Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| Tab / Space / Enter | action_trigger はネイティブ <button type="button"> であり、フォーカス・押下操作はブラウザ既定動作で成立する。 |