Skip to content
fandhe-frontendcore v0.4.3
GitHub

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"> に無効な属性のため反映しません)。

キーボード操作

キー対象効果
TabItemグループへフォーカスを移します(選択済み項目があればその項目、無ければ先頭項目)。ネイティブ radio group のロービングタブインデックスに従います。
SpaceItemフォーカス中の未選択項目を選択します。
ArrowDown / ArrowRightItem次の項目へ移動し選択します。
ArrowUp / ArrowLeftItem前の項目へ移動し選択します。

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 カスタムプロパティから描く最小例です。

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-text

API Reference

Arguments

NameTypeDefaultDescription
root(props).disabledboolfalsedata-disabled/aria-disabled="true"(true 時のみ)を反映する。
root(props).readonlyboolfalsearia-readonly="true"(true 時のみ)のみを反映する(data-readonly は root へ出力しない)。
root(props).invalidboolfalsedata-invalid を反映する。
root(props).requiredboolfalsedata-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).disabledboolfalsedata-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/invalidboolfalsedata-disabled/data-readonly/data-invalid を反映する(data-required は持たない)。
item(value)&str—選択肢の値。data-value として動的値のまま出力する。
item_hidden_input(props).requiredboolfalserequired 属性(true 時のみ)を反映する。
item_hidden_input(props).invalidboolfalsearia-invalid="true"(true 時のみ)を反映する。
item_hidden_input(name)Option<&str>—Some のとき name 属性を出力する(グループ内排他選択の同一 name グループ)。
item_hidden_input(value)&str—ネイティブ value 属性。

Data Attributes

PartAttributeObserved Values
indicatordata-disabled(値なし)
indicatordata-orientationvertical
indicatordata-statechecked
itemdata-disabled(値なし)
itemdata-invalid(値なし)
itemdata-readonly(値なし)
itemdata-statechecked, unchecked
itemdata-valuegrid, list
item-controldata-disabled(値なし)
item-controldata-invalid(値なし)
item-controldata-readonly(値なし)
item-controldata-statechecked, unchecked
item-hidden-inputdata-statechecked, unchecked
item-textdata-disabled(値なし)
item-textdata-invalid(値なし)
item-textdata-readonly(値なし)
item-textdata-statechecked, unchecked
rootdata-disabled(値なし)
rootdata-invalid(値なし)
rootdata-orientationvertical
rootdata-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 を自前で当てられます。

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

KeyDescription
Tabitem_hidden_input はネイティブ <input type="radio"> であり、同一 name グループ内ではブラウザ標準のロービングタブインデックスに従う(radio_group と同型)。
Spaceフォーカス中の未選択項目を選択する。
ArrowDown / ArrowRight次項目へ移動して選択する。
ArrowUp / ArrowLeft前項目へ移動して選択する。

WAI-ARIA

AttributeDescription
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-checkeditem_control へは明示付与しない(radio_group と同じ二重読み上げ防止の最小主義)。
aria-invalid="true" (item-hidden-input)SegmentGroupProps.invalid が真のときのみ出力する(イシュー #1618)。