1723928368
2024-08-15 13:10:34
解決策は 2 つあります。
- ネイティブCSS変数を使用できます
- または、2 セットの scss 変数を使用し、React Context でプロバイダーをセットアップします
まとめ
シンプルな解決策
CSS 変数を使用したくなるかもしれません。確かにそれは可能ですが、実際には実用的ではなく、次のような scss 関数で常に動作するとは限りません。 darken() または lighten()。したがって、プロジェクト中に scss 関数をまったく使用しない場合は、使用しても問題ありません。
ファイル内の構文は非常に単純です デフォルト.scss:
:root {
--var1: #000,
--var2: #fff
}
使用方法も非常に簡単です。
div {
background-color: var(--var1);
color: var(--var2);
}
ネイティブ CSS 変数をさらに使用するには: CSS 変数を使用してすべてをより動的にする方法は?
次に、js 関数を使用してボタンを使用してそれらを変更するだけです。 document.documentElement.style.setProperty('--var1', '#262626');
次に 2 番目の解決策を見てみましょう。
を使用することもできます theme provider。のようなコンポーネント auth provider これは、プロジェクト全体に適用されるタグの階層に可能な限り配置されます。
変数セットから始めましょう。同じ変数を 2 回作成しますが、テーマごとに異なる値を使用します。これを行うには、変数を作成します themes これには、それぞれのテーマの他のすべてが含まれます。
$themes: (
"dark": (
var1: red,
bg: #1f1f1f,
text: #fff,
),
"light": (
var1: blue,
bg: #fff,
text: #000,
),
);
それらを使用するには、次のようにします。
.theme--light {
div {
background-color: map-get(map-get($themes, "light"), "bg");
}
}
.theme--dark {
div {
background-color: map-get(map-get($themes, "dark"), "bg");
}
}
それはあまり実用的ではなく、あまり動的でもありません。毎回これを行う必要は避けたいと考えています。私たちは適応しなければなりません。
を作成します mixin これによりテーマが確実に適用されます dark または light おかげで私たちが何を選んだかに応じて context、その後すぐに使用します。 (テーマから) 変数の値を動的に注入することができます。 light または dark)CSSで。
@mixin themed() {
@each $theme, $map in $themes {
.theme-#{$theme} & {
$theme-map: () !global;
@each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), "#{$key}");
$theme-map: map-merge(
$theme-map,
(
$key: $value,
)
) !global;
}
// execute le contenu original du mixin
@content;
// supprime la variable globale
$theme-map: null !global;
}
}
}
@each $theme, $map in $themes {
ここで、宣言された変数は、 $theme 入っているものの名前を表します $themes、つまり ライト ら 暗い。 $map は各テーマに関連付けられたカード、ペアを表します。 key-value このテーマに特化したもの。 (例: var1:red)。
.theme--#{$theme} & {
上記では、次の形式で CSS セレクターを定義しています。 .theme-nomDuTheme.
$theme-map: () !global;
ここでグローバル変数を宣言します $theme-map 空のマップとして初期化されます。
@each $key, $submap in $map {
ここでもう一度ループを作成します。今度はマップ ( light または dark)。 $key は変数の名前であり、 $submap は値です (使用しません。例: key: submap)。
$value: map-get(map-get($themes, $theme), "#{$key}");
$theme-map: map-merge(
$theme-map,
(
$key: $value,
)
) !global;
各キーについて、その値が取得され、 $theme-mapを使用して マップマージ。
@content;
$theme-map: null !global;
私は、 @contentの内容を実行できます。 ミックスイン、次にグローバル変数を削除します $theme-map。
@function t($key: "var0") {
@return map-get($theme-map, $key);
}
この機能 t、値を使用して どこ0 デフォルトでは、キーを取得できます ($key) それぞれのカードに ($theme-map)。たとえば鍵 文章 私たちの地図では 暗い。ご了承ください $theme-map はグローバル変数です。
したがって、完全な scss コードは次のようになります。
@mixin themed() {
@each $theme, $map in $themes {
.theme-#{$theme} & {
$theme-map: () !global;
@each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), "#{$key}");
$theme-map: map-merge(
$theme-map,
(
$key: $value,
)
) !global;
}
@content;
$theme-map: null !global;
}
}
}
@function t($key: "base0") {
@return map-get($theme-map, $key);
}
少し特殊ですが、CSS モジュール (file.module.scss) を使用する場合は、関数を再作成する必要があります。 themed()ですが、次の式を追加します :global()、前のものも保持しません。
// ↓↓↓ ici est la seule différence
:global(.theme-#{$theme}) & {
[…]
}
.theme-#{$theme} & {
[…]
}
この場合、使用します themed() モジュラー scss ファイル内、および themedGlobal() 基本的な scss ファイル。
- 使用する場合
themed()タイプのファイル内 ファイル.scss、エラーが発生します。 - 使用する場合
themedGlobal()タイプのファイル内 ファイル.モジュール.scss、対応するスタイルは適用されません。
適用するには、単に使用します @include 使用する scss キー:
div {
@include themed {
background-color: t("bg");
}
}
プロバイダーをセットアップする
プロバイダーを使用すると、ある意味、js を scss に接続できるようになります。これは最も面白くない部分です。モードを選択できるようにするには、React Context を使用する必要があります。
あなたのファイル
_app入っている必要があります.tsxそして入っていない.js、これから作成するファイルと同じように =>ThemeContext.tsx。理想的には、ファイルに保存します コンテキスト、フォルダーと同じ場所にあります ページ。
ファイルは次のようになります。
import React, { createContext, useState, ReactNode, useEffect } from "react"
type ContextType = {
toggleDark: () => void
isDark: boolean
};
const defaultContext: ContextType = {
toggleDark: () => {
console.warn("Should have been overriden")
},
isDark: false,
};
const ThemeContext = createContext(defaultContext)
export const ThemeContextProvider = ({ children }: { children: ReactNode }) => {
const [isDark, setIsDark] = useState(true)
const context: ContextType = {
toggleDark: () => {
setIsDark(!isDark)
},
isDark,
};
return (
{children}
);
};
export default ThemeContext
もっとよく理解してみましょう
import React, { createContext, useState, ReactNode } from "react"
まず、useState をインポートします。これはおそらくご存知かと思いますが、次のとおりです。
- コンテキストを作成する: コンテキスト オブジェクトを作成できます。これにより、コンポーネント ツリーの各レベルを介して値を明示的に渡すことなく、コンポーネント間で値を共有できるようになります。
- リアクトノード: これは typescript に固有であり、React によってレンダリングされるすべてのもの (要素、ボリアン文字列など) を表します。これは小道具を入力するのに非常に便利です
childrenコンポーネントが有効な React の子を受け入れるようにします。
コンテキストタイプの定義
type ContextType = {
toggleDark: () => void
isDark: boolean
};
コンテキストに保存されるデータの標準を指定します。それは私たちに次のように宣言するのに役立ちます。
- 機能切り替えダーク: 暗いテーマと明るいテーマを切り替えます。
- 暗い: テーマがダークモードかどうかを知ることができるブール値
デフォルトのコンテキスト
const defaultContext: ContextType = {
toggleDark: () => {
console.warn("Should have been overridden")
},
isDark: false,
};
これにより、コンテキストにデフォルト値を与えることができます。
- トグルダーク: 関数を置き換える必要があることを示す警告
- 暗い: デフォルト、偽
コンテキストの作成
const ThemeContext = createContext(defaultContext);
というコンテキストを作成します。 ThemeContext、デフォルト値は defaultContext。
コンテキストプロバイダー、プロバイダー
export const ThemeContextProvider = ({ children }: { children: ReactNode }) => {
const [isDark, setIsDark] = useState(false)
const context: ContextType = {
toggleDark: () => {
setIsDark(!isDark)
},
isDark,
};
return (
{children}
);
};
の値を管理するコンポーネントです。 isDark と useState()。
context オブジェクトもあります。 context。
- トグルダーク: 完成バージョン。値を切り替えます。
isDarkの 間違い もっている 真実 そしてその逆もまた然り - 暗い: テーマが暗いか明るいかを表します。
最後に、戻ります ThemeContext.Provider、値を使用して context、過去の子供たちを包み込むことによって ThemeContextProvider。
- テーマコンテキスト: 以前に作成した
- プロバイダー: コンテキスト値を子に渡すことができる React 要素
- 属性値: コンテキストのすべての子コンポーネントからアクセスできる値を受け取ります (ここでは、次の値が含まれています)
isDarkらtoggleDark)
export default ThemeContext;
次に、デフォルトでコンテキストをエクスポートし、他の場所で使用できるようにします。
プロバイダーの実装
私たちのファイルでは _app.tsx、私たちの方法でアプリを包含するつもりです ProviderNextAuth 認証の場合と同様です。有名なクラスを含む div を追加することを忘れないでください theme--nomDuTheme。そのおかげで、他の要素に暗いスタイルまたは明るいスタイルが与えられます。
すべてが機能することを確認するには、インポートしてください AppProps、後に追加します Component ら pageProps、 このようにして:
function MyApp({ Component, pageProps }: AppProps) {
魔法のボタン
次に、関数を実行するボタンを作成します。 toggleDark。
この関数を同じファイルに忘れずにインポートしてください
import React, { useContext } from 'react'
import ThemeContext from '../../../context/ThemeContext.tsx'
const votreComposantOuPage = () => {
const { isDark, toggleDark } = useContext(ThemeContext)
return (
)
}
export default votreComposantOuPage
本文にテーマを適用する
実際には、 の値に基づいてクラスを適用することはできません。 コンテクスト とても高いところにある要素に 体。実際、後者はファイル内にあります document.jsしたがって、プロバイダーの上にあります。 _app.tsx。これを修正するには、最初に行ったことを使用できます。つまり、プロバイダーを使用せず、body 要素のみを使用する非実用的なバージョンです。次のようになります。
body {
.theme--dark {
background-color: map-get(map-get($themes, "dark"), "bg");
}
.theme--light {
background-color: map-get(map-get($themes, "light"), "bg");
}
}
さらに、ファイル内の本文にテーマ –XXXX クラスを手動で適用する必要があります。 _app.tsx, このような:
useEffect(() => {
document.querySelector('body').className = isDark ? 'theme-dark' : 'theme-light'
}, [isDark]);
これを必ず置いてください useEffect 変数宣言の下 isDark
このようにして、問題は解決されます。
ユーザーの好みを検出する
ユーザーの好みのテーマを検出します matchMediaに条件を追加することで、 Provider。したがって、 使用効果 で ThemeContextProvider、ちょうど間 使用状態は暗い そして context:
useEffect(() => {
if (
window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
) {
setIsDark(true)
}
}, []);
ここでは、ユーザーのお気に入りのテーマを適用します。 window.matchMedia('(prefers-color-scheme: dark)').matches。
ローカルストレージあり
localStorage も使用できることに注意してください。 使用効果、ユーザーがテーマを手動で変更したかどうかを確認し、 ContextType単純な変数ではなく、そのためリロード時に値が保持されません。この場合、次のように使用します。 getItem() で 使用効果、など setItem() 文脈の中で。 localStorage について詳しくは、Next をご覧ください。
スタイル回復の例(まだ途中) ThemeContextProvider:
useEffect(() => {
const isItDark = JSON.parse(localStorage.getItem('ThemeIsDark'));
if (isItDark !== undefined && isItDark !== null) {
setIsDark(isItDark)
}
else if (
window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
) {
setIsDark(true)
}
}, [])
ここでは、最初に localStorage 内のテーマの値を取得し、それが存在するかどうかを確認し、この場合はそれを適用します。そうでない場合は、ユーザーの優先テーマを適用します。
そしてコンテキストでは次のようになります。
const context: ContextType = {
toggleDark: () => {
localStorage.setItem('ThemeIsDark', String(!isDark))
setIsDark(!isDark)
},
isDark,
};
ここでは、の逆数の値を送信します。 isDark localStorage 内で、この同じ変数も変更します。
バグを解決するには
An import path cannot with a '.tsx' extension
このエラーは、TS のバージョンが原因で発生する可能性があります。プロパティを追加してみてください "allowImportingTsExtensions": true tsconfig.json ファイルに追加します。
Unknown compiler option 'allowImportingTsExtensions'
このエラーは、TS のバージョンが原因で発生します。この式は以前は存在しませんでした。次のようにして最新バージョンをインストールする必要があります。 npm i typescript@next
Des imcompatibilités
code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/typescript
npm ERR! typescript@"^5.6.0-dev.20240805" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peerOptional typescript@">=3.3.1" from [email protected]
npm ERR! node_modules/eslint-config-next
npm ERR! dev eslint-config-next@"12.1.0" from the root project
npm ERR!
npm ERR! Conflicting peer dependency: [email protected]
npm ERR! node_modules/typescript
npm ERR! node_modules/typescript
npm ERR! peerOptional typescript@">=3.3.1" from [email protected]
npm ERR! node_modules/eslint-config-next
npm ERR! dev eslint-config-next@"12.1.0" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
私のように、プロジェクトの途中で追加した場合、別のライブラリをインストールするときにこのようなエラーが発生する危険があります。これを解決するには、バージョンを更新するだけです。 eslint-config-next。このために、次のコマンドを使用します npm install eslint-config-next@latest -f。こちらも試してみてください npm i @types/react -f TSの更新も忘れずに。
問題が解決しない場合は、バージョンをダウングレードしてみてください。そうでない場合は、コメントで質問するか、私までご連絡ください。
結論としては
確かに、セットアップは簡単ではありませんが、一度完了すると、結果は非常に優れています。一方、既に要素に scss 変数を適用している場合は、実際に次のミックスインを使用してそれらをすべて置き換える必要があります。 @include。それは面倒ですし、私自身も経験しました。ただし、scss 変数を直接変更することはできないため、これが唯一の解決策の 1 つです。請願書に署名しましょう。したがって、次のプロジェクトでは、開始する前にこれを直接実装することを忘れないでください。理想的には、プロバイダーと基本要素のみを含む空のプロジェクトを作成し、毎回再実行する必要がないように、それらを新しいプロジェクトにコピーすることです。たとえば、github リポジトリを使用します。
#NextJs #または #React #とコンテキストを使用して #SCSS #でテーマを設定する