スムーズなライトモードとダークモードの移行: トークン、CSS、モーション
システム設定を尊重し、ロード時のフラッシュを回避し、ネイティブ コントロールのスタイルを設定し、アクセシビリティを損なうことなくアニメーション化する、明暗テーマのスイッチを構築する方法。
核心
セマンティック トークンに基づいてテーマを構築し、デフォルトで prefers-color-scheme のシステム設定に従い、ユーザーがそれをオーバーライドできるようにし、フォーム コントロールとスクロールバーが一致するように color-scheme プロパティを設定します。フラッシュを避けるために、ページがレンダリングされる前に保存したテーマを適用します。クイック クロスフェードやビュー トランジションなど、スイッチ アニメーションを短くし、モーションの削減がオンになっている場合はスキップします。
システム設定から始める
好みのカラースキームのメディア機能は、ユーザーがオペレーティング システムに明るいテーマと暗いテーマのどちらを要求したかを示します。これをデフォルトとして使用し、それをオーバーライドして記憶されるアプリ内スイッチを追加します。
オーバーライドではなくトークンを使用したテーマ
色をセマンティック トークン (表面、テキスト、境界線、アクセント) として定義し、各テーマに独自の値 (通常はルート要素の CSS カスタム プロパティとして) を与えます。コンポーネントはテーマ間で変更されることはありません。トークン値のみが影響します。これは、マルチブランド デザイン システムに使用されるものと同じ 3 層トークン アプローチです。
ネイティブコントロールを忘れないでください
MDN は、color-scheme プロパティが要素がサポートするスキームをブラウザーに伝え、フォーム コントロール、スクロールバー、その他の組み込み UI のデフォルトの色を変更すると説明しています。これがないと、暗いページに明るい白い入力とスクロールバーが表示される可能性があります。
ロード時のフラッシュを避ける
ページのレンダリング後に保存したテーマが JavaScript によって適用されると、間違ったテーマが点滅して表示されます。スタイルシートが描画される前に、ドキュメントのヘッダーまたはサーバーでレンダリングされた属性に小さなインライン スクリプトを使用して、できるだけ早く適用します。
スイッチを慎重にアニメーション化する
約 150 ~ 250 ミリ秒の短いクロスフェードにより、変更が意図的に行われているように感じられます。すべての要素のすべての色のプロパティを遷移することは避けてください。これは、大きなページでは時間がかかる可能性があります。多くの場合、単一のビュー遷移スナップショットの方がスムーズです。ラベルまたはツールチップを使用してトグル自体を明確にし、モーションの減少がオンになっている場合はアニメーションを削除します。
実装コード例
/* Tokens per theme */
:root { color-scheme: light dark; --surface: #ffffff; --text: #121212; }
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) { --surface: #121212; --text: #f2f2f2; }
}
:root[data-theme="dark"] { color-scheme: dark; --surface: #121212; --text: #f2f2f2; }
:root[data-theme="light"] { color-scheme: light; }
body { background: var(--surface); color: var(--text); }
// Switch with a View Transition where supported, instantly otherwise
function setTheme(theme) {
const apply = () => { document.documentElement.dataset.theme = theme; localStorage.setItem('theme', theme); };
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!document.startViewTransition || reduce) return apply();
document.startViewTransition(apply);
}覚えておきたい原則
- デフォルトはprefers-color-schemeです。人々がそれを上書きして覚えられるようにする
- コンポーネントごとのオーバーライドではなく、セマンティック トークンを含むテーマ
- ネイティブ コントロールと一致するようにカラー スキームを設定する
- 最初のペイントの前に保存したテーマを適用します
- スイッチのアニメーションを短くし、モーションを減らしてください
よくある質問
出典
- prefers-color-scheme — MDN ウェブ ドキュメント
- color-scheme — MDN ウェブ ドキュメント
- View Transition API — MDN ウェブ ドキュメント