diff --git a/docs/tasarim/DESIGN.md b/docs/tasarim/DESIGN.md index 5d62bbe..f897b3c 100644 --- a/docs/tasarim/DESIGN.md +++ b/docs/tasarim/DESIGN.md @@ -135,12 +135,12 @@ Seçici `src/components/tema-secici.tsx` (footer'da), sağlayıcı `src/componen **Geçiş ve kısayol (7 Eki 2026; son hâli 9 Eki):** tema değişimi anlık değildir; yeni tema header'daki logodan **yumuşak bir halka** gibi açılır: kenarı keskin değil, geniş ve -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), kısa ve hafif: 600 ms. **Geçiş bitince** logonun kareleri yeni temanın biçimine dönüşür: koyuya geçişte şeftali kare hilale, mavi kare hilalin iki ucunun ortasındaki yıldıza (turuncu söner); 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; hover döngüsünden önceliklidir, biçim bitince imleç üzerindeyse döngü kaldığı yerden değil yerinden sürer). Tema -seçicinin ikonları aynı biçimlerin durağan hâlidir (`tema-ikonu.tsx`: güneş, ay, Sistem için +seçicinin ikonları aynı biçimlerin durağan hâlidir (ayrıntı ve kurallar §4.22) (`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 değişir. Denenip bırakılanlar: logo biçimli maske ("logo içinde logo"), düz crossfade, piksel @@ -714,6 +714,41 @@ genişletilebilir olmalı; UI için prompt-kit kullan." zaman yazılıdır (§2.2). Sohbet ilerlese de kalır. Yeni bir liste uyarısı gerekince `listeUyarilari`'na eklenir, sayfaya yeni kutu açılmaz. +### 4.22 Logo biçim dönüşümü (morph) — `AnimasyonluMark` `temaSekli`, `tema-ikonu.tsx` + +Bilal'in kararı (9 Eki 2026): logonun üç karesi yalnız süs değildir; **bir durumu anlatmak +için kısa bir an başka bir biçime dönüşüp geri toplanabilir.** İlk ve şimdilik tek kullanım +tema değişimidir (§2.3). Bu, markanın yumuşak ve oyunsu tarafının imza hareketidir; piksel +imzası (§1) durağan dokuysa bu onun canlı karşılığıdır. + +**Biçimler** (180'lik logo kutusunda; renkler logonun kendi renkleri, iki temada aynı): + +| Durum | Mavi kare | Şeftali kare | Turuncu kare | +|---|---|---|---| +| Açık tema: **güneş** | merkezde söner | hale: büyük, yarı saydam daire | güneş: küçük dolu daire | +| Koyu tema: **ay** | yıldız: hilalin iki sivri ucunun tam ortasında | hilal: sırtı solda | yıldızın yerinde söner | +| Sistem (yalnız ikon) | logo karesi | logo karesi | logo karesi | + +Simetri kasıtlıdır: güneşte mavi, ayda turuncu sahneden çekilir. Hilal, kareyi kesen ikinci +bir şekille değil karenin kendi iç gölgesiyle çizilir (zemin rengine bağlı kalmaz). + +**Koreografi:** toplam 1,7 sn — %30'da biçim tamam, %70'e kadar durur, sonra logoya döner. +Kareler yuvarlanırken yer değiştirir ve boy değiştirir; sekme yok (§6). Sayfa geçişiyle +**üst üste binmez**: önce sayfa geçişi biter (600 ms), kısa bir nefes, sonra logo dönüşür. +Hover döngüsünden önceliklidir; biçim bitince imleç hâlâ üzerindeyse hover yerinden sürer. +"Hareketi azalt" açıksa dönüşüm oynamaz. + +**Durağan hâli:** tema seçicinin ikonları aynı biçimlerdir (`tema-ikonu.tsx`). Logo ile ikon +aynı geometriyi kullanır; biri değişirse öbürü de aynı commit'te değişir. + +**Kurallar:** +- Dönüşüm yalnız header'daki logoda oynar; aynı ekranda iki logo birden dönüşmez. +- Bir **durum değişimini** anlatır (tema gibi), dekor olarak döngüye alınmaz, sayfa + açılışında oynatılmaz. +- Yeni bir biçim eklenecekse üç kareden ve logonun üç renginden çıkar; dördüncü şekil, + yeni renk, çizgi ikon eklenmez. Her biçimin durağan ikon karşılığı da yazılır. +- Biçim logonun kendi alanına sığar (x 27–151): sağındaki "KolayTercih" yazısına değmez. + ## 5. Yerleşim ### 5.1 Genel @@ -783,6 +818,8 @@ Hareket kuralları (Apple, `motion/react`): - Kaydırmaya bağlı tetik `IntersectionObserver` ile (kaydırma dinleyicisi değil). - "Hareketi azalt": kayma/yay yok, yalnız opaklık (`useReducedMotion`, `motion-reduce:transition-opacity`). +- Logo kareleri bir durumu anlatmak için kısa bir an başka biçime dönüşebilir (§4.22); sayfa + geçişiyle üst üste binmez, sırayla oynar. ## 7. Yap / Yapma @@ -800,6 +837,7 @@ Hareket kuralları (Apple, `motion/react`): | Gösterge kareleri masonry (kaymış sütun, farklı boy) | Hizalı eşit ızgara; 8 px'lik "neredeyse hizalı" kaymalar | | Liste satırında ad sarar, sayı alt satıra iner | `truncate` ile yarım ad; 32 px satır butonu | | Hareket: `Reveal`, piksel ısısı, `kt-unblur`, sekmesiz yay; `animate-ping` yalnızca "şu anda canlı" noktasında | Her elemanda `fade-up`, `whileHover scale 1.05`, `animate-bounce` | +| Tema ikonu ve logo dönüşümü: logonun üç karesinden güneş / ay (§4.22) | Tema seçicide lucide `Sun`/`Moon`; logoyu döngüde sürekli dönüştürmek | | Başlık `font-heading`, vurgu `font-bricolage` | Inter/Roboto/serif, tek kelimelik BÜYÜK HARF etiket, 10–11 px yazı | | Risk rengi + metin etiketi; tür ayrımı ikonla | Renk tek başına, "yerleşirsin" gibi vaat cümlesi | | Üç kart yalnızca üç gerçek öğe varsa | Hero + üç kart iskeleti refleksi | diff --git a/src/app/globals.css b/src/app/globals.css index 9856f46..8a63b82 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -483,7 +483,8 @@ değil: yarıçapın %55'ine kadar tam opak, oradan kenara doğru tüy gibi söner (--kt-tema-cekirdek; JS'teki CEKIRDEK ile aynı olmalı) — ışık yayılır gibi, sert çizgi yok. Yarıçap (--kt-tema-r) 0 → --kt-tema-son - yumuşak kalkıp yumuşak durur. Merkez ve son yarıçap JS'ten gelir. + yumuşak kalkıp yumuşak durur; kısa ve hafif (600 ms) — asıl gösteri, + geçiş bitince oynayan logo dönüşümüdür. Merkez ve son yarıçap JS'ten gelir. Hareket azaltılmışsa JS geçişi hiç başlatmaz, tema düz değişir. */ @property --kt-tema-r { syntax: ""; @@ -509,7 +510,7 @@ #000 calc(var(--kt-tema-r) * var(--kt-tema-cekirdek)), transparent var(--kt-tema-r) ); - animation: kt-tema-halka 1100ms cubic-bezier(0.45, 0, 0.2, 1) both; + animation: kt-tema-halka 600ms cubic-bezier(0.4, 0, 0.2, 1) both; } @keyframes kt-tema-halka { from { diff --git a/src/components/animasyonlu-mark.tsx b/src/components/animasyonlu-mark.tsx index dbcf91a..4e40759 100644 --- a/src/components/animasyonlu-mark.tsx +++ b/src/components/animasyonlu-mark.tsx @@ -103,7 +103,7 @@ function merkezde(cx: number, cy: number, boyut: number): string { return `${((cx - boyut / 2) / boyut) * 100}% ${((cy - boyut / 2) / boyut) * 100}%`; } -// Tema geçişi biçimi (DESIGN.md §2.3): geçiş logodan yayılırken kareler +// Tema geçişi biçimi (DESIGN.md §2.3): sayfa geçişi bitince kareler // neye geçildiğini gösterir, kısa bir an durur ve yeniden logoya toplanır. // - Koyuya: şeftali kare hilale, mavi kare hilalin kucağındaki yıldıza; // turuncu söner. @@ -404,7 +404,7 @@ export function AnimasyonluMark({ const cik = () => kontrolRef.current?.ayarla(null, true); hoverHedefi.addEventListener("mouseenter", gir); hoverHedefi.addEventListener("mouseleave", cik); - // Tema geçişi başlarken (lib/tema-gecisi.ts) kareler yeni temanın + // Tema geçişi bitince (lib/tema-gecisi.ts) kareler yeni temanın // biçimine dönüşür: koyuya geçişte ay ve yıldızlar, açığa geçişte güneş. // Olay anında yeni temanın sınıfını taşır. const temaGecti = () => diff --git a/src/lib/tema-gecisi.ts b/src/lib/tema-gecisi.ts index 72644ff..88939e2 100644 --- a/src/lib/tema-gecisi.ts +++ b/src/lib/tema-gecisi.ts @@ -1,8 +1,9 @@ // Tema geçişi (DESIGN.md §2.3): açık ↔ koyu değişimi ekranı bir anda // çevirmez; yeni tema header'daki KolayTercih işaretinden yumuşak bir halka // gibi açılır — kenarı keskin değil, geniş ve tüylü; ışık yayılır gibi. -// Aynı anda logonun kareleri yeni temanın biçimine dönüşür (koyuya: ay ve -// yıldızlar; açığa: güneş), kısa bir an durur ve yeniden logoya toplanır. +// Geçiş kısa ve hafiftir; BİTİNCE logonun kareleri yeni temanın biçimine +// dönüşür (koyuya: ay ve yıldız; açığa: güneş), kısa bir an durur ve +// yeniden logoya toplanır. // Markanın yumuşak, oyunsu diline uysun diye sert kenar, piksel ya da ikinci // bir şekil yok (9 Eki 2026). // @@ -14,7 +15,7 @@ /** Geçiş sırasında 'de duran sınıf (globals.css). */ const GECIS_SINIFI = "kt-tema-gecis"; -/** Geçiş başlarken pencereye atılan olay; header'daki logo bunu dinleyip +/** Geçiş bitince pencereye atılan olay; header'daki logo bunu dinleyip * karelerini güneşe ya da aya dönüştürür (animasyonlu-mark.tsx). */ export const TEMA_GECIS_OLAYI = "kt-tema-gecis"; @@ -23,6 +24,9 @@ export const TEMA_GECIS_OLAYI = "kt-tema-gecis"; // en uzak köşeyi de örtecek kadar büyük seçilir. const CEKIRDEK = 0.55; +/** Sayfa geçişi ile logo dönüşümü arasındaki kısa nefes (ms). */ +const LOGO_NEFESI = 80; + let suruyor = false; /** @@ -36,10 +40,20 @@ export function temayiGecisle(uygula: () => void) { return; } const azHareket = window.matchMedia("(prefers-reduced-motion: reduce)").matches; - if (!document.startViewTransition || azHareket) { + if (azHareket) { uygula(); return; } + if (!document.startViewTransition) { + // Sayfa geçişi yok ama logo yine dönüşür; temanın sınıfı 'e + // yazılsın diye kısa bir an beklenir. + uygula(); + window.setTimeout( + () => window.dispatchEvent(new Event(TEMA_GECIS_OLAYI)), + LOGO_NEFESI, + ); + return; + } if (suruyor) return; const kok = document.documentElement; @@ -64,9 +78,7 @@ export function temayiGecisle(uygula: () => void) { const gecis = document.startViewTransition(uygula); // Sekme gizliyken / yeniden boyutlanırken tarayıcı geçişi iptal eder: // `ready` reddedilir, tema yine uygulanmıştır; konsola hata düşmesin. - gecis.ready - .then(() => window.dispatchEvent(new Event(TEMA_GECIS_OLAYI))) - .catch(() => {}); + gecis.ready.catch(() => {}); gecis.finished .catch(() => {}) .finally(() => { @@ -75,5 +87,14 @@ export function temayiGecisle(uygula: () => void) { kok.style.removeProperty("--kt-tema-y"); kok.style.removeProperty("--kt-tema-son"); suruyor = false; + // Sıra: önce sayfa geçişi biter, sonra logo dönüşür — ikisi üst üste + // binmez, göz önce temaya sonra logoya gider. Kısa nefes (LOGO_NEFESI): + // geçiş iptal olduysa (sekme gizli vb.) `finished` hemen döner ve + // temanın sınıfı 'e henüz yazılmamış olabilir; logo yanlış + // biçime dönüşmesin. + window.setTimeout( + () => window.dispatchEvent(new Event(TEMA_GECIS_OLAYI)), + LOGO_NEFESI, + ); }); }