İçeriğe geç
Teknik YazıDesign

TailwindCSS ile Modern Dark Tema Tasarımı

next-themes ve TailwindCSS ile sadece çalışan değil, iki temada da göze düzgün oturan bir dark mode yapısını nasıl kurduğuma dair sade notlar.

10 Ocak 20246 dk okuma

Dark tema artık ek özellik gibi durmuyor. İnsanlar doğrudan bekliyor. Ama iyi bir dark mode yapmak sadece arka planı siyaha boyamak değil. Yazı okunaklı olacak, yüzeyler birbirinden ayrılacak ve açık temadaki düzen duygusu karanlık temada da korunacak.

Benim için en önemli nokta şu oldu: tema değişince sitenin karakteri değişmemeli. Yani biri açık, biri koyu versiyon gibi değil; aynı sitenin iki düzgün hali gibi durmalı. Bunun için de baştan temiz bir renk sistemi kurmak gerekiyor.

Kurulum: tailwind.config ve next-themes

ts
// tailwind.config.ts
export default {
  darkMode: 'class',
  // ...
}
tsx
// app/layout.tsx
import { ThemeProvider } from 'next-themes'

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="dark"
          enableSystem={false}
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  )
}

Dikkat

Sunucu ilk anda hangi temanın seçileceğini bilmez. Bu yüzden hydration tarafında küçük görünen ama can sıkıcı uyuşmazlıklar çıkabilir.

Renkleri Değişkenle Yönetmek

Dark mode kurarken en yorucu yol, her bileşene tek tek `dark:` sınıfı eklemek. Başta hızlı gibi görünür ama proje büyüdükçe toparlaması zorlaşır. Renkleri değişkenle yönetince hem düzen korunuyor hem de sonradan ayar yapmak kolaylaşıyor.

css
:root {
  --bg:       255 255 255;
  --surface:  248 250 252;
  --text:      15  23  42;
  --muted:    100 116 139;
  --border:   226 232 240;
  --accent:    99 102 241;
}

.dark {
  --bg:         5   8  16;
  --surface:   15  18  28;
  --text:      241 245 249;
  --muted:     100 116 139;
  --border:    30  41  59;
  --accent:    129 140 248;
}

İlk Yüklenmedeki Titremeyi Önlemek

Tema düğmesi gibi parçalar ilk istemci renderında henüz hangi temada olduğunu bilmeyebilir. Bu durumda kullanıcı bir anlığına yanlış temayı görür, sonra ekran yerine oturur. Küçük bir `mounted` kontrolü bu titremeyi kesmek için yeterli oluyor.

tsx
'use client'
import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'

export function ThemeToggle() {
  const { theme, setTheme } = useTheme()
  const [mounted, setMounted] = useState(false)

  useEffect(() => setMounted(true), [])

  if (!mounted) return <div className="w-8 h-8" />

  return (
    <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
      {theme === 'dark' ? <SunIcon /> : <MoonIcon />}
    </button>
  )
}

Cam Etkili Kartlar Her Temada Aynı Çalışmaz

css
.glass {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(12px);
}

html:not(.dark) .glass {
  background: rgba(255, 255, 255, 0.75);
  border: 1px solid rgba(0, 0, 0, 0.07);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}

Cam etkisi koyu temada çoğu zaman güzel görünür ama açık temada aynı etki kolayca dağılır. O yüzden iki tema için aynı ayarı körü körüne kopyalamak yerine, her birini kendi içinde dengelemek daha doğru geliyor.

İpucu

backdrop-filter her cihazda aynı sonucu vermeyebilir. Zayıf cihazlarda okunurluk bozuluyorsa güzel görünen efektin pek anlamı kalmıyor.