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:
56
src/lib/tema-gecisi.ts
Normal file
56
src/lib/tema-gecisi.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
// Tema geçişi (DESIGN.md §2.3): açık ↔ koyu değişimi ekranı bir anda
|
||||
// çevirmez; yeni tema, header'daki KolayTercih işaretinin biçiminde logodan
|
||||
// başlayıp büyüyerek ekrana yayılır (View Transitions API + CSS maskesi,
|
||||
// globals.css `.kt-tema-gecis`). Tarayıcı View Transitions bilmiyorsa ya da
|
||||
// kullanıcı hareketi azaltmışsa tema düz değişir.
|
||||
|
||||
/** Header'daki işaretin kök elemanı (site-header.tsx, AnimasyonluMark). */
|
||||
export const TEMA_ODAK_ID = "kt-tema-odak";
|
||||
|
||||
/** Geçiş sırasında <html>'de duran sınıf ve maskenin merkez değişkenleri. */
|
||||
const GECIS_SINIFI = "kt-tema-gecis";
|
||||
|
||||
let suruyor = false;
|
||||
|
||||
/**
|
||||
* `uygula` temayı değiştiren çağrıdır (ör. `setTheme("dark")`). Geçiş
|
||||
* destekleniyorsa yeni tema işaretin bulunduğu noktadan maskeyle açılır;
|
||||
* işaret ekranda değilse (krom gizli sayfalar) sol üst köşeden açılır.
|
||||
*/
|
||||
export function temayiGecisle(uygula: () => void) {
|
||||
if (typeof document === "undefined") {
|
||||
uygula();
|
||||
return;
|
||||
}
|
||||
const azHareket = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
if (!document.startViewTransition || azHareket || suruyor) {
|
||||
uygula();
|
||||
return;
|
||||
}
|
||||
|
||||
const odak = document.getElementById(TEMA_ODAK_ID)?.getBoundingClientRect();
|
||||
const kok = document.documentElement;
|
||||
const x = odak && odak.width > 0 ? odak.left + odak.width / 2 : 40;
|
||||
const y = odak && odak.height > 0 ? odak.top + odak.height / 2 : 48;
|
||||
// Maske merkezden ekranın en uzak köşesine kadar büyümeli.
|
||||
const uzak = Math.hypot(
|
||||
Math.max(x, window.innerWidth - x),
|
||||
Math.max(y, window.innerHeight - y),
|
||||
);
|
||||
|
||||
kok.style.setProperty("--kt-tema-x", `${x}px`);
|
||||
kok.style.setProperty("--kt-tema-y", `${y}px`);
|
||||
// İşaretin görünür karesi viewBox'ın ~%67'si; kenar bu oranla çarpılır ki
|
||||
// en büyük hâlde köşe karesi bile ekranı aşsın.
|
||||
kok.style.setProperty("--kt-tema-son", `${Math.ceil(uzak * 3.4)}px`);
|
||||
kok.classList.add(GECIS_SINIFI);
|
||||
suruyor = true;
|
||||
|
||||
const gecis = document.startViewTransition(uygula);
|
||||
gecis.finished
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
kok.classList.remove(GECIS_SINIFI);
|
||||
suruyor = false;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user