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

@@ -138,7 +138,9 @@ tema header'daki logodan **yumuşak bir halka** gibi açılır: kenarı keskin d
tüylü (ışık yayılır gibi), 1100 ms, yumuşak kalkıp yumuşak durur. Aynı anda logonun kareleri tüylü (ışık yayılır gibi), 1100 ms, yumuşak kalkıp yumuşak durur. Aynı anda logonun kareleri
yeni temanın biçimine dönüşür: koyuya geçişte şeftali kare hilale, mavi ve turuncu kareler iki yeni temanın biçimine dönüşür: koyuya geçişte şeftali kare hilale, mavi ve turuncu kareler iki
yıldıza; açığa geçişte turuncu kare güneşe, şeftali kare halesine. Kısa bir an durur, yeniden yıldıza; açığa geçişte turuncu kare güneşe, şeftali kare halesine. Kısa bir an durur, yeniden
logoya toplanır (`animasyonlu-mark.tsx` `temaSekli`, 1,7 sn; yalnız header'daki logo). View Transitions + radyal degrade logoya toplanır (`animasyonlu-mark.tsx` `temaSekli`, 1,7 sn; yalnız header'daki logo). Tema
seçicinin ikonları aynı biçimlerin durağan hâlidir (`tema-ikonu.tsx`: güneş, ay, Sistem için
logonun üç karesi); burada lucide ikonu kullanılmaz. View Transitions + radyal degrade
maskesi: `src/lib/tema-gecisi.ts`, `globals.css` `.kt-tema-gecis`; hareket azaltılmışsa düz maskesi: `src/lib/tema-gecisi.ts`, `globals.css` `.kt-tema-gecis`; hareket azaltılmışsa düz
değişir. Denenip bırakılanlar: logo biçimli maske ("logo içinde logo"), düz crossfade, piksel değişir. Denenip bırakılanlar: logo biçimli maske ("logo içinde logo"), düz crossfade, piksel
ızgarası (sert bulundu). Kural: bu geçişte sert kenar ve ikinci bir şekil yok. ızgarası (sert bulundu). Kural: bu geçişte sert kenar ve ikinci bir şekil yok.

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 { useSyncExternalStore } from "react";
import { useTheme } from "next-themes"; 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 { Kbd } from "@/components/ui/kbd";
import { temayiGecisle } from "@/lib/tema-gecisi"; import { temayiGecisle } from "@/lib/tema-gecisi";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
const SECENEKLER = [ const SECENEKLER = [
{ deger: "light", etiket: "Açık", ikon: Sun }, { deger: "light", etiket: "Açık" },
{ deger: "dark", etiket: "Koyu", ikon: Moon }, { deger: "dark", etiket: "Koyu" },
{ deger: "system", etiket: "Sistem", ikon: Monitor }, { deger: "system", etiket: "Sistem" },
] as const; ] as const;
const aboneOlma = () => () => {}; const aboneOlma = () => () => {};
@@ -18,7 +18,8 @@ const aboneOlma = () => () => {};
/** /**
* Tema seçici: açık / koyu / sistem (DESIGN.md §2.3). Seçili tema yalnızca * 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, * 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. * gibi yayılarak uygulanır (lib/tema-gecisi.ts); altında `D` kısayolu ipucu durur.
*/ */
export function TemaSecici({ className }: { className?: string }) { export function TemaSecici({ className }: { className?: string }) {
@@ -36,7 +37,7 @@ export function TemaSecici({ className }: { className?: string }) {
aria-label="Tema" aria-label="Tema"
className="inline-flex items-center gap-1 rounded-full border border-slate-200 bg-card p-1" 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; const secili = tarayicida && theme === deger;
return ( return (
<button <button
@@ -52,7 +53,7 @@ export function TemaSecici({ className }: { className?: string }) {
: "text-slate-600 hover:text-orange-500", : "text-slate-600 hover:text-orange-500",
)} )}
> >
<Ikon className="size-4" aria-hidden /> <TemaIkonu tur={deger} className="size-[18px]" />
{etiket} {etiket}
</button> </button>
); );