feat(tema): tema seçici ikonları güneş, ay ve logo kareleri

Geçişte logonun dönüştüğü biçimlerin durağan hâli (tema-ikonu.tsx): Açık
için güneş ve halesi, Koyu için hilal ve iki yıldız, Sistem için logonun üç
karesi. lucide Sun/Moon/Monitor seçiciden kalktı. DESIGN §2.3 güncel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-10-09 00:30:58 +03:00
parent 8c48303051
commit 316f1dafa0
3 changed files with 64 additions and 8 deletions

View File

@@ -0,0 +1,53 @@
import { useId } from "react";
// Tema seçicinin ikonları (DESIGN.md §2.3): tema geçişinde header'daki logonun
// dönüştüğü biçimlerin durağan hâli (animasyonlu-mark.tsx `temaSekli`).
// - "light": güneş — turuncu daire, arkasında şeftali hale.
// - "dark": ay — şeftali hilal, yanında mavi ve turuncu iki yıldız.
// - "system": logonun üç karesi — ikisine de dönüşebilen hâl.
// Renkler logonun renkleridir ve iki temada aynıdır (§2.3); 180'lik kutu
// public/kolay-tercih-mark.svg ile aynı. Dekoratiftir: anlamı yanındaki
// etiket taşır.
const MAVI = "#3b82f6";
const SEFTALI = "#fdc7a7";
const TURUNCU = "#ff5a00";
export type TemaIkonuTuru = "light" | "dark" | "system";
export function TemaIkonu({
tur,
className,
}: {
tur: TemaIkonuTuru;
className?: string;
}) {
const maskeId = useId();
return (
<svg viewBox="0 0 180 180" aria-hidden="true" className={className}>
{tur === "light" ? (
<>
<circle cx="90" cy="90" r="84" fill={SEFTALI} opacity="0.55" />
<circle cx="90" cy="90" r="52" fill={TURUNCU} />
</>
) : tur === "dark" ? (
<>
{/* Hilal: daireden, sola-yukarı kaydırılmış ikinci daire çıkarılır. */}
<mask id={maskeId}>
<rect width="180" height="180" fill="#fff" />
<circle cx="64" cy="76" r="66" fill="#000" />
</mask>
<circle cx="104" cy="92" r="74" fill={SEFTALI} mask={`url(#${maskeId})`} />
<circle cx="40" cy="42" r="15" fill={MAVI} />
<circle cx="74" cy="92" r="10" fill={TURUNCU} />
</>
) : (
<>
<rect x="27" y="56" width="57" height="57" rx="13" fill={MAVI} />
<rect x="91" y="29" width="56" height="56" rx="13" fill={SEFTALI} />
<rect x="91" y="95" width="56" height="56" rx="13" fill={TURUNCU} />
</>
)}
</svg>
);
}

View File

@@ -2,15 +2,15 @@
import { useSyncExternalStore } from "react";
import { useTheme } from "next-themes";
import { Monitor, Moon, Sun } from "lucide-react";
import { TemaIkonu } from "@/components/tema-ikonu";
import { Kbd } from "@/components/ui/kbd";
import { temayiGecisle } from "@/lib/tema-gecisi";
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 },
{ deger: "light", etiket: "Açık" },
{ deger: "dark", etiket: "Koyu" },
{ deger: "system", etiket: "Sistem" },
] as const;
const aboneOlma = () => () => {};
@@ -18,7 +18,8 @@ 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). Seçim logodan yumuşak bir halka
* hydration sonrası işaretlenir (yerleşim oynamaz). İkonlar geçişte logonun
* dönüştüğü biçimlerdir: güneş, ay ve logonun kendisi (tema-ikonu.tsx). Seçim logodan yumuşak bir halka
* gibi yayılarak uygulanır (lib/tema-gecisi.ts); altında `D` kısayolu ipucu durur.
*/
export function TemaSecici({ className }: { className?: string }) {
@@ -36,7 +37,7 @@ export function TemaSecici({ className }: { className?: string }) {
aria-label="Tema"
className="inline-flex items-center gap-1 rounded-full border border-slate-200 bg-card p-1"
>
{SECENEKLER.map(({ deger, etiket, ikon: Ikon }) => {
{SECENEKLER.map(({ deger, etiket }) => {
const secili = tarayicida && theme === deger;
return (
<button
@@ -52,7 +53,7 @@ export function TemaSecici({ className }: { className?: string }) {
: "text-slate-600 hover:text-orange-500",
)}
>
<Ikon className="size-4" aria-hidden />
<TemaIkonu tur={deger} className="size-[18px]" />
{etiket}
</button>
);