feat(tema): açık / koyu / sistem teması
All checks were successful
Deploy / deploy (push) Successful in 6m23s

- TemaSaglayici (next-themes) + alt bilgide TemaSecici
- globals.css: .dark bloğunda palet token'ları çevrilir; bileşenlere
  dark: sınıfı yazılmaz
- yüzeyler bg-white → bg-card, koyu haplar text-slate-50, SVG'lerde
  sabit renk yerine değişken
- DESIGN.md §2.3 + tasarim-denetim kuralları (yuzey-beyaz,
  koyu-hap-beyaz-yazi, dark-sinif), AGENTS.md karanlık mod bölümü
- docs: koyu tema denetimi raporu

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-10-05 10:20:02 +03:00
parent 9e4c5608eb
commit 78ad84922a
70 changed files with 637 additions and 243 deletions

View File

@@ -0,0 +1,61 @@
"use client";
import { useSyncExternalStore } from "react";
import { useTheme } from "next-themes";
import { Monitor, Moon, Sun } from "lucide-react";
import { cn } from "@/lib/utils";
const SECENEKLER = [
{ deger: "light", etiket: "Açık", ikon: Sun },
{ deger: "dark", etiket: "Koyu", ikon: Moon },
{ deger: "system", etiket: "Sistem", ikon: Monitor },
] as const;
const aboneOlma = () => () => {};
/**
* Tema seçici: açık / koyu / sistem (DESIGN.md §2.3). Seçili tema yalnızca
* tarayıcıda bilinir; sunucu HTML'inde hiçbir seçenek işaretli gelmez,
* hydration sonrası işaretlenir (yerleşim oynamaz).
*/
export function TemaSecici({ className }: { className?: string }) {
const { theme, setTheme } = useTheme();
const tarayicida = useSyncExternalStore(
aboneOlma,
() => true,
() => false,
);
return (
<div
role="radiogroup"
aria-label="Tema"
className={cn(
"inline-flex items-center gap-1 rounded-full border border-slate-200 bg-card p-1",
className,
)}
>
{SECENEKLER.map(({ deger, etiket, ikon: Ikon }) => {
const secili = tarayicida && theme === deger;
return (
<button
key={deger}
type="button"
role="radio"
aria-checked={secili}
onClick={() => setTheme(deger)}
className={cn(
"inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-full px-3 text-sm font-semibold transition-[background-color,color,transform] duration-150 outline-none focus-visible:ring-3 focus-visible:ring-ring/50 active:scale-[0.97]",
secili
? "bg-slate-900 text-slate-50"
: "text-slate-600 hover:text-orange-500",
)}
>
<Ikon className="size-4" aria-hidden />
{etiket}
</button>
);
})}
</div>
);
}