うまく動かないとき
よくつまずく症状と、その原因、直し方をまとめています。
このページの内容
ダークを選んでいるのに、再読み込みすると一瞬ライトで表示される
原因
インラインスクリプトが、ページが描かれる前に走っていません。多いのは、CSPがスクリプトを止めている場合と、プロバイダを入れ子のレイアウトに置いているために、それより前の部分が先に描かれている場合です。
直し方
コンソールにCSPの違反が出ていれば、次の「CSPでスクリプトが止まる」を見てください。そうでなければ、プロバイダをルートレイアウトの<body>の中に置き、ページ全体を包むようにします。
CSPでスクリプトが止まる
原因
スクリプトを制限するポリシーが、インラインスクリプトを許可していません。ハッシュで許可しているなら、プロバイダと違うdefaultPreferenceでハッシュを計算しているか、古い版で計算した値を書き写していることがあります。
直し方
@k8ordo/serverなら、プロバイダのnonceにnonce()を渡します。ハッシュで許可するなら、プロバイダと同じdefaultPreferenceをcolorSchemeScriptHash()に渡し、ポリシーを書く場所で毎回計算します。'unsafe-inline'は、ページに紛れ込んだほかのインラインスクリプトまで許可してしまうので使いません。
<html>でハイドレーションの食い違いが報告される
原因
インラインスクリプトが付けたclass="dark"は、サーバーのHTMLにはありません。開発時のReactは<html>の属性を描画するpropsと比べ、このclassをA tree hydrated but some attributes of the server rendered HTML didn't matchという警告で報告します。
直し方
<html>にsuppressHydrationWarningを付けます。報告が止まるのは<html>自身の属性だけで、ページの中の食い違いはこれまでどおり報告されます。ハイドレーションは属性を書き戻さないので、クラスはそのまま残ります。
スクロールバーやフォーム部品の配色が、選んだ配色とずれる
原因
ブラウザが自分で描く部品の配色は、CSSのcolor-schemeプロパティで決まります。このパッケージはこのプロパティを設定しないので、@k8ordo/uiを使っていなければ宣言するものがありません。color-scheme: light darkと書いている場合は、ブラウザがOSの設定で選ぶので、訪問者の選択とずれます。
直し方
:rootにcolor-scheme: lightを、.darkにcolor-scheme: darkを宣言して、プロパティもクラスに従わせます。
Tailwind CSSのdark:が選んだ配色に従わない
原因
Tailwind CSS 4のdark:は、既定ではprefers-color-schemeを読みます。そのため、OSの設定には従っても、訪問者が選んだ配色には従いません。
直し方
スタイルシートで@custom-variant dark (&:where(.dark, .dark *));を宣言し、dark:がクラスを読むようにします。@k8ordo/uiのtailwind.cssを読み込んでいれば、すでに宣言されています。
アイコンや文言が、読み込んだ直後だけ反対の配色のものになる
原因
サーバーは保存された選択を読めないので、既定値で描きます。schemeから選んだマークアップは、ハイドレーションが終わるまでその推測を表示します。
直し方
両方を描いてdark:で片方を隠すか、use(browser())を<Suspense>の中で呼んでブラウザでだけ描きます。詳しくは「切り替えのボタンを作る」を見てください。
useColorScheme needs <ColorSchemeProvider> above itというエラーが出る
原因
useColorScheme()を呼んだコンポーネントの上に、プロバイダがありません。プロバイダを入れ子のレイアウトに置いているか、テストでwrapperを渡していないことがよくあります。
直し方
プロバイダをルートレイアウトの<body>の中に置き、ページ全体を包みます。テストでは、プロバイダをrenderHookのwrapperに渡します。
OSの設定を変えても配色が変わらない
原因
OSの設定に従うのは、訪問者が何も選んでいない間だけです。一度でもライトかダークを選ぶと、その選択が優先されます。プロバイダのdefaultPreferenceが'system'でない場合も、OSの設定は使われません。
直し方
setPreference('system')を呼ぶと、保存していた選択が消えて既定値に戻ります。訪問者が自分で戻れるように、「システム」を含む3択を用意してください。
localStorageを書き換えても表示が変わらない
原因
タブ同士はstorageイベントで選択を伝え合いますが、このイベントは書き込んだタブ自身には届きません。そのため、同じタブでlocalStorage.setItemを直接呼んでも、プロバイダは再読み込みするまで気づきません。
直し方
選択を変えるときはsetPreferenceを使います。同じページを開いているほかのタブにも、そのまま伝わります。
テストでEncountered a script tagというエラーが出る
原因
テストのようにブラウザの中だけで描くと、Reactはプロバイダが描くインラインの<script>を実行しません。開発ビルドのReactは、そのことをエラーとしてコンソールに出します。
直し方
テストが失敗したわけではないので、直す必要はありません。テストで見る<html>のクラスは、プロバイダのeffectが付けたものです。