Toggle Group
複数の押下可能なボタンをグループ化する unstyled 部品です。「高々 1 項目が押下される」状態機械(常時 deselectable)を提供し、各項目は独立した Toggle の集合として振る舞います。各 item は root と同じ orientation が Some のときに data-orientation を持ち、ToggleGroupProps::roving_focus(既定 false)を有効にすると SSR 側から roving tabindex を opt-in で出力できます(イシュー #1630)。
Themes 版(fandhe-frontend-pre-styled-ui)はこの構造へ既定 CSS を追加するだけの薄いラッパーであり、CSS は持ちません。スタイル済みの表示例は Toggle Group を参照してください。
関連 API: fandhe-frontend-headless-ui API
Demo
枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。
Features
- Root(
<div role="group">)/Item(<button type="button">)の 2 anatomy パーツと、「高々 1 項目が押下される」状態機械ToggleGroup(single モード、常時 deselectable)を提供する。 - 各
itemは単体のcrate::toggle::rootと同じaria-pressed/data-state(on/off)語彙を持つ(ToggleGroup の各項目は独立した Toggle の集合という ark-ui の位置付け)。 root/itemは共通のToggleGroupProps(disabled/orientation/roving_focus)を受け取る。orientationがSomeのときのみ両パーツへdata-orientationを付与する(イシュー #1630 で item にも追加。role="group"は WAI-ARIA 上aria-orientationを許可されていないためaria-orientationは付与しない)。props.disabledはrootのdata-disabledに加えて全itemへも伝播する(item個別のdisabledとの論理和。ark-ui/Radix の Root disabled が全 item を無効化する契約に合わせた、イシュー #1630 の是正)。labelled_byがSomeのときのみaria-labelledbyを付与する(名前なしの関連付けを作らない方針)。ToggleGroupProps::roving_focus(既定false)をtrueにすると、itemのfocused引数に応じてtabindex="0"/"-1"(roving tabindex)を SSR 側から出力できる(イシュー #1630 opt-in。既定falseは no-JS でも全 item が Tab で到達可能なままにするため)。矢印キーによる実際のフォーカス移動 DOM 配線はfandhe-frontend-wasm-fullの keynav が担い、本コンポーネント自体のスコープ外(下記 Keyboard 節参照)。- 呼び出し側
attrsからのtabindex/data-value/aria-pressed/data-orientation等の偽装は fail-closed に除去する(イシュー #1630)。
Anatomy
root
itemAPI Reference
Arguments
| Name | Type | Default | Description |
|---|---|---|---|
props | &ToggleGroupProps | &ToggleGroupProps::default() | disabled/orientation/roving_focus の 3 フィールドを持つグループ状態束。root/item の双方が参照する(イシュー #1630)。 |
labelled_by | Option<&str> | None | Some のときのみ aria-labelledby を付与する。 |
pressed (item) | bool | false | 各 item の押下状態。data-state(on/off)・aria-pressed・data-pressed へ反映する。 |
focused (item) | bool | false | props.roving_focus が true のときのみ tabindex="0"/"-1" へ反映する(false のときは無視され tabindex を出力しない)。 |
disabled (item) | bool | false | 各 item 個別の無効化状態。props.disabled との論理和が実効値になる。 |
value (item) | &str | (必須) | data-value としてそのまま出力する項目値(既定エスケープ経由)。 |
Data Attributes
| Part | Attribute | Observed Values |
|---|---|---|
item | data-disabled | (値なし) |
item | data-orientation | vertical |
item | data-pressed | (値なし) |
item | data-state | off, on |
item | data-value | bold, bottom, center, italic, left, right, top |
root | data-disabled | (値なし) |
root | data-orientation | vertical |
Examples
縦方向 + ラベル関連付け
props.orientation に Some(Orientation::Vertical)、labelled_by に Some("...") を渡すと data-orientation="vertical"(root/item 双方)と aria-labelledby が付与されます。
roving tabindex(opt-in)
ToggleGroupProps::roving_focus = true と item の focused 引数で、SSR 側から roving tabindex の初期状態を出力する例です。
roving_focus = true を渡すと、focused (第 3 引数) に応じて item が tabindex="0"/"-1" を出力します(既定は false)。矢印キーでの実際のフォーカス移動は fandhe-frontend-wasm-full の keynav が担うため、JS なしのこの静的例では center 以外はタブ順から外れたままです。
自前 CSS の最小例
headless-ui はスタイルレスであるため、root/item の data-* 属性セレクタで見た目を組み立てます。
利用者が data-scope / data-part / data-state / data-orientation / data-disabled 属性セレクタで自前 CSS を当てる最小例です。headless-ui 自体はスタイルを持ちません。
[data-scope="toggle-group"][data-part="item"][data-state="on"] {
background: #2563eb;
color: #fff;
}
[data-scope="toggle-group"][data-part="item"][data-disabled] {
opacity: 0.5;
}
[data-scope="toggle-group"][data-part="root"][data-orientation="vertical"] {
flex-direction: column;
}
Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| Tab | roving_focus = false(既定)のときは全 item がネイティブなタブ順で到達可能。roving_focus = true のときは focused な 1 item のみが Tab ストップになる(roving tabindex、矢印キー押下後は fandhe-frontend-wasm-full の keynav が押下位置へ収束させる)。 |
| Space / Enter | 各 item はネイティブ <button type="button"> であり、押下操作はブラウザ既定動作で成立する(MAPPING_TABLE の toggle-group/item → "toggle" 行が dispatch へ接続する)。 |
| ArrowLeft / ArrowRight | 水平方向(data-orientation が None/"horizontal")のフォーカス移動。fandhe-frontend-wasm-full の keynav::toggle_group_next_index(radio_next_index 共有)が実装し、常時循環・disabled item をスキップする。 |
| ArrowUp / ArrowDown | 垂直方向(data-orientation が "vertical")のフォーカス移動。data-orientation が欠落(None)のときは水平・垂直いずれの矢印キーも受理する(両軸受理、モジュール doc「意図的に合わせなかった点」参照)。 |
| Home / End | 先頭/末尾の非 disabled item へフォーカス移動する(orientation 非依存、keynav::toggle_group_next_index 実装)。 |
WAI-ARIA
| Attribute | Description |
|---|---|
| role | root へ "group" を固定付与する。 |
| aria-pressed | 各 item へ現在の押下状態を付与する。 |