From 09c68680897f1db23b3843af15a637b07df4a20f Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Thu, 24 Sep 2026 12:16:54 +0300 Subject: [PATCH] =?UTF-8?q?feat(tasarim):=20her=20CTA=20benzeri=20y=C3=BCz?= =?UTF-8?q?eyin=20arkas=C4=B1nda=20masonry=20piksel=20zemin=20(PikselZemin?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bilal'in isteği (24 Eyl 2026): piksel arka planı her CTA benzeri kısmın arkasında, büyüklü küçüklü masonry. Tek kaynak PikselZemin (açık kartta silik mavi, koyu/mavi yüzeyde beyaz); CtaBandi ve PikselCtaYuzeyi de onu kullanır. Eklenen yüzeyler: /sonuc "listen hazır" kartı, /listem dört üretim kartı + boş durum + kilit kartı, tadımlık kartı, sıralama-yok sayfası, meraklısına kapanış kartı, ana sayfa ve /paket ana paket kartı, kaydet bandı, katalog üstü kompakt satır, arama penceresi kartı. Kapsam dışı (form, durum kartı, modal düğmesi, 404) DESIGN.md §10.8'de. Co-Authored-By: Claude Fable 5.1 --- docs/tasarim/DESIGN.md | 15 +++++++- src/app/page.tsx | 4 +- src/components/arama-funnel-karti.tsx | 4 +- src/components/cta-bandi.tsx | 12 +----- src/components/piksel-cta-yuzeyi.tsx | 9 +---- src/components/piksel-zemin.tsx | 38 +++++++++++++++++++ .../liste/components/kaydet-banner.tsx | 4 +- .../odeme/components/paket-satinal.tsx | 4 +- .../pazarlama/components/meraklisina-demo.tsx | 4 +- .../rapor/components/liste-uretici.tsx | 13 +++++-- .../rapor/components/listem-govde.tsx | 4 +- .../rapor/components/listem-icerik.tsx | 4 +- .../rapor/components/tadimlik-satiri.tsx | 4 +- .../sihirbaz/components/cta-sira-form.tsx | 4 +- .../components/sihirbaz-cagri-karti.tsx | 4 +- .../sihirbaz/components/sira-gerekli.tsx | 4 +- 16 files changed, 98 insertions(+), 33 deletions(-) create mode 100644 src/components/piksel-zemin.tsx diff --git a/docs/tasarim/DESIGN.md b/docs/tasarim/DESIGN.md index 4ebf302..9aa6c2e 100644 --- a/docs/tasarim/DESIGN.md +++ b/docs/tasarim/DESIGN.md @@ -689,7 +689,20 @@ karar. `-inset-y-10`, h2 `font-heading text-2xl font-bold sm:text-3xl`, paragraf `text-white/90`. - Kullanıldığı yerler: `/sonuc`'taki iki sihirbaz paneli, katalog altındaki `CtaSiraForm` kart varyantı, "Kendi Listem" çekmecesinin altı (kompakt). Aynı sayfadaki iki band farklı - `seed` alır. Band üstündeki ikincil metin `text-white/90`, ince yazı `text-white/70`; + `seed` alır. + +**Her CTA benzeri yüzeyin arkasında piksel zemin — `PikselZemin`, `src/components/piksel-zemin.tsx`** +(Bilal, 24 Eyl 2026: "piksel arka planları her CTA benzeri kısmın arkasında olmalı, masonry gibi +büyüklü küçüklü"). CTA benzeri yüzey = tek bir aksiyona çağıran kart, bant, boş durum ya da +sayfa bölümü. Kap `relative isolate overflow-hidden`; zemin ilk çocuk. Açık kartta +`ton="acik"` (silik mavi, `text-primary/70`), mavi bant ve koyu kartta `ton="koyu"` (beyaz). +`CtaBandi` ve `PikselCtaYuzeyi` de bunu kullanır. Kapsananlar: `/sonuc` "24 tercihlik listen +hazır" kartı, `/listem` dört üretim kartı + boş durum + kilit kartı, tadımlık kartı, +sıralama-yok sayfası, meraklısına kapanış kartı, ana sayfa ve `/paket` ana paket kartı, kaydet +bandı, katalog üstü kompakt satır, arama penceresi kartı. **Kapsam dışı:** form kartları (giriş, +ödeme formu), durum kartları (ödeme sonucu), modal düğmeleri, hata/404 sayfası, yan kredi +kartları — bunlar tek aksiyona çağıran yüzey değil. Yeni bir CTA yüzeyi açan ajan zemini +ekler ve bu listeye yazar. Band üstündeki ikincil metin `text-white/90`, ince yazı `text-white/70`; `slate-300`/`slate-400` yazılmaz. **Kapanış yüzeyi — `PikselCtaYuzeyi`, `src/components/piksel-cta-yuzeyi.tsx` (yalnız ana diff --git a/src/app/page.tsx b/src/app/page.tsx index 7ad8b6b..506bf1a 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -28,6 +28,7 @@ import { SectionEyebrow, } from "@/components/pixel-decor"; import { PikselCtaYuzeyi } from "@/components/piksel-cta-yuzeyi"; +import { PikselZemin } from "@/components/piksel-zemin"; import { MAX_REVIZYON, URUNLER } from "@/lib/credits"; import { VERI_YIL_SAYISI } from "@/lib/veri-kapsami"; import { @@ -554,8 +555,9 @@ export default function Home() { > +

{URUNLER.paket.label} diff --git a/src/components/arama-funnel-karti.tsx b/src/components/arama-funnel-karti.tsx index 2ca1dae..bd35824 100644 --- a/src/components/arama-funnel-karti.tsx +++ b/src/components/arama-funnel-karti.tsx @@ -1,5 +1,6 @@ "use client"; +import { PikselZemin } from "@/components/piksel-zemin"; import { useState } from "react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { @@ -70,8 +71,9 @@ export function AramaFunnelKarti({ onSec }: { onSec: () => void }) { return (
+
-
- -
+ {children} ); diff --git a/src/components/piksel-cta-yuzeyi.tsx b/src/components/piksel-cta-yuzeyi.tsx index ff01744..be3fe43 100644 --- a/src/components/piksel-cta-yuzeyi.tsx +++ b/src/components/piksel-cta-yuzeyi.tsx @@ -2,7 +2,7 @@ import { useState, type ReactNode } from "react"; import { cn } from "@/lib/utils"; -import { PixelMasonry } from "@/components/pixel-decor"; +import { PikselZemin } from "@/components/piksel-zemin"; import { PikselListe, PikselListeAciklama } from "@/components/piksel-liste"; /** @@ -39,12 +39,7 @@ export function PikselCtaYuzeyi({ className, )} > -
- -
+
{children} diff --git a/src/components/piksel-zemin.tsx b/src/components/piksel-zemin.tsx new file mode 100644 index 0000000..74fbc96 --- /dev/null +++ b/src/components/piksel-zemin.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { PixelMasonry } from "@/components/pixel-decor"; +import { cn } from "@/lib/utils"; + +/** + * CTA yüzeylerinin piksel zemini (DESIGN.md §10.8) — tek kaynak. Bilal'in + * kararı (24 Eyl 2026): "piksel arka planları her CTA benzeri kısmın + * arkasında olmalı, masonry gibi büyüklü küçüklü." Kap `relative isolate + * overflow-hidden` olmalı; zemin ilk çocuk olarak konur. + * + * - `acik`: beyaz/açık kart — silik mavi kareler. + * - `koyu`: mavi bant ya da koyu kart — beyaz kareler. + */ +export function PikselZemin({ + seed = 7, + ton = "acik", + className, +}: { + /** Aynı ekrandaki iki yüzey farklı desen alsın */ + seed?: number; + ton?: "acik" | "koyu"; + /** Dikey taşma payı vb. (varsayılan `-inset-y-6`) */ + className?: string; +}) { + return ( +
+ +
+ ); +} diff --git a/src/features/liste/components/kaydet-banner.tsx b/src/features/liste/components/kaydet-banner.tsx index 3526d7a..7983920 100644 --- a/src/features/liste/components/kaydet-banner.tsx +++ b/src/features/liste/components/kaydet-banner.tsx @@ -6,6 +6,7 @@ // hariç her yerde görünür; kapatma oturum boyunca kalıcıdır (sessionStorage). // Exit-intent/agresif popup YOK (PRD kuralı). +import { PikselZemin } from "@/components/piksel-zemin"; import { useEffect, useState } from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; @@ -60,8 +61,9 @@ export function KaydetBanner() { ref={gorunumRef} // Telefonda metin kendi satırında, düğme altında tam genişlik: // yan yana dizince başlık kelime kelime kırılıyordu (375 px). - className="pointer-events-auto relative mx-auto flex max-w-xl flex-wrap items-center gap-3 rounded-2xl border border-slate-200 bg-white p-4 pr-14 shadow-xl sm:flex-nowrap sm:gap-4" + className="pointer-events-auto relative isolate mx-auto flex max-w-xl flex-wrap items-center gap-3 overflow-hidden rounded-2xl border border-slate-200 bg-white p-4 pr-14 shadow-xl sm:flex-nowrap sm:gap-4" > + {/* Kullanıcının o anki işletim sisteminin logosu: "bu tarayıcıda" mesajını somutlaştırır */}
diff --git a/src/features/odeme/components/paket-satinal.tsx b/src/features/odeme/components/paket-satinal.tsx index fcca771..0c04290 100644 --- a/src/features/odeme/components/paket-satinal.tsx +++ b/src/features/odeme/components/paket-satinal.tsx @@ -1,3 +1,4 @@ +import { PikselZemin } from "@/components/piksel-zemin"; import { Suspense } from "react"; import Image from "next/image"; import Link from "next/link"; @@ -58,7 +59,8 @@ export function PaketSatinAl() { className="mt-10 grid gap-6 sm:grid-cols-[1fr_auto]" > {/* Ana paket */} -
+
+

Tercih Dönemi Paketi diff --git a/src/features/pazarlama/components/meraklisina-demo.tsx b/src/features/pazarlama/components/meraklisina-demo.tsx index d34ff55..14cb772 100644 --- a/src/features/pazarlama/components/meraklisina-demo.tsx +++ b/src/features/pazarlama/components/meraklisina-demo.tsx @@ -7,6 +7,7 @@ // context'ten okur. (Sihirbaz hunisi statik bir şemayla anlatılır — bkz. // huni-semasi.tsx.) +import { PikselZemin } from "@/components/piksel-zemin"; import { createContext, useContext, @@ -234,7 +235,8 @@ export function DemoKapanisCta() { const profil = useTercihProfili(); return ( -
+
+

{profil ? "Kuralları öğrendin — sayılar zaten senin sıralamanla hesaplandı." diff --git a/src/features/rapor/components/liste-uretici.tsx b/src/features/rapor/components/liste-uretici.tsx index 8f62125..bd75128 100644 --- a/src/features/rapor/components/liste-uretici.tsx +++ b/src/features/rapor/components/liste-uretici.tsx @@ -22,6 +22,7 @@ // Kural: action cevabının hemen ardından yönlendiriciye TEK aksiyon gönder // (router.replace); ikinci aksiyon (refresh) ancak o gezinme bitince. +import { PikselZemin } from "@/components/piksel-zemin"; import { useEffect, useRef, useState, type ReactNode } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; @@ -264,7 +265,8 @@ export function ListeUretici({ // Paketli kullanıcıya paket satma: onun ürünü top-up. Liste ve sohbet // geçmişi kalıcı; kredi yükleyince kaldığı yerden devam eder. return ( -

+
+

Kredin bu liste için yetmiyor @@ -294,7 +296,8 @@ export function ListeUretici({ if (durum.ad === "hata") { return ( -
+
+

Liste kurulamadı

{durum.mesaj} @@ -344,7 +347,8 @@ function SunucuYokEkrani() { } return ( -

+
+

Listen hazır, sunucuya şu an ulaşamıyoruz

@@ -375,7 +379,8 @@ function SunucuYokEkrani() { * "Tekrar dene" yok: tekrar taze requestId üretip ikinci üretimi başlatırdı. */ function SuruyorEkrani() { return ( -
+
+

Listen zaten hazırlanıyor

Bunu başka bir sekmede başlatmışsın. İki kez kurmayalım — kredin bu diff --git a/src/features/rapor/components/listem-govde.tsx b/src/features/rapor/components/listem-govde.tsx index f2a6076..49d911c 100644 --- a/src/features/rapor/components/listem-govde.tsx +++ b/src/features/rapor/components/listem-govde.tsx @@ -4,6 +4,7 @@ // ekranda. Harita ⇄ liste köprüsü (pinden satıra, satırdan haritaya) ve // paketsiz paywall kartları burada; veri çekimi sunucuda (page.tsx) kalır. +import { PikselZemin } from "@/components/piksel-zemin"; import { useMemo, useRef, useState } from "react"; import Link from "next/link"; import { @@ -337,8 +338,9 @@ export function ListemGovde({

+

Tablo ücretsiz. diff --git a/src/features/rapor/components/listem-icerik.tsx b/src/features/rapor/components/listem-icerik.tsx index f0b0a41..fa50bbb 100644 --- a/src/features/rapor/components/listem-icerik.tsx +++ b/src/features/rapor/components/listem-icerik.tsx @@ -19,6 +19,7 @@ import { raporMaskele, ACIK_SATIR } from "@/lib/rapor-maske"; import type { Mesaj } from "./sohbet-client"; import { ListemGovde } from "./listem-govde"; import { ListemBosCta } from "./listem-bos-cta"; +import { PikselZemin } from "@/components/piksel-zemin"; import { ListeUretici } from "./liste-uretici"; import { UretimBasladiOlayi } from "./uretim-basladi-olayi"; @@ -65,7 +66,8 @@ export async function ListemIcerik({ if (!raporVar) { const bosDurum = ( -
+
+

Henüz Yapay Zeka listen yok diff --git a/src/features/rapor/components/tadimlik-satiri.tsx b/src/features/rapor/components/tadimlik-satiri.tsx index 73f2c93..aa07d4d 100644 --- a/src/features/rapor/components/tadimlik-satiri.tsx +++ b/src/features/rapor/components/tadimlik-satiri.tsx @@ -8,6 +8,7 @@ // varsa /api/tadimlik'ten kategoriye özel satır çekilip yerinde değiştirilir // (seçim sunucuda kalır). Havuzda içerik yoksa hiç render edilmez. +import { PikselZemin } from "@/components/piksel-zemin"; import { useEffect, useRef, useState } from "react"; import Link from "next/link"; import { ArrowRight } from "lucide-react"; @@ -150,7 +151,8 @@ export function TadimlikSatiri({

-
+
+ {/* Rapor listesinin satırıyla aynı düzen (§4.15): ad sarar, üniversite adı altında, taban sıra sağda iri. */}
diff --git a/src/features/sihirbaz/components/cta-sira-form.tsx b/src/features/sihirbaz/components/cta-sira-form.tsx index aedaa2f..f3a3a96 100644 --- a/src/features/sihirbaz/components/cta-sira-form.tsx +++ b/src/features/sihirbaz/components/cta-sira-form.tsx @@ -23,6 +23,7 @@ import { authClient } from "@/lib/auth-client"; import { olay, siraKovasi } from "@/lib/analitik"; import { cn } from "@/lib/utils"; import { CtaBandi } from "@/components/cta-bandi"; +import { PikselZemin } from "@/components/piksel-zemin"; import { onbellekliJsonGetir } from "@/lib/istek-cache"; import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili"; import type { SihirbazFacetleri } from "@/types/yokatlas"; @@ -373,10 +374,11 @@ export function CtaSiraForm({
+
+
+

24 tercihlik listen hazır

diff --git a/src/features/sihirbaz/components/sira-gerekli.tsx b/src/features/sihirbaz/components/sira-gerekli.tsx index bd827e9..4a0d25d 100644 --- a/src/features/sihirbaz/components/sira-gerekli.tsx +++ b/src/features/sihirbaz/components/sira-gerekli.tsx @@ -4,6 +4,7 @@ // sayfayı tazele (URL değişmez); yoksa kapıyı aç. Sıra bilgisi localStorage'daki // TercihProfili ile tüm uygulamada ortak, sunucu tablosu çerezden okur. +import { PikselZemin } from "@/components/piksel-zemin"; import { useEffect } from "react"; import { useRouter } from "next/navigation"; import Link from "next/link"; @@ -42,7 +43,8 @@ export function SiraGerekli() { } return ( -
+
+

Önce sıralamanı gir