Segment Group
fandhe-frontend-headless-ui の segment_group mod が提供する segmented control(セグメントコントロール)の unstyled 部品です。Root / Indicator / Item / ItemText / ItemControl / ItemHiddenInput の 6 anatomy パーツを持ちますが、状態機械・dispatch・hydration は radio_group へ全委譲しており独自の状態機械は持ちません(WAI-ARIA 上 segmented control は radio パターンそのものであるため)。Indicator は SSR 決定的な位置表現(CSS カスタムプロパティ 2 種)のみを提供します。
/themes/ 側の SegmentGroup(Chakra ライクな variant/size を持つスタイル層)と異なり、CSS を一切持ちません。
SegmentGroupProps(disabled/readonly/invalid/required)は次のように各パーツへ反映されます。root は data-disabled/data-invalid/data-required を付与し、さらに disabled/readonly/required が true のときのみ対応する aria-disabled/aria-readonly/aria-required="true"(radiogroup ロールの Supported States)を付与します。indicator は data-disabled のみを付与します。item/item-control/item-text は data-disabled/data-readonly/data-invalid を付与します(data-required は item 系パーツに無い属性です)。item-control は意味論を持たない装飾パーツであることを明示するため aria-hidden="true" を常時付与します。item-hidden-input には invalid: true のときのみ aria-invalid="true" を、required: true のときのみネイティブ required 存在属性を付与します(readonly はネイティブ <input type="radio"> に無効な属性のため反映しません)。
キーボード操作
| キー | 対象 | 効果 |
|---|---|---|
| Tab | Item | グループへフォーカスを移します(選択済み項目があればその項目、無ければ先頭項目)。ネイティブ radio group のロービングタブインデックスに従います。 |
| Space | Item | フォーカス中の未選択項目を選択します。 |
| ArrowDown / ArrowRight | Item | 次の項目へ移動し選択します。 |
| ArrowUp / ArrowLeft | Item | 前の項目へ移動し選択します。 |
item-hidden-input が生成するネイティブ <input type="radio">(同一 name グループ)の標準操作のみで成立します。radio_group と異なり fandhe-frontend-wasm-full に segment-group の CSR 配線が一切無いため、Home/End 拡張・data-readonly の選択変更抑止といった keynav 由来の挙動はいずれも提供されません。
参考サイトとの差分
ark-ui 公式 Segment Group(API 節・Data Attributes 表)と突合し、上記の data-*/aria-* 語彙を是正しました。一方、以下は意図的に合わせていません。
- ark の API 節には
Label(data-orientation/data-disabled/data-invalid/data-required)が載りますが、Anatomy 図(コードスニペット)には存在しません。/themes/側のSLOTS固定リストと CSS への波及を避けるため今回は採用を見送っています(外部ラベルとの関連付けはrootのlabelled_byで成立させます)。 indicatorのdata-state/style(CSS カスタムプロパティ 2 種、SSR 決定的)は本フレームワーク固有として維持します(/themes/側がindicator[data-state="unchecked"]セレクタで依存するため。ark の CSR 実測による追従は持ち込みません)。data-orientationはroot/indicatorのみへ付与し、子パーツへ伝播しません(radio_groupと同判断)。data-readonlyはrootへ出力しません(ark の Root Data Attributes 表に無いためです)。ネイティブ<input type="radio">にreadonly属性は無効なためitem-hidden-inputへも反映しません。fandhe-frontend-wasm-fullに segment-group の CSR 配線が一切無いため、data-readonly/aria-readonlyは SSR 語彙にとどまり、CSR での選択変更抑止はradio_groupと異なり未提供です。- Radix Themes の Segmented Control は styled 部品のため視覚参考にとどめ、ARIA・キーボードの一次情報は ark-ui を採用しています。
スタイル済みの表示例は Segment Group を参照してください。
関連 API: fandhe-frontend-headless-ui API
自前 CSS の最小例
Themes 版を使わず本部品を直接使う場合、[data-scope="segment-group"][data-part="..."] セレクタでスタイルを当てます。以下は root の外枠、item の余白、checked/disabled の見た目、invalid 時の枠色、フォーカスリング、indicator を CSS カスタムプロパティから描く最小例です。
[data-scope="segment-group"][data-part="root"] {
display: inline-flex;
position: relative;
border: 1px solid #ccc;
border-radius: 6px;
}
[data-scope="segment-group"][data-part="root"][data-invalid] {
border-color: #d33;
}
[data-scope="segment-group"][data-part="item"] {
padding: 0.25rem 0.75rem;
cursor: pointer;
}
[data-scope="segment-group"][data-part="item"][data-state="checked"] {
color: #fff;
}
[data-scope="segment-group"][data-part="item"][data-disabled] {
opacity: 0.5;
}
[data-scope="segment-group"][data-part="item"]:focus-within {
outline: 2px solid #06c;
outline-offset: 2px;
}
[data-scope="segment-group"][data-part="indicator"] {
position: absolute;
inset-block: 0;
width: calc(100% / var(--fandhe-segment-group-count));
transform: translateX(calc(100% * var(--fandhe-segment-group-index)));
background: #06c;
border-radius: 6px;
transition: transform 0.15s ease;
}Demo
枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。
Features
- Root / Indicator / Item / ItemText / ItemControl / ItemHiddenInput の 6 anatomy パーツを提供する。状態機械・dispatch・hydration は
crate::radio_group::RadioGroupへ全委譲し、独自の状態機械を新設しない(WAI-ARIA 上 segmented control は radio パターンそのものであるため)。 SegmentGroupProps(disabled/readonly/invalid/required)が各パーツのdata-*/ARIA 属性を決定する(イシュー #1618、RadioGroupPropsと同じパート別反映契約)。rootへdata-disabled/data-invalid/data-required+aria-disabled/aria-readonly/aria-required(true 時のみ)、indicatorへdata-disabled、item/item_control/item_textへdata-disabled/data-readonly/data-invalid、item_hidden_inputへrequired/aria-invalid="true"を反映する。indicatorは SSR 決定的な位置表現を持つ:(index, item_count)から--fandhe-segment-group-index/--fandhe-segment-group-countの 2 CSS カスタムプロパティのみをstyle属性へ出力する(JS 計測は行わない)。item_hidden_inputが生成するネイティブ<input type="radio">がチェック状態・フォーム送信・グループ内排他選択を担う(radio_groupと同型)。fandhe-frontend-wasm-fullには segment-group の CSR 配線が一切無いため(radio_groupと異なる点)、data-readonlyは SSR 語彙にとどまり、選択変更の CSR 抑止は未提供。
Anatomy
root
indicator
item
item-hidden-input
item-control
item-textAPI Reference
Arguments
| Name | Type | Default | Description |
|---|---|---|---|
root(props).disabled | bool | false | data-disabled/aria-disabled="true"(true 時のみ)を反映する。 |
root(props).readonly | bool | false | aria-readonly="true"(true 時のみ)のみを反映する(data-readonly は root へ出力しない)。 |
root(props).invalid | bool | false | data-invalid を反映する。 |
root(props).required | bool | false | data-required/aria-required="true"(true 時のみ)を反映する。 |
root(orientation) | Option<Orientation> | — | Some のときのみ data-orientation/aria-orientation を付与する。 |
root(labelled_by) | Option<&str> | — | Some のときのみ aria-labelledby を付与する。 |
indicator(position) | Option<(usize, usize)> | — | Some((index, count)) のとき data-state="checked" + 位置 CSS 変数 2 種を出力する。None(未選択)は data-state="unchecked" のみ。 |
indicator(props).disabled | bool | false | data-disabled を反映する(ark-ui の Indicator Data Attributes 表準拠)。 |
indicator(orientation) | Option<Orientation> | — | Some のとき data-orientation を出力する(styled 層が translateX/translateY を切り替える判断材料)。 |
item/item_control/item_text(checked) | bool | — | data-state の checked/unchecked を決める。 |
item/item_control/item_text(props).disabled/readonly/invalid | bool | false | data-disabled/data-readonly/data-invalid を反映する(data-required は持たない)。 |
item(value) | &str | — | 選択肢の値。data-value として動的値のまま出力する。 |
item_hidden_input(props).required | bool | false | required 属性(true 時のみ)を反映する。 |
item_hidden_input(props).invalid | bool | false | aria-invalid="true"(true 時のみ)を反映する。 |
item_hidden_input(name) | Option<&str> | — | Some のとき name 属性を出力する(グループ内排他選択の同一 name グループ)。 |
item_hidden_input(value) | &str | — | ネイティブ value 属性。 |
Data Attributes
| Part | Attribute | Observed Values |
|---|---|---|
indicator | data-disabled | (値なし) |
indicator | data-orientation | vertical |
indicator | data-state | checked |
item | data-disabled | (値なし) |
item | data-invalid | (値なし) |
item | data-readonly | (値なし) |
item | data-state | checked, unchecked |
item | data-value | grid, list |
item-control | data-disabled | (値なし) |
item-control | data-invalid | (値なし) |
item-control | data-readonly | (値なし) |
item-control | data-state | checked, unchecked |
item-hidden-input | data-state | checked, unchecked |
item-text | data-disabled | (値なし) |
item-text | data-invalid | (値なし) |
item-text | data-readonly | (値なし) |
item-text | data-state | checked, unchecked |
root | data-disabled | (値なし) |
root | data-invalid | (値なし) |
root | data-orientation | vertical |
root | data-required | (値なし) |
Examples
Vertical orientation
orientation: Some(Orientation::Vertical) で縦並びにした例。indicator の data-orientation が styled 層の translateY 切替判断材料になる。
Custom CSS
自前 CSS を data-scope/data-part/data-* セレクタで当てる最小例(invalid: true の状態を装飾)。
headless-ui はスタイルレスです。data-scope/data-part/data-* をセレクタに使い、以下のような CSS を自前で当てられます。
[data-scope="segment-group"][data-part="root"] {
display: inline-flex;
position: relative;
border: 1px solid #ccc;
border-radius: 6px;
}
[data-scope="segment-group"][data-part="root"][data-invalid] {
border-color: #d33;
}
[data-scope="segment-group"][data-part="item"] {
padding: 0.25rem 0.75rem;
cursor: pointer;
}
[data-scope="segment-group"][data-part="item"][data-state="checked"] {
color: #fff;
}
[data-scope="segment-group"][data-part="item"][data-disabled] {
opacity: 0.5;
}
[data-scope="segment-group"][data-part="item"]:focus-within {
outline: 2px solid #06c;
outline-offset: 2px;
}
[data-scope="segment-group"][data-part="indicator"] {
position: absolute;
inset-block: 0;
width: calc(100% / var(--fandhe-segment-group-count));
transform: translateX(calc(100% * var(--fandhe-segment-group-index)));
background: #06c;
border-radius: 6px;
transition: transform 0.15s ease;
}Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| Tab | item_hidden_input はネイティブ <input type="radio"> であり、同一 name グループ内ではブラウザ標準のロービングタブインデックスに従う(radio_group と同型)。 |
| Space | フォーカス中の未選択項目を選択する。 |
| ArrowDown / ArrowRight | 次項目へ移動して選択する。 |
| ArrowUp / ArrowLeft | 前項目へ移動して選択する。 |
WAI-ARIA
| Attribute | Description |
|---|---|
| role="radiogroup" (root) | 固定付与。 |
| aria-disabled / aria-readonly / aria-required (root) | SegmentGroupProps が真のときのみ "true" を出力する(イシュー #1618)。 |
| aria-hidden="true" (indicator / item-control) | 装飾専用パーツとして固定付与する(item-control はイシュー #1618 で追加)。 |
| role="radio" / aria-checked | item_control へは明示付与しない(radio_group と同じ二重読み上げ防止の最小主義)。 |
| aria-invalid="true" (item-hidden-input) | SegmentGroupProps.invalid が真のときのみ出力する(イシュー #1618)。 |