ラジオグループ
stimeo--radio-group
カスタムラジオの単一選択。aria-checked、ロービング tabindex、矢印での選択移動を担う。
stimeo--radio-group コントローラは、カスタム(非ネイティブ)のラジオ(カード型 UI など)向けに APG の Radio Group パターンを実装します。選択は aria-checked、1 タブストップはロービング tabindex で表します。 APG に従い「フォーカス=選択」で、矢印キーは移動と選択を同時に行い(ループ)、Home/End で先頭/末尾へ移動します。選択値は任意の hidden field に反映され、change ごとに stimeo--radio-group:change を発火します。
選択中のプラン: ベーシック
キーボード操作
| キー | 動作 |
|---|---|
| ↓ / → | 次のラジオへ移動して選択(ループ)。 |
| ↑ / ← | 前のラジオへ移動して選択(ループ)。 |
| Home / End | 先頭 / 末尾のラジオへ移動して選択。 |
| Space | フォーカス中のラジオを選択。 |
<%# Markup for the radio-group (APG Radio Group) demo.
A custom card-style radio. Single selection is shown via aria-checked and the single
tab stop via roving tabindex. Arrows move the selection (focus = selection). The
selected value is reflected into a hidden field; the look is the consumer's CSS. %>
<div class="radio-group-demo" role="radiogroup" aria-labelledby="rg-label"
data-controller="stimeo--radio-group">
<span class="radio-group-demo__legend" id="rg-label">
<%= t("components.radio_group.demo.legend") %>
</span>
<div class="radio-group-demo__options">
<div class="radio-group-demo__option" role="radio" aria-checked="true" tabindex="0"
data-value="basic" data-stimeo--radio-group-target="radio"
data-action="click->stimeo--radio-group#select
keydown->stimeo--radio-group#onKeydown">
<span class="radio-group-demo__name"><%= t("components.radio_group.demo.basic") %></span>
<span class="radio-group-demo__desc"><%= t("components.radio_group.demo.basic_desc") %></span>
</div>
<div class="radio-group-demo__option" role="radio" aria-checked="false" tabindex="-1"
data-value="pro" data-stimeo--radio-group-target="radio"
data-action="click->stimeo--radio-group#select
keydown->stimeo--radio-group#onKeydown">
<span class="radio-group-demo__name"><%= t("components.radio_group.demo.pro") %></span>
<span class="radio-group-demo__desc"><%= t("components.radio_group.demo.pro_desc") %></span>
</div>
<div class="radio-group-demo__option" role="radio" aria-checked="false" tabindex="-1"
data-value="max" data-stimeo--radio-group-target="radio"
data-action="click->stimeo--radio-group#select
keydown->stimeo--radio-group#onKeydown">
<span class="radio-group-demo__name"><%= t("components.radio_group.demo.max") %></span>
<span class="radio-group-demo__desc"><%= t("components.radio_group.demo.max_desc") %></span>
</div>
</div>
<input type="hidden" name="plan" value="basic" data-stimeo--radio-group-target="field" />
<p class="radio-group-demo__status" aria-live="polite">
<%= t("components.radio_group.demo.selected") %>
<span data-radio-value><%= t("components.radio_group.demo.basic") %></span>
</p>
</div>
/*
* Presentation-only styles for the radio-group demo.
* Selection is expressed via [role="radio"][aria-checked="true"] and focus via :focus-visible.
*/
.radio-group-demo {
display: flex;
flex-direction: column;
gap: 0.5rem;
max-width: 26rem;
}
.radio-group-demo__legend {
font-weight: 600;
}
.radio-group-demo__options {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.radio-group-demo__option {
display: flex;
flex-direction: column;
gap: 0.15rem;
padding: 0.625rem 0.875rem;
background: var(--bg);
border: 1px solid var(--border-interactive);
border-radius: 0.5rem;
cursor: pointer;
transition: border-color 0.15s ease, background 0.15s ease;
}
.radio-group-demo__option[aria-checked="true"] {
border-color: var(--accent);
background: var(--color-primary-soft);
}
.radio-group-demo__option:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.radio-group-demo__name {
font-weight: 600;
}
.radio-group-demo__desc {
font-size: 0.85rem;
color: var(--color-text-muted);
}
.radio-group-demo__status {
margin: 0;
font-size: 0.85rem;
color: var(--color-text-muted);
}
// Demo script that subscribes to the radio-group change event and shows the selected plan name.
// The name is read from the localized text inside the card.
document.addEventListener('stimeo--radio-group:change', function (event) {
const out = document.querySelector('[data-radio-value]');
const name = event.detail.radio.querySelector('.radio-group-demo__name');
if (out && name) out.textContent = name.textContent.trim();
});
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。 共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--radio-group"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
radio
必須
|
カスタムラジオ項目(role=radio)。選択は aria-checked で表す。 |
data-stimeo--radio-group-target="radio" |
field
|
選択ラジオの data-value を反映する任意の隠しフィールド。 |
data-stimeo--radio-group-target="field" |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
onKeydown
|
矢印・Home/End・Space のナビ。フォーカス追従で選択(ラップする)。 | stimeo--radio-group#onKeydown |
select
|
クリックされたラジオを選択する。 | stimeo--radio-group#select |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
change
|
選択が変わると発火。detail に値と radio を載せる。 | stimeo--radio-group:change |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
aria-checked |
ラジオ | 選択中のラジオのみ "true"。 |
tabindex |
ラジオ | 選択中(または先頭)が 0、他は -1(ロービング)。 |