تجريد للموضوعات في تطبيق React الخاص بك.
appDir
useTheme
ربط الموضوعتحقق من المثال المباشر لتجربته بنفسك.
$ npm install next-themes
# or
$ yarn add next-themes
ستحتاج إلى App
مخصص لاستخدام السمات التالية. أبسط _app
يبدو كالتالي:
// pages/_app.js
function MyApp ( { Component , pageProps } ) {
return < Component { ... pageProps } / >
}
export default MyApp
تتطلب إضافة دعم الوضع المظلم سطرين من التعليمات البرمجية:
// pages/_app.js
import { ThemeProvider } from 'next-themes'
function MyApp ( { Component , pageProps } ) {
return (
< ThemeProvider >
< Component { ... pageProps } / >
< / ThemeProvider >
)
}
export default MyApp
ستحتاج إلى تحديث app/layout.jsx
الخاص بك لاستخدام السمات التالية. أبسط layout
يبدو كالتالي:
// app/layout.jsx
export default function Layout ( { children } ) {
return (
< html >
< head / >
< body > { children } < / body >
< / html >
)
}
تتطلب إضافة دعم الوضع المظلم سطرين من التعليمات البرمجية:
// app/layout.jsx
import { ThemeProvider } from 'next-themes'
export default function Layout ( { children } ) {
return (
< html suppressHydrationWarning >
< head / >
< body >
< ThemeProvider > { children } < / ThemeProvider >
< / body >
< / html >
)
}
لاحظ أن ThemeProvider
هو مكون عميل، وليس مكون خادم.
ملحوظة! إذا لم تقم بإضافة PreventHydrationWarning إلى
<html>
الخاص بك، فسوف تحصل على تحذيرات لأنnext-themes
تقوم بتحديث هذا العنصر. تنطبق هذه الخاصية على مستوى واحد فقط من العمق، لذلك لن تمنع تحذيرات الترطيب على العناصر الأخرى.
هذا كل شيء، تطبيق Next.js الخاص بك يدعم الوضع المظلم بشكل كامل، بما في ذلك تفضيلات النظام مع prefers-color-scheme
. تتم أيضًا مزامنة السمة على الفور بين علامات التبويب. افتراضيًا، تقوم next-themes بتعديل سمة data-theme
في عنصر html
، والتي يمكنك استخدامها بسهولة لتصميم تطبيقك:
: root {
/* Your default theme */
--background : white;
--foreground : black;
}
[ data-theme = 'dark' ] {
--background : black;
--foreground : white;
}
ملحوظة! إذا قمت بتعيين سمة موفر السمات الخاص بك إلى فئة لـ Tailwind، فسوف تقوم السمات التالية بتعديل سمة
class
في عنصرhtml
. انظر مع الريح الخلفية.
ستحتاج واجهة المستخدم الخاصة بك إلى معرفة السمة الحالية والقدرة على تغييرها. يوفر خطاف useTheme
معلومات حول الموضوع:
import { useTheme } from 'next-themes'
const ThemeChanger = ( ) => {
const { theme , setTheme } = useTheme ( )
return (
< div >
The current theme is: { theme }
< button onClick = { ( ) => setTheme ( 'light' ) } > Light Mode < / button >
< button onClick = { ( ) => setTheme ( 'dark' ) } > Dark Mode < / button >
< / div >
)
}
تحذير! الكود أعلاه غير آمن للترطيب وسوف يلقي تحذيرًا بعدم تطابق الترطيب عند التقديم باستخدام SSG أو SSR. وذلك لأننا لا نستطيع معرفة
theme
الموجودة على الخادم، لذلك ستكون دائمًاundefined
حتى يتم تثبيتها على العميل.يجب عليك تأخير عرض أي سمة لتبديل واجهة المستخدم حتى يتم تثبيتها على العميل. انظر المثال.
دعونا نحفر في التفاصيل.
يتم تمرير كل تكوينات السمة الخاصة بك إلى ThemeProvider.
storageKey = 'theme'
: المفتاح المستخدم لتخزين إعدادات السمة في localStoragedefaultTheme = 'system'
: اسم السمة الافتراضي (بالنسبة للإصدار 0.0.12 والإصدارات الأقل، كان الاسم الافتراضي light
). إذا كانت قيمة enableSystem
خاطئة، فإن المظهر الافتراضي هو light
forcedTheme
: اسم السمة القسري للصفحة الحالية (لا يعدل إعدادات السمة المحفوظة)enableSystem = true
: ما إذا كان سيتم التبديل بين dark
light
بناءً على prefers-color-scheme
enableColorScheme = true
: ما إذا كان سيتم الإشارة إلى المتصفحات بنظام الألوان المستخدم (داكن أو فاتح) لواجهة المستخدم المضمنة مثل المدخلات والأزرارdisableTransitionOnChange = false
: قم بتعطيل جميع انتقالات CSS بشكل اختياري عند تبديل السمات (مثال)themes = ['light', 'dark']
: قائمة بأسماء السماتattribute = 'data-theme'
: تم تعديل سمة HTML بناءً على السمة النشطةclass
و data-*
(يعني أي سمة بيانات، data-mode
، أو data-color
، وما إلى ذلك) (مثال)value
: تعيين اختياري لاسم السمة لقيمة السمةobject
حيث المفتاح هو اسم السمة والقيمة هي قيمة السمة (مثال)nonce
: تم تمرير nonce اختياري إلى علامة script
التي تم إدخالها، وتستخدم للسماح بإدراج البرنامج النصي للموضوعات التالية في CSP الخاص بكscriptProps
: الدعائم الاختيارية لتمريرها إلى علامة script
المحقونة (مثال)لا يأخذ useTheme أي معلمات، ولكنه يُرجع:
theme
: اسم الموضوع النشطsetTheme(name)
: وظيفة لتحديث السمة. واجهة برمجة التطبيقات (API) مطابقة للدالة المحددة التي يتم إرجاعها بواسطة الخطاف useState
. قم بتمرير قيمة السمة الجديدة أو استخدم رد اتصال لتعيين السمة الجديدة بناءً على السمة الحالية.forcedTheme
: سمة الصفحة القسرية أو الزائفة. إذا تم تعيين forcedTheme
، فيجب عليك تعطيل أي واجهة مستخدم لتبديل السماتresolvedTheme
: إذا كانت enableSystem
صحيحة والموضوع النشط هو "system"، فسيُرجع هذا ما إذا كان تفضيل النظام قد تم تحديده إلى "غامق" أو "فاتح". خلاف ذلك، مطابقة theme
systemTheme
: إذا كان enableSystem
صحيحًا، فإنه يمثل تفضيل سمة النظام ("غامق" أو "فاتح")، بغض النظر عن السمة النشطةthemes
: قائمة السمات التي تم تمريرها إلى ThemeProvider
(مع إلحاق "النظام"، إذا كان enableSystem
صحيحًا)ليس سيئا للغاية، أليس كذلك؟ دعونا نرى كيفية استخدام هذه الخصائص مع الأمثلة:
يعرض المثال المباشر السمات التالية قيد التنفيذ، مع سمات النظام والصفحات الداكنة والفاتحة ذات السمات القسرية.
بالنسبة للإصدارات الأعلى من v0.0.12، يتم تعيين defaultTheme
تلقائيًا على "النظام"، لذا لاستخدام تفضيلات النظام، يمكنك ببساطة استخدام:
< ThemeProvider >
إذا كنت لا تريد سمة النظام، فقم بتعطيلها عبر enableSystem
:
< ThemeProvider enableSystem = { false } >
إذا كان تطبيق Next.js يستخدم فئة لتصميم الصفحة بناءً على السمة، فقم بتغيير خاصية السمة إلى class
:
< ThemeProvider attribute = "class" >
الآن، سيؤدي تعيين السمة إلى "غامق" إلى تعيين class="dark"
على عنصر html
.
لنفترض أن صفحة التسويق الجديدة الرائعة الخاصة بك هي الوضع المظلم فقط. يجب أن تستخدم الصفحة دائمًا المظهر الداكن، ويجب ألا يكون لتغيير المظهر أي تأثير. لفرض سمة على صفحات Next.js، ما عليك سوى تعيين متغير في مكون الصفحة:
// pages/awesome-page.js
const Page = ( ) => { ... }
Page . theme = 'dark'
export default Page
في _app
الخاص بك، اقرأ المتغير وقم بتمريره إلى ThemeProvider:
function MyApp ( { Component , pageProps } ) {
return (
< ThemeProvider forcedTheme = { Component . theme || null } >
< Component { ... pageProps } / >
< / ThemeProvider >
)
}
منتهي! صفحتك دائمًا ذات مظهر داكن (بغض النظر عن تفضيل المستخدم)، وأصبح استدعاء setTheme
من useTheme
الآن محظورًا. ومع ذلك، يجب عليك التأكد من تعطيل أي واجهة مستخدم لديك والتي عادةً ما تغير المظهر:
const { forcedTheme } = useTheme ( )
// Theme is forced, we shouldn't allow user to change the theme
const disabled = ! ! forcedTheme
لقد كتبت عن هذه التقنية هنا. يمكننا تعطيل جميع انتقالات CSS بالقوة قبل تغيير السمة، وإعادة تمكينها فورًا بعد ذلك. وهذا يضمن أن واجهة المستخدم الخاصة بك ذات فترات الانتقال المختلفة لن تشعر بعدم الاتساق عند تغيير السمة.
لتمكين هذا السلوك، قم بتمرير الخاصية disableTransitionOnChange
:
< ThemeProvider disableTransitionOnChange >
يتم استخدام اسم السمة النشطة كقيمة localStorage وقيمة سمة DOM. إذا كان اسم السمة "وردي"، فسيحتوي localStorage على theme=pink
وسيكون DOM هو data-theme="pink"
. لا يمكنك تعديل قيمة localStorage، لكن يمكنك تعديل قيمة DOM.
إذا أردنا أن يقوم DOM بعرض data-theme="my-pink-theme"
بدلاً من ذلك عندما يكون المظهر "ورديًا"، فقم بتمرير خاصية value
:
< ThemeProvider value = { { pink : 'my-pink-theme' } } >
منتهي! لكي نكون أكثر وضوحًا، يؤثر هذا على DOM فقط. إليك كيف ستبدو جميع القيم:
const { theme } = useTheme ( )
// => "pink"
localStorage . getItem ( 'theme' )
// => "pink"
document . documentElement . getAttribute ( 'data-theme' )
// => "my-pink-theme"
Rocket Loader عبارة عن تحسين لـ Cloudflare يعمل على تأجيل تحميل البرامج النصية المضمنة والخارجية لتحديد أولويات محتوى موقع الويب. نظرًا لأن السمات التالية تعتمد على إدخال البرنامج النصي لتجنب وميض الشاشة عند تحميل الصفحة، فإن Rocket Loader يعطل هذه الوظيفة. يمكن تجاهل البرامج النصية الفردية عن طريق إضافة سمة data-cfasync="false"
إلى علامة البرنامج النصي:
< ThemeProvider scriptProps = { { 'data-cfasync' : 'false' } } >
تم تصميم السمات التالية لدعم أي عدد من المواضيع! ما عليك سوى تمرير قائمة بالمواضيع:
< ThemeProvider themes = { [ 'pink' , 'red' , 'blue' ] } >
ملحوظة! عندما تقوم بتمرير
themes
، يتم تجاوز مجموعة السمات الافتراضية ("فاتحة" و"داكنة"). تأكد من تضمين تلك العناصر إذا كنت لا تزال تريد السمات الفاتحة والداكنة:
< ThemeProvider themes = { [ 'pink' , 'red' , 'blue' , 'light' , 'dark' ] } >
للحصول على مثال حول كيفية استخدام هذا، راجع المثال متعدد المواضيع
لا تعتمد هذه المكتبة على تصميم السمة الخاصة بك باستخدام متغيرات CSS. يمكنك ترميز القيم في CSS الخاص بك، وسيعمل كل شيء كما هو متوقع (بدون أي وميض):
html ,
body {
color : # 000 ;
background : # fff ;
}
[ data-theme = 'dark' ] ,
[ data-theme = 'dark' ] body {
color : # fff ;
background : # 000 ;
}
Next Themes مستقل تمامًا عن CSS، وسيعمل مع أي مكتبة. على سبيل المثال، باستخدام Styled Components، تحتاج فقط إلى createGlobalStyle
في تطبيقك المخصص:
// pages/_app.js
import { createGlobalStyle } from 'styled-components'
import { ThemeProvider } from 'next-themes'
// Your themeing variables
const GlobalStyle = createGlobalStyle `
:root {
--fg: #000;
--bg: #fff;
}
[data-theme="dark"] {
--fg: #fff;
--bg: #000;
}
`
function MyApp ( { Component , pageProps } ) {
return (
< >
< GlobalStyle / >
< ThemeProvider >
< Component { ... pageProps } / >
< / ThemeProvider >
< / >
)
}
نظرًا لأننا لا نستطيع معرفة theme
الموجودة على الخادم، فإن العديد من القيم التي يتم إرجاعها من useTheme
ستكون undefined
حتى يتم تثبيتها على العميل. وهذا يعني أنه إذا حاولت عرض واجهة المستخدم بناءً على السمة الحالية قبل التثبيت على العميل، فسوف ترى خطأ عدم تطابق الماء.
نموذج التعليمات البرمجية التالي غير آمن :
import { useTheme } from 'next-themes'
// Do NOT use this! It will throw a hydration mismatch error.
const ThemeSwitch = ( ) => {
const { theme , setTheme } = useTheme ( )
return (
< select value = { theme } onChange = { e => setTheme ( e . target . value ) } >
< option value = "system" > System < / option >
< option value = "dark" > Dark < / option >
< option value = "light" > Light < / option >
< / select >
)
}
export default ThemeSwitch
لإصلاح ذلك، تأكد من عرض واجهة المستخدم التي تستخدم السمة الحالية فقط عند تثبيت الصفحة على العميل:
import { useState , useEffect } from 'react'
import { useTheme } from 'next-themes'
const ThemeSwitch = ( ) => {
const [ mounted , setMounted ] = useState ( false )
const { theme , setTheme } = useTheme ( )
// useEffect only runs on the client, so now we can safely show the UI
useEffect ( ( ) => {
setMounted ( true )
} , [ ] )
if ( ! mounted ) {
return null
}
return (
< select value = { theme } onChange = { e => setTheme ( e . target . value ) } >
< option value = "system" > System < / option >
< option value = "dark" > Dark < / option >
< option value = "light" > Light < / option >
< / select >
)
}
export default ThemeSwitch
وبدلاً من ذلك، يمكنك تحميل المكون على جانب العميل بشكل بطيء. يستخدم المثال التالي next/dynamic
ولكن يمكنك أيضًا استخدام React.lazy
:
import dynamic from 'next/dynamic'
const ThemeSwitch = dynamic ( ( ) => import ( './ThemeSwitch' ) , { ssr : false } )
const ThemePage = ( ) => {
return (
< div >
< ThemeSwitch / >
< / div >
)
}
export default ThemePage
لتجنب تغيير التخطيط، فكر في عرض هيكل عظمي/عنصر نائب حتى يتم تثبيته على جانب العميل.
عرض صور مختلفة بناءً على الموضوع الحالي يعاني أيضًا من مشكلة عدم تطابق الماء. باستخدام next/image
يمكنك استخدام صورة فارغة حتى يتم حل الموضوع:
import Image from 'next/image'
import { useTheme } from 'next-themes'
function ThemedImage ( ) {
const { resolvedTheme } = useTheme ( )
let src
switch ( resolvedTheme ) {
case 'light' :
src = '/light.png'
break
case 'dark' :
src = '/dark.png'
break
default :
src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
break
}
return < Image src = { src } width = { 400 } height = { 400 } / >
}
export default ThemedImage
يمكنك أيضًا استخدام CSS لإخفاء المحتوى أو إظهاره بناءً على السمة الحالية. لتجنب عدم تطابق الماء، ستحتاج إلى عرض كلا الإصدارين من واجهة المستخدم، مع إخفاء CSS للإصدار غير المستخدم. على سبيل المثال:
function ThemedImage ( ) {
return (
< >
{ /* When the theme is dark, hide this div */ }
< div data-hide-on-theme = "dark" >
< Image src = "light.png" width = { 400 } height = { 400 } / >
< / div >
{ /* When the theme is light, hide this div */ }
< div data-hide-on-theme = "light" >
< Image src = "dark.png" width = { 400 } height = { 400 } / >
< / div >
< / >
)
}
export default ThemedImage
[ data-theme = 'dark' ] [ data-hide-on-theme = 'dark' ] ,
[ data-theme = 'light' ] [ data-hide-on-theme = 'light' ] {
display : none;
}
قم بزيارة المثال المباشر • عرض رمز مصدر المثال
ملحوظة! يدعم Tailwind الوضع المظلم فقط في الإصدار>2.
في tailwind.config.js
، اضبط خاصية الوضع المظلم على selector
:
// tailwind.config.js
module . exports = {
darkMode : 'selector'
}
ملاحظة: إذا كنت تستخدم إصدارًا أقدم من tailwindcss < 3.4.1، فاستخدم 'class'
بدلاً من 'selector'
قم بتعيين السمة لموفر السمة الخاص بك إلى الفئة:
// pages/_app.tsx
< ThemeProvider attribute = "class" >
إذا كنت تستخدم خاصية القيمة لتحديد قيم سمات مختلفة، فتأكد من أن المظهر الداكن يستخدم القيمة "المظلمة" بشكل صريح، كما هو مطلوب بواسطة Tailwind.
هذا كل شيء! يمكنك الآن استخدام فئات محددة للوضع المظلم:
< h1 className = "text-black dark:text-white" >
يتيح لك Tailwind أيضًا استخدام محدد مخصص للوضع المظلم اعتبارًا من الإصدار 3.4.1.
في هذه الحالة، سيبدو tailwind.config.js
الخاص بك كما يلي:
// tailwind.config.js
module . exports = {
// data-mode is used as an example, next-themes supports using any data attribute
darkMode : [ 'selector' , '[data-mode="dark"]' ]
…
}
الآن قم بتعيين السمة الخاصة بـ ThemeProvider الخاص بك على data-mode
:
// pages/_app.tsx
< ThemeProvider attribute = "data-mode" >
باستخدام هذا الإعداد، يمكنك الآن استخدام فئات الوضع المظلم في Tailwind، كما في المثال السابق:
يقوم ThemeProvider تلقائيًا بإدخال برنامج نصي في next/head
لتحديث عنصر html
بالسمات الصحيحة قبل تحميل بقية صفحتك. وهذا يعني أن الصفحة لن تومض تحت أي ظرف من الظروف، بما في ذلك السمات القسرية وموضوع النظام والموضوعات المتعددة والتصفح المتخفي. لا يلزم وجود noflash.js
.
لماذا لا تزال صفحتي تومض؟
في وضع تطوير Next.js، قد تستمر الصفحة في الوميض. عندما تقوم بإنشاء تطبيقك في وضع الإنتاج، لن يكون هناك أي وميض.
لماذا أحصل على خطأ عدم تطابق الخادم/العميل؟
عند استخدام useTheme
، ستشاهد خطأ عدم تطابق الماء عند عرض واجهة المستخدم التي تعتمد على السمة الحالية. وذلك لأن العديد من القيم التي يتم إرجاعها بواسطة useTheme
غير محددة على الخادم، نظرًا لأننا لا نستطيع قراءة localStorage
حتى يتم تثبيتها على العميل. انظر المثال لمعرفة كيفية إصلاح هذا الخطأ.
هل أحتاج إلى استخدام متغيرات CSS مع هذه المكتبة؟
لا. انظر المثال.
هل يمكنني تعيين فئة أو سمة البيانات على النص أو عنصر آخر؟
لا. إذا كان لديك سبب وجيه لدعم هذه الميزة، يرجى فتح مشكلة.
هل يمكنني استخدام هذه الحزمة مع Gatsby أو CRA؟
نعم، بدءاً من الإصدار 0.3.0.
هل تم تصغير النص المُدخل؟
نعم.
لماذا يتم resolvedTheme
ضروري؟
عند دعم تفضيل سمة النظام، فإنك تريد التأكد من أن ذلك ينعكس في واجهة المستخدم الخاصة بك. وهذا يعني أن الأزرار أو التحديدات أو القوائم المنسدلة أو أي شيء تستخدمه للإشارة إلى السمة الحالية يجب أن تقول "النظام" عندما يكون تفضيل سمة النظام نشطًا.
إذا لم نفرق بين theme
و resolvedTheme
، فستظهر واجهة المستخدم "Dark" أو "Light"، في حين أنه من المفترض أن يكون "System".
يعد resolvedTheme
مفيدًا بعد ذلك لتعديل السلوك أو الأنماط في وقت التشغيل:
const { resolvedTheme } = useTheme ( )
< div style = { { color : resolvedTheme === 'dark' ? 'white' : 'black' } } >
إذا لم نقم resolvedTheme
واستخدمنا theme
فقط، فستفقد معلومات حول حالة واجهة المستخدم الخاصة بك (ستعرف فقط أن السمة هي "النظام"، وليس ما تم الحل له).