7 Dakika

React Context API ve Tailwind CSS ile Dark Mode Nasıl Yapılır?

Dark mode, artık neredeyse tüm servislerde mevcut. İlk başta gerçekten nasıl yapacağımı bilmiyordum. Kullanılacak paketler olduğundan eminim ama kendim yapmak istiyordum.

Daha sonra Josh Comeau'un bu makalesine rastladım: The Quest for the Perfect Dark Mode. Kafamda iki şey netleşmiş oldu: CSS değişkenleri ve prefers-color-scheme.

Bu yazıda, bir Gatsby ve Tailwind CSS projesinde karanlık modun nasıl uygulanacağıyla ilgili sürecimde size yol göstereceğim.

CSS değişkenlerini ekleme

Öncelikle ana css dosyamızdaki tüm css değişkenlerimizi tanımlayalım.

Örneğin bu websiteyi ele alalım, hem arka plan hem de metinler için üç renk mevcut: primary, secondary, ve accent. Renkler tasarımınıza göre değişecektir.

Color Palette

Daha sonra, .light ve .dark classlarını ekleyin ve her bir değişken için renkleri tanımlayın. Ardından, sayfanız için varsayılan bir tema uygulamak için :root içerisinde @apply yönergesini tanımlayın.

index.css
:root {
@apply .light;
}
.dark {
--color-bg-primary: #2d3748;
--color-bg-secondary: #283141;
--color-text-primary: #f7fafc;
--color-text-secondary: #e2e8f0;
--color-text-accent: #81e6d9;
}
.light {
--color-bg-primary: #ffffff;
--color-bg-secondary: #edf2f7;
--color-text-primary: #2d3748;
--color-text-secondary: #4a5568;
--color-text-accent: #2b6cb0;
}
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

Tailwind CSS'e Müdahele Etme

Oluşturduğumuz css değişkenlerini kullanabilmek için Tailwind CSS'e müdahele etmemiz gerekiyor.

tailwind.config.js
module.exports = {
theme: {
extend: {
backgroundColor: {
primary: 'var(--color-bg-primary)',
secondary: 'var(--color-bg-secondary)',
},
textColor: {
accent: 'var(--color-text-accent)',
primary: 'var(--color-text-primary)',
secondary: 'var(--color-text-secondary)',
},
},
},
}

Tailwind'in config.js dosyasında yaptığımız bu değişiklik ile özel classlarımız Tailwind'e dahil olacak.

Toggle Ekleme

Kullanıcıya toggle butonu verebilmek için gerekli React fonksiyonumuzu ekleyelim.

Varsayılan Temayı Alma

themeContext.jsx
const getInitialTheme = _ => {
if (typeof window !== 'undefined' &&
window.localStorage) {
const storedPrefs =
window.localStorage.getItem('color-theme')
if (typeof storedPrefs === 'string') {
return storedPrefs
}
const userMedia =
window.matchMedia('(prefers-color-scheme: dark)')
if (userMedia.matches) {
return 'dark'
}
}
// Varsayılan olarak açık tema isterseniz burayı 'light' olarak değiştirin.
return 'dark'
}

Burada önce localStorage içerisinde bir değerimiz olup olmadığını kontrol ediyoruz. Eğer yoksa kullanıcının tarayıcısının prefers-color-scheme sorgusunu kullanarak açık veya koyu tema tercih edip etmediğini kontrol ediyoruz.

Context Oluşturma

Her bileşende desteği manuel olarak aşağı aktarmak zorunda kalmadan tema verilerimizi iletmek için Context API'i kullanıyoruz.

Context'imiz aşağıdakileri yapmalı:

  1. Tema için bir state oluşturmak daha önce yazdığımız getInitialTheme fonksiyonuna göndermek.
  2. CSS :root içinde .dark veya .light class'ını uygulayacak ve uygulanan değeri localStorage'e kaydedecek rawSetTheme adında başka bir fonksiyon oluşturarak.
  3. theme değeri değiştiğinde rawSetTheme'i çağıracak.
themeContext.jsx
export const ThemeContext = createContext()
export const ThemeProvider = ({ initialTheme, children }) => {
const [theme, setTheme] = useState(getInitialTheme)
const rawSetTheme = theme => {
const root = window.document.documentElement
const isDark = theme === 'dark'
root.classList.remove(isDark ? 'light' : 'dark')
root.classList.add(theme)
localStorage.setItem('color-theme', theme)
}
if (initialTheme) {
rawSetTheme(initialTheme)
}
React.useEffect(
_ => {
rawSetTheme(theme)
},
[theme]
)
return (
<ThemeContext.Provider value={{ theme, setTheme }}> {children} </ThemeContext.Provider>
)
}

Context Provider Kullanma

Componentlerin oluşturduğumuz ThemeProvider context'ini kullanması için onu Higher Order Component (HOC) yapalım.

layout.jsx
import { ThemeProvider } from './themeContext'
const Layout = ({ children }) => {
return (
<ThemeProvider> <Header /> <main>{children}</main> </ThemeProvider>
)
}

Toggle Fonksiyonlarını Ekleme

Context'imiz hazır olduğuna göre artık toggle'ımıza fonksiyon ekleyebiliriz.

  1. theme ve setTheme için ThemeContext'i kullanma.
  2. theme dark'a eşitle checkbox checked'ı true olarak ayarlama.
  3. onChange eventında setTheme'i çalıştırma.
toggle.jsx
export const Toggle = () => {
const { theme, setTheme } = useContext(ThemeContext)
function isDark() {
return theme === 'dark'
}
function toggleTheme(e) {
setTheme(e.target.checked ? 'dark' : 'light')
}
return (
<label> <input type="checkbox" checked={isDark()} onChange={e => toggleTheme(e)} /> Dark Mode </label>
)
}

İşte Bu Kadar!

Artık toggle'ımız Componentlerden bağımsız olarak çalışıyor.

Dark Mode Tasarımı

Gördüğünüz üzere bu websitesinde karanlık mod için çok fazla fonksiyon yok, eğer karmaşık uygulamanızın arayüzünü karanlık moda çevirmek istiyorsanız aşağıdaki makaleye göz atabilirsiniz.

in-depth article about Dark UI by Miklos Philips.