テーマ切替
stimeo--theme
ライト、ダーク、端末に合わせるの 3 択を覚え、ページ全体へ反映します。
配色の選択を覚えて、次に来たときも同じ見た目にします。選べるのはライト、ダーク、端末の設定に合わせるの 3 つです。端末に合わせるを選んでいる間は、OS 側の設定が変わればすぐ追随します。結果の配色はページのルート要素に書き出すので、色そのものは利用側の CSS が決めます。この部品は色を 1 つも持ちません。3 択のときは選択肢のグループとして作るので、矢印キーで選べて読み上げにも正しく伝わります。ライトとダークだけを切り替えるなら、押した状態を持つ 1 つのボタンでも作れます。
実行中
プレビュー
このボックスだけローカルにテーマ適用されます — ページ自体は変わりません。
キーボード操作
| キー | 動作 |
|---|---|
| → / ↓ | 次のテーマ選択肢へ移動して選択する(radiogroup)。RTL では左右のみ入れ替わる。 |
| ← / ↑ | 前のテーマ選択肢へ移動して選択する。RTL では左右のみ入れ替わる。 |
| Home / End | 最初 / 最後の選択肢を選択する。 |
| Enter / Space | フォーカス中の選択肢を実行する(単一ボタンでは切替)。 |
<%# Markup for the theme / color-scheme toggle demo.
The controller persists the light/dark/system choice, follows the OS while in
system, and writes data-theme + color-scheme onto the target. To avoid theming the
whole Playground, this demo targets a local preview element instead of <html>, so
the preview box below reacts to the resolved theme while the page is unaffected. %>
<div class="theme-demo">
<div class="theme-demo__switcher" data-controller="stimeo--theme"
data-stimeo--theme-target-value="#theme-demo-preview"
data-stimeo--theme-storage-key-value="stimeo-theme-demo"
data-stimeo--theme-mode-value="system"
role="radiogroup" aria-label="<%= t("components.theme.demo.aria_label") %>">
<button type="button" class="theme-demo__option"
data-stimeo--theme-target="option" role="radio"
data-action="click->stimeo--theme#set" data-value="light">
<%= t("components.theme.demo.light") %>
</button>
<button type="button" class="theme-demo__option"
data-stimeo--theme-target="option" role="radio"
data-action="click->stimeo--theme#set" data-value="dark">
<%= t("components.theme.demo.dark") %>
</button>
<button type="button" class="theme-demo__option"
data-stimeo--theme-target="option" role="radio"
data-action="click->stimeo--theme#set" data-value="system">
<%= t("components.theme.demo.system") %>
</button>
</div>
<div id="theme-demo-preview" class="theme-demo__preview">
<h3 class="theme-demo__preview-title"><%= t("components.theme.demo.preview_title") %></h3>
<p><%= t("components.theme.demo.preview_text") %></p>
</div>
</div>
/*
* Presentation-only styles for the theme toggle demo.
* The library writes data-theme (resolved light/dark) and color-scheme onto the
* preview element and keeps aria-checked in sync; this CSS owns the palette keyed
* off data-theme — exactly the consumer's job. No colors ship in the library.
*/
.theme-demo {
display: flex;
flex-direction: column;
gap: 1rem;
max-width: 32rem;
}
.theme-demo__switcher {
display: inline-flex;
gap: 0.25rem;
padding: 0.25rem;
border: 1px solid var(--border);
border-radius: 0.5rem;
width: fit-content;
}
.theme-demo__option {
padding: 0.4rem 0.8rem;
border: 0;
border-radius: 0.375rem;
background: transparent;
cursor: pointer;
font-size: 0.9rem;
}
/* The library marks the selected option with aria-checked="true". */
.theme-demo__option[aria-checked="true"] {
/* Same pairing as clipboard's button: white over the brand primary is 2.99:1,
so the filled state uses the accessible fill token. */
background: var(--accent-fill);
color: var(--white);
}
/* The preview reacts to the resolved theme the library applied. */
.theme-demo__preview {
padding: 1.25rem;
border-radius: 0.75rem;
border: 1px solid var(--border-default);
background: var(--surface-card);
color: var(--color-text);
}
.theme-demo__preview[data-theme="dark"] {
border-color: var(--border-strong);
background: var(--slate-900);
color: var(--slate-200);
}
.theme-demo__preview-title {
margin: 0 0 0.5rem;
font-size: 1.05rem;
}
// Consumer-side JS for the theme toggle demo (demo-only).
// The controller persists the choice and applies data-theme on its own; this only
// logs the change event to show the contract (mode = chosen, resolved = effective).
document.addEventListener("stimeo--theme:change", (event) => {
console.log(`[theme] mode=${event.detail.mode} resolved=${event.detail.resolved}`);
});
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--theme"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
option
|
モードを data-value(light / dark / system)で宣言する radiogroup の選択肢(role="radio")。 |
data-stimeo--theme-target="option" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
mode
|
現在の選択: light / dark / system(既定 system)。3 値の外の宣言は system として読む。 |
data-stimeo--theme-mode-value |
storageKey
|
永続化に使う localStorage キー(既定 stimeo-theme)。 |
data-stimeo--theme-storage-key-value |
target
|
状態フックの付与先要素のセレクタ(既定 html)。解析できないセレクタは既定へ落ちる。 |
data-stimeo--theme-target-value |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
set
|
押された選択肢が data-value で宣言するモードを選ぶ。3 値の外を宣言した選択肢は選択されない。 |
stimeo--theme#set |
toggle
|
2 値の単一ボタン契約で light↔dark を切り替える。 |
stimeo--theme#toggle |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
change
|
テーマが実際に変化したときだけ発火。detail に mode と resolved を伴う。 |
stimeo--theme:change |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
data-theme |
target 要素(既定 html) | 実効テーマ: "light" か "dark"。 |
color-scheme |
target 要素 | 実効テーマに合わせたネイティブ UI の配色ヒント。 |
aria-checked |
radiogroup の選択肢 | 選択中は "true"、他は "false"。 |
aria-pressed |
単一トグルボタン(2 値) | 実効テーマが dark のとき "true"。 |