@k8ordo/color-scheme

スタイル

このパッケージが画面に出すのは、<html> の dark クラス 1 つだけです。そのクラスの下で何がどう変わるかは CSS が決めます。@k8ordo/ui と使うとき、Tailwind CSS だけのとき、素の CSS のときの当て方と、CSS の color-scheme プロパティ、OS のコントラストの設定との関係を説明します。

<html> の dark クラス

クラスの名前も付け先も変えられず、結果がダークのときだけ <html> に dark が付きます。ライトのときは何も付かず、light というクラスはありません。最初の描画の前にインラインスクリプトが付け、そのあとは Provider の effect が付け外しします。どのスタイルシートも、このクラスを読めば訪問者の選択に従います。

@k8ordo/ui と使う

@k8ordo/ui のセマンティックトークンは、:root にライトの値を、.dark にダークの値を持ちます。styles.css でも tailwind.css でも同じなので、コンポーネントも bg-bg-base のようなユーティリティも、追加の設定なしでクラスに従います。@k8ordo/ui が自分でクラスを付けることはなく、付けるのはこのパッケージです。

css
/* src/styles/globals.css */
@import '@k8ordo/ui/tailwind.css';

tailwind.css は dark: と light: のバリアントもクラスを読むように宣言しています。dark: は .dark の下で、light: はそれ以外で効くので、自前のマークアップでもそのまま使えます。

tsx
// src/components/logo.tsx
export function Logo() {
  return (
    <div className="bg-bg-base text-fg-base rounded-md p-4">
      <img alt="k8ordo" className="dark:invert" src="/logo.svg" />
    </div>
  );
}

CSS の color-scheme プロパティ

color-scheme プロパティは、スクロールバー、フォーム部品、<dialog> の既定の色のような、ブラウザ自身が描くものをライトとダークのどちらで描くかを決めます。このパッケージはこれを設定しません。@k8ordo/ui のベースレイヤーが、トークンと並べて宣言しています。

css
/* @k8ordo/ui's base layer */
:root {
  color-scheme: light;
}

.dark {
  color-scheme: dark;
}

color-scheme: light dark と書かないのは、そう書くとブラウザが OS の prefers-color-scheme で選び、訪問者がこのサイトで選んだものとずれるからです。OS がダークでも訪問者がライトを選んでいれば、スクロールバーもライトのままでなければなりません。プロパティもトークンと同じクラスに従わせます。

高コントラストは OS の設定に従う

コントラストは、このパッケージが持つ軸ではありません。prefers-contrast: more と forced-colors: active は訪問者が OS で決める設定で、アプリが保存したり切り替えたりするものではないので、useColorScheme() にも保存行にも出てきません。

@k8ordo/ui のスタイルシートは、この 2 つに自分で従います。prefers-contrast: more ではテキストと境界のトークンを地の色から遠ざけ、それを :root と .dark の両方に用意しているので、訪問者がライトを選んでいてもダークを選んでいても効きます。forced-colors: active ではブラウザが色を訪問者のパレットで塗り直し、部品は境界、フォーカスリング、選択状態をシステムカラーで保ちます。

2 つの軸は組み合わさります。ダークを選んだ訪問者が OS で高コントラストを求めていれば、ダークの高コントラストの値で描かれます。

@k8ordo/ui のテーマ: 高コントラストと強制カラー、自前の UI での書き方

Tailwind CSS だけで使う

Tailwind CSS 4 の dark: バリアントは、既定では prefers-color-scheme を読みます。そのままでは OS の設定に従い、訪問者の選択を無視します。クラスを読むように宣言し直します。@k8ordo/ui の tailwind.css がしている宣言と同じものです。

css
/* src/styles/globals.css */
@import 'tailwindcss';

@custom-variant dark (&:where(.dark, .dark *));

素の CSS で使う

色をクラスに結びつけます。@k8ordo/ui を使わないなら color-scheme プロパティを宣言するものは無いので、フォーム部品やスクロールバーのようなブラウザ自身の描画も合わせたいなら、色と一緒に宣言します。

css
/* src/styles/globals.css */
:root {
  color-scheme: light;
  --page-bg: #ffffff;
  --page-fg: #1f1f1f;
}

:root.dark {
  color-scheme: dark;
  --page-bg: #1f1f1f;
  --page-fg: #f5f5f5;
}

body {
  background: var(--page-bg);
  color: var(--page-fg);
}