feat(tema): D kısayolu, logodan yayılan tema geçişi, /surum-notlari beta sayfası (→ 1.1.0)
- lib/tema-gecisi.ts: View Transitions + CSS maskesi (kolay-tercih-mark.svg), merkez header'daki işaret; reduced-motion ve desteksiz tarayıcıda düz geçiş - tema-kisayolu.tsx: D tuşu açık ↔ koyu (yazı alanı/değiştirici tuşta pasif) - tema-secici.tsx: seçim de maskeyle oynar, altında Kbd ile D ipucu - /surum-notlari: beta açıklaması, geri bildirim (destek@kolaytercih.com), sürüm günlüğü; Beta rozeti ve footer buraya bağlanır, sitemap'e eklendi - DESIGN.md §2.3 notu Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Monitor, Moon, Sun } from "lucide-react";
|
||||
import { Kbd } from "@/components/ui/kbd";
|
||||
import { temayiGecisle } from "@/lib/tema-gecisi";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const SECENEKLER = [
|
||||
@@ -16,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).
|
||||
* hydration sonrası işaretlenir (yerleşim oynamaz). Seçim logodan yayılan
|
||||
* maskeyle uygulanır (lib/tema-gecisi.ts); altında `D` kısayolu ipucu durur.
|
||||
*/
|
||||
export function TemaSecici({ className }: { className?: string }) {
|
||||
const { theme, setTheme } = useTheme();
|
||||
@@ -27,35 +30,38 @@ export function TemaSecici({ className }: { className?: string }) {
|
||||
);
|
||||
|
||||
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 className={cn("flex flex-col items-center gap-2", className)}>
|
||||
<div
|
||||
role="radiogroup"
|
||||
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 }) => {
|
||||
const secili = tarayicida && theme === deger;
|
||||
return (
|
||||
<button
|
||||
key={deger}
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={secili}
|
||||
onClick={() => temayiGecisle(() => 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>
|
||||
<p className="flex items-center gap-1.5 text-xs text-slate-500">
|
||||
<Kbd>D</Kbd>
|
||||
<span>ile açık / koyu arasında geç</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user