feat(tasarim): form alanları hap, CTA bandı tek kaynak, masonry piksel zemin, çekmeceler §4.10
- Input/Select/Textarea: shadcn'in ince varsayılanı yerine üst çubuk haplarıyla aynı kalıp (h-12, rounded-full, slate-200, 16 px; md:text-sm yok); kullanım yerleri yalnız yerleşim sınıfı taşır. DESIGN.md §4.6 tam sınıf dizisi; denetime ince-form-alani (hata) ve ham-input (uyarı) - CtaBandi (mavi zemin + piksel doku) tek kaynak: /sonuc iki panel, katalog altı kart, "Kendi Listem" çekmecesi altı. Ana sayfa kapanışı koyu PikselCtaYuzeyi + 24'lük piksel liste kreatifiyle kalır (Bilal: "orası bayağı iyiydi"); yazılıp silinen satır kaldırıldı - PixelMasonry: CTA arka planları büyüklü küçüklü, hafif kaymış kareler; eşit ızgaralı PixelField kaldırıldı - Çekmece başlığındaki 12×2 piksel liste kaldırıldı (çok dolu); sayı + ilerleme çubuğu + risk dağılımı geri geldi - "Saniyede gör" çekmecesi §4.10 düzeninde: ortalı DrawerTitle + bağlam, rozet ve çizgili "neler alacaksın" listesi yok; kapı modalındaki iki rozet kalktı; denetime modal-rozet (hata) - Kapı panelinde 3 px odak halkası kırpılmıyor (px-px → p-1) - Liste satırı: üniversite adı + tür/il/süre adın altında tek akan satır, telefonda text-xs (SATIR_ALT_GRUP); katalog listelerinde ad kendi satırında (SATIR_GRUP space-y-2). Ölçüm 375 px canlı bölüm sayfası: satır yüksekliğini metin değil alt alta iki 44 px düğme belirliyor Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -257,7 +257,7 @@ export function BolumProgramListesi({
|
||||
</span>
|
||||
</span>
|
||||
{varyant ? (
|
||||
<span className={SATIR_ALT_AD}>
|
||||
<span className={`mt-0.5 ${SATIR_ALT_AD}`}>
|
||||
{varyant}
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { PikselListe } from "@/components/piksel-liste";
|
||||
import { CtaBandi } from "@/components/cta-bandi";
|
||||
import { UniLogo } from "@/components/uni-logo";
|
||||
import { RiskEtiketi } from "@/components/risk-etiketi";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -122,6 +122,7 @@ export function ListeCekmecesi() {
|
||||
// drawer içinde erken ölçüm satırları kaydırıyordu
|
||||
const [oturdu, setOturdu] = useState(false);
|
||||
|
||||
const doluluk = Math.round((liste.length / MANUEL_LISTE_MAX) * 100);
|
||||
// Risk dağılımı: riskliden güvenliye, sıfır olanlar yazılmaz
|
||||
const riskSayilari = (["riskli", "az-riskli", "guvenli"] as const)
|
||||
.map((r) => [r, liste.filter((t) => t.risk === r).length] as const)
|
||||
@@ -184,60 +185,50 @@ export function ListeCekmecesi() {
|
||||
<DrawerTitle className="px-10 text-center font-heading text-2xl font-bold text-slate-900">
|
||||
Kendi Listem
|
||||
</DrawerTitle>
|
||||
{/* Durum satırı: solda listenin piksel hâli (ilerleme çubuğunun
|
||||
yerine), sağda sayı ve risk dağılımı — dikine uzamasın diye
|
||||
yan yana (DESIGN.md §5.3). Durum ekran okuyucuya duyurulur. */}
|
||||
<div className="mt-3 flex items-center justify-center gap-3">
|
||||
<div
|
||||
className="w-40 shrink-0 rounded-xl bg-slate-950 px-2.5 py-2"
|
||||
role="progressbar"
|
||||
aria-valuenow={liste.length}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={MANUEL_LISTE_MAX}
|
||||
aria-label="Liste doluluğu"
|
||||
>
|
||||
<PikselListe
|
||||
gercek={liste.map((t) =>
|
||||
t.risk === "riskli"
|
||||
? "hayal"
|
||||
: t.risk === "az-riskli"
|
||||
? "dengeli"
|
||||
: t.risk === "guvenli"
|
||||
? "guvenli"
|
||||
: "belirsiz",
|
||||
)}
|
||||
sutun={12}
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0 text-left">
|
||||
<p role="status" className="text-sm text-slate-500">
|
||||
{liste.length === MANUEL_LISTE_MAX ? (
|
||||
// Nadir, hak edilmiş an: küçük giriş (Emil: scale 0.95 + opaklık,
|
||||
// güçlü ease-out, <300 ms). Hareketi azalt'ta yalnız opaklık.
|
||||
<span className="inline-flex items-center gap-1.5 font-semibold text-primary transition-[opacity,transform] duration-300 ease-[cubic-bezier(0.23,1,0.32,1)] starting:scale-95 starting:opacity-0 motion-reduce:starting:scale-100">
|
||||
<Check className="size-4" aria-hidden />
|
||||
Listen tamam · {liste.length} / {MANUEL_LISTE_MAX}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
{liste.length} / {MANUEL_LISTE_MAX} tercih
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
{riskSayilari.length > 0 ? (
|
||||
<p className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1">
|
||||
{riskSayilari.map(([seviye, adet]) => (
|
||||
<span key={seviye} className="inline-flex items-center gap-1.5">
|
||||
<RiskEtiketi risk={seviye} />
|
||||
<span className="text-sm font-semibold text-slate-900">
|
||||
{adet}
|
||||
</span>
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
{/* Durum ekran okuyucuya da duyurulur (WCAG 4.1.3). Bilal'in
|
||||
kararı (24 Eyl 2026): buradaki piksel liste başlığı çok dolu
|
||||
gösteriyordu; ilerleme çubuğuna geri dönüldü. */}
|
||||
<p role="status" className="mt-1 px-10 text-center text-sm text-slate-500">
|
||||
{liste.length === MANUEL_LISTE_MAX ? (
|
||||
// Nadir, hak edilmiş an: küçük giriş (Emil: scale 0.95 + opaklık,
|
||||
// güçlü ease-out, <300 ms). Hareketi azalt'ta yalnız opaklık.
|
||||
<span className="inline-flex items-center gap-1.5 font-semibold text-primary transition-[opacity,transform] duration-300 ease-[cubic-bezier(0.23,1,0.32,1)] starting:scale-95 starting:opacity-0 motion-reduce:starting:scale-100">
|
||||
<Check className="size-4" aria-hidden />
|
||||
Listen tamam · {liste.length} / {MANUEL_LISTE_MAX}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
{liste.length} / {MANUEL_LISTE_MAX} tercih
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
<div
|
||||
className="mx-auto mt-3 h-2 w-full max-w-xs overflow-hidden rounded-full bg-slate-100"
|
||||
role="progressbar"
|
||||
aria-valuenow={liste.length}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={MANUEL_LISTE_MAX}
|
||||
aria-label="Liste doluluğu"
|
||||
>
|
||||
<motion.div
|
||||
className="h-full rounded-full bg-primary"
|
||||
animate={{ width: `${doluluk}%` }}
|
||||
transition={{ type: "spring", duration: 0.5, bounce: 0 }}
|
||||
/>
|
||||
</div>
|
||||
{/* Listenin dengesi metinle (renk tek başına değil, §2.2) */}
|
||||
{riskSayilari.length > 0 ? (
|
||||
<p className="mt-2.5 flex flex-wrap items-center justify-center gap-x-3 gap-y-1">
|
||||
{riskSayilari.map(([seviye, adet]) => (
|
||||
<span key={seviye} className="inline-flex items-center gap-1.5">
|
||||
<RiskEtiketi risk={seviye} />
|
||||
<span className="text-sm font-semibold text-slate-900">
|
||||
{adet}
|
||||
</span>
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
) : null}
|
||||
{/* Denge ipucu: 24'lük iskeletin mantığı (alt sıralarda güvenli
|
||||
programlar). Yalnız riskleri bilinen ve en az 6 tercihli
|
||||
listede; vaat değil, gerekçe. */}
|
||||
@@ -331,18 +322,23 @@ export function ListeCekmecesi() {
|
||||
</div>
|
||||
) : null}
|
||||
{/* Ana sayfadaki final CTA panelinin kompakt hâli: koyu primary
|
||||
zemin + kenarlara doğru yoğunlaşan piksel dokusu (PixelField) */}
|
||||
zemin + büyüklü küçüklü piksel dokusu (PixelMasonry, CtaBandi) */}
|
||||
{/* Telefonda alt alta (başlık tek satır, buton tam genişlik):
|
||||
yan yana dizince başlık üç satıra kırılıp listenin yerini
|
||||
yiyordu (24 Eyl 2026, 375 px ölçümü). */}
|
||||
{/* Funnel yüzeyi (DESIGN.md §10.8): arama penceresindeki kartın
|
||||
koyu zemini; listenin piksel hâli başlıkta. */}
|
||||
<div className="flex flex-col gap-3 rounded-2xl border border-blue-400/20 bg-slate-950 px-5 py-4 text-left text-white sm:flex-row sm:items-center sm:justify-between sm:gap-4 sm:py-5">
|
||||
<div className="min-w-0 sm:order-1 sm:flex-1">
|
||||
{/* CTA bandı (DESIGN.md §10.8, tek kaynak CtaBandi). Bilal'in
|
||||
kararı (24 Eyl 2026): koyu slate-950 yüzeyden bu hâle dönüldü. */}
|
||||
<CtaBandi
|
||||
etiket="div"
|
||||
boyut="kompakt"
|
||||
seed={8}
|
||||
className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between sm:gap-4"
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<p className="font-heading text-xl font-bold sm:text-2xl">
|
||||
{riskEksik ? "Risk analizi sıralamanı bekliyor" : "Risk analizini biz yaptık"}
|
||||
</p>
|
||||
<p className="mt-1 hidden truncate text-sm leading-relaxed text-slate-300 sm:block">
|
||||
<p className="mt-1 hidden truncate text-sm leading-relaxed text-white sm:block">
|
||||
{riskEksik
|
||||
? "Riskleri gösterelim; istersen 24'lük listeni de kur (3 kredi)."
|
||||
: "Her seçimin yerleşme riski hazır — son kararı sen ver."}
|
||||
@@ -351,7 +347,7 @@ export function ListeCekmecesi() {
|
||||
{riskEksik ? (
|
||||
<Button
|
||||
size="lg"
|
||||
className="h-12 w-full shrink-0 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600 active:scale-[0.97] sm:order-3 sm:w-auto"
|
||||
className="h-12 w-full shrink-0 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600 active:scale-[0.97] sm:w-auto"
|
||||
onClick={() => {
|
||||
cekmeceKapat();
|
||||
// Hedef "sihirbaz": kapı kapanınca akış /sonuc'ta devam
|
||||
@@ -367,7 +363,7 @@ export function ListeCekmecesi() {
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
className="h-12 w-full shrink-0 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600 active:scale-[0.97] sm:order-3 sm:w-auto"
|
||||
className="h-12 w-full shrink-0 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600 active:scale-[0.97] sm:w-auto"
|
||||
>
|
||||
<Link href="/listem" onClick={cekmeceKapat}>
|
||||
Listemde aç
|
||||
@@ -375,7 +371,7 @@ export function ListeCekmecesi() {
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</CtaBandi>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
@@ -466,7 +462,7 @@ function CekmeceSatiri({
|
||||
>
|
||||
{tercih.isim}
|
||||
</Link>
|
||||
<span className="mt-1 flex items-center gap-2 text-sm leading-snug text-slate-600">
|
||||
<span className="mt-1 flex items-center gap-2 text-xs leading-snug text-slate-600 sm:text-sm">
|
||||
<UniLogo ad={tercih.universite} boy="xs" />
|
||||
<Link
|
||||
href={uniHref(tercih.universite)}
|
||||
|
||||
@@ -52,7 +52,7 @@ import {
|
||||
SATIR_BUTON_HUCRE,
|
||||
SATIR_BUTON_KABI,
|
||||
SATIR_CIP,
|
||||
SATIR_GRUP,
|
||||
SATIR_ALT_GRUP,
|
||||
SATIR_IKINCIL_LINK,
|
||||
SATIR_META,
|
||||
SATIR_MOBIL_SAYI,
|
||||
@@ -360,7 +360,7 @@ function ProgramSatiri({
|
||||
>
|
||||
{p.isim}
|
||||
</Link>
|
||||
<span className={SATIR_GRUP}>
|
||||
<span className={SATIR_ALT_GRUP}>
|
||||
<span className={SATIR_ALT_AD}>
|
||||
<Link
|
||||
href={`/universite/${uniSayfaSlug(p.universite)}`}
|
||||
|
||||
@@ -7,7 +7,6 @@ import { useReducedMotion } from "motion/react";
|
||||
import useMeasure from "react-use-measure";
|
||||
import { toast } from "sonner";
|
||||
import { TransitionPanel } from "@/components/core/transition-panel";
|
||||
import { SectionEyebrow } from "@/components/pixel-decor";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
@@ -293,11 +292,10 @@ function KapıIcerik({
|
||||
}
|
||||
}
|
||||
>
|
||||
<div ref={panelRef} className="flex flex-col gap-5 px-px">
|
||||
{/* p-1: geçiş paneli taşmayı kırpar; alanların 3 px odak halkası
|
||||
(Input/Select/Button ring-3) sığsın (Bilal, 24 Eyl 2026). */}
|
||||
<div ref={panelRef} className="flex flex-col gap-5 p-1">
|
||||
<div>
|
||||
{duzenle ? (
|
||||
<SectionEyebrow>Tercihlerini güncelle</SectionEyebrow>
|
||||
) : null}
|
||||
<h2 className="mt-3 font-heading text-2xl font-bold text-slate-900 first:mt-0">
|
||||
{duzenle
|
||||
? "Seçimlerini değiştir"
|
||||
@@ -343,12 +341,9 @@ function KapıIcerik({
|
||||
<form
|
||||
ref={panelRef}
|
||||
onSubmit={siraGonder}
|
||||
className="flex flex-col gap-5 px-px"
|
||||
className="flex flex-col gap-5 p-1"
|
||||
>
|
||||
<div>
|
||||
{duzenle ? (
|
||||
<SectionEyebrow>Sıralama ve tercihler</SectionEyebrow>
|
||||
) : null}
|
||||
<h2 className="mt-3 font-heading text-xl font-bold text-slate-900 first:mt-0">
|
||||
{duzenle
|
||||
? "Yeni sıralaman nedir?"
|
||||
@@ -370,7 +365,7 @@ function KapıIcerik({
|
||||
<div className="flex flex-col gap-2 sm:flex-row">
|
||||
<div className="relative sm:flex-1">
|
||||
<Trophy
|
||||
className="pointer-events-none absolute left-4 top-1/2 size-4 -translate-y-1/2 text-slate-400"
|
||||
className="pointer-events-none absolute left-5 top-1/2 size-4 -translate-y-1/2 text-slate-400"
|
||||
aria-hidden
|
||||
/>
|
||||
<Input
|
||||
@@ -383,13 +378,13 @@ function KapıIcerik({
|
||||
onFocus={preloadSihirbazAdimlar}
|
||||
aria-invalid={hata ? true : undefined}
|
||||
aria-describedby={hata ? "kapisi-siralama-hata" : undefined}
|
||||
className="h-12 bg-background pl-11 text-base"
|
||||
className="pl-12"
|
||||
/>
|
||||
</div>
|
||||
<Select value={tur} onValueChange={setTur}>
|
||||
<SelectTrigger
|
||||
aria-label="Puan türü"
|
||||
className="w-full bg-background data-[size=default]:h-12 sm:w-44"
|
||||
className="w-full sm:w-44"
|
||||
>
|
||||
<SelectValue placeholder="Puan türü" />
|
||||
</SelectTrigger>
|
||||
@@ -436,7 +431,7 @@ function KapıIcerik({
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div ref={panelRef} className="px-px">
|
||||
<div ref={panelRef} className="p-1">
|
||||
{facetler && sira != null ? (
|
||||
<div className="flex flex-col gap-4">
|
||||
{hata ? (
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
SATIR_BUTON_HUCRE,
|
||||
SATIR_CIP,
|
||||
SATIR_DUGME_BOYU,
|
||||
SATIR_GRUP,
|
||||
SATIR_ALT_GRUP,
|
||||
SATIR_IKINCIL_LINK,
|
||||
SATIR_META,
|
||||
SATIR_MOBIL_SAYI,
|
||||
@@ -248,7 +248,7 @@ export function RaporListesi({
|
||||
) : (
|
||||
<span className={SATIR_AD_DUZ}>{t.programId}</span>
|
||||
)}
|
||||
<span className={SATIR_GRUP}>
|
||||
<span className={SATIR_ALT_GRUP}>
|
||||
{p?.universite ? (
|
||||
<span className={SATIR_ALT_AD}>
|
||||
<Link
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
SATIR_ALT_AD,
|
||||
SATIR_AD_DUZ,
|
||||
SATIR_CIP,
|
||||
SATIR_GRUP,
|
||||
SATIR_ALT_GRUP,
|
||||
SATIR_META,
|
||||
} from "@/components/liste-satiri";
|
||||
import { listemUretHref, PROFIL_DEGISTI_EVENT } from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||
@@ -157,7 +157,7 @@ export function TadimlikSatiri({
|
||||
<UniLogo ad={p.universite} boy="sm" />
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className={SATIR_AD_DUZ}>{p.isim}</span>
|
||||
<span className={SATIR_GRUP}>
|
||||
<span className={SATIR_ALT_GRUP}>
|
||||
<span className={SATIR_ALT_AD}>
|
||||
{trBaslikDuzeni(p.universite)}
|
||||
</span>
|
||||
|
||||
@@ -3,14 +3,7 @@
|
||||
import { useId, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import {
|
||||
ArrowRight,
|
||||
ChartNoAxesCombined,
|
||||
Database,
|
||||
ListChecks,
|
||||
Trophy,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { ArrowRight, Trophy, X } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
@@ -20,7 +13,7 @@ import {
|
||||
DrawerDescription,
|
||||
DrawerTitle,
|
||||
} from "@/components/ui/drawer";
|
||||
import { PagePixelDivider, SectionEyebrow } from "@/components/pixel-decor";
|
||||
import { PagePixelDivider } from "@/components/pixel-decor";
|
||||
import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
|
||||
import {
|
||||
SihirbazAdimlarLazy,
|
||||
@@ -29,7 +22,7 @@ import {
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { olay, siraKovasi } from "@/lib/analitik";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { PikselCtaYuzeyi } from "@/components/piksel-cta-yuzeyi";
|
||||
import { CtaBandi } from "@/components/cta-bandi";
|
||||
import { onbellekliJsonGetir } from "@/lib/istek-cache";
|
||||
import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili";
|
||||
import type { SihirbazFacetleri } from "@/types/yokatlas";
|
||||
@@ -45,13 +38,6 @@ const PUAN_TURLERI_SECENEK = [
|
||||
{ deger: "tyt", etiket: "TYT" },
|
||||
] as const;
|
||||
|
||||
// Kanıt üçlüsü arama funnel kartıyla aynı dili konuşur (bkz. arama-funnel-karti)
|
||||
const KANITLAR = [
|
||||
{ ikon: ListChecks, etiket: "24 tercihlik plan" },
|
||||
{ ikon: ChartNoAxesCombined, etiket: "Risk analizi" },
|
||||
{ ikon: Database, etiket: "YÖK Atlas verisi" },
|
||||
] as const;
|
||||
|
||||
// Kayıtlı profilli CTA'lar /listem boş durumunun dilini taşır (bkz.
|
||||
// listem-bos-cta.tsx): büyük turuncu buton, piksel ayraç. Sınıf seti tek yerde.
|
||||
const PROFILLI_CTA_SINIF =
|
||||
@@ -65,22 +51,22 @@ function turEtiketi(tur: string) {
|
||||
function KayitliSira({
|
||||
sira,
|
||||
tur,
|
||||
koyu = false,
|
||||
beyaz = false,
|
||||
}: {
|
||||
sira: number;
|
||||
tur: string;
|
||||
/** Koyu funnel yüzeyinde (PikselCtaYuzeyi) */
|
||||
koyu?: boolean;
|
||||
/** Mavi CTA bandının üstünde (CtaBandi): beyaz yazı */
|
||||
beyaz?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1.5 text-sm",
|
||||
koyu ? "text-slate-300" : "text-slate-600",
|
||||
beyaz ? "text-white/90" : "text-slate-600",
|
||||
)}
|
||||
>
|
||||
<Trophy
|
||||
className={cn("size-4 shrink-0", koyu ? "text-orange-500" : "text-primary")}
|
||||
className={cn("size-4 shrink-0", beyaz ? "text-white" : "text-primary")}
|
||||
aria-hidden
|
||||
/>
|
||||
<span>
|
||||
@@ -88,7 +74,7 @@ function KayitliSira({
|
||||
<strong
|
||||
className={cn(
|
||||
"font-semibold tabular-nums",
|
||||
koyu ? "text-white" : "text-slate-900",
|
||||
beyaz ? "text-white" : "text-slate-900",
|
||||
)}
|
||||
>
|
||||
{sira.toLocaleString("tr-TR")}.
|
||||
@@ -278,7 +264,7 @@ export function CtaSiraForm({
|
||||
>
|
||||
<div className="relative sm:flex-1">
|
||||
<Trophy
|
||||
className="pointer-events-none absolute left-4 top-1/2 size-4 -translate-y-1/2 text-slate-400"
|
||||
className="pointer-events-none absolute left-5 top-1/2 size-4 -translate-y-1/2 text-slate-400"
|
||||
aria-hidden
|
||||
/>
|
||||
<Input
|
||||
@@ -291,7 +277,7 @@ export function CtaSiraForm({
|
||||
onFocus={preloadSihirbazAdimlar}
|
||||
aria-invalid={hata ? true : undefined}
|
||||
aria-describedby={hata ? hataId : undefined}
|
||||
className="h-12 bg-background pl-11 text-base"
|
||||
className="pl-12"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
@@ -319,20 +305,16 @@ export function CtaSiraForm({
|
||||
);
|
||||
|
||||
// Çekmece iki varyantta da aynıdır; kompakt satırda sıra adımı sayfada
|
||||
// olduğundan çekmece yalnız sihirbazı taşır.
|
||||
// olduğundan çekmece yalnız sihirbazı taşır. Sıra adımında başlık kanonik
|
||||
// alt sayfa düzeninde (DESIGN.md §4.10: ortalı başlık + tek cümle bağlam,
|
||||
// rozet yok); sihirbaz adımlarında başlıklar adımların kendisinde, çekmece
|
||||
// başlığı yalnız ekran okuyucuya.
|
||||
const siraAdimi = !kompakt && adim === "sira";
|
||||
const cekmece = (
|
||||
<Drawer open={acik} onOpenChange={acikDegisti} handleOnly>
|
||||
<DrawerContent
|
||||
className="max-h-[92dvh] bg-white sm:mx-auto sm:max-w-2xl sm:border-x sm:border-slate-200 sm:shadow-2xl"
|
||||
>
|
||||
{/* Görünür başlıklar adıma göre değişir; erişilebilir ad sabit kalır. */}
|
||||
<DrawerTitle className="sr-only">
|
||||
Sıralamana yakın programlar hangileri?
|
||||
</DrawerTitle>
|
||||
<DrawerDescription className="sr-only">
|
||||
YKS başarı sıralamanı gir; sıralamana uygun programları bu sayfadan
|
||||
ayrılmadan gör.
|
||||
</DrawerDescription>
|
||||
<DrawerClose className="absolute top-3 right-3 z-10 flex size-9 cursor-pointer items-center justify-center rounded-full border border-slate-200 bg-popover text-slate-500 transition-[background-color,transform] duration-150 hover:bg-slate-100 hover:text-slate-800 active:scale-[0.96]">
|
||||
<X className="size-4" aria-hidden />
|
||||
<span className="sr-only">Kapat</span>
|
||||
@@ -341,37 +323,28 @@ export function CtaSiraForm({
|
||||
<div
|
||||
className="min-h-0 flex-1 overflow-y-auto px-4 pt-5 pb-[max(1.25rem,env(safe-area-inset-bottom))] sm:px-6"
|
||||
>
|
||||
{!kompakt && adim === "sira" ? (
|
||||
<div className="flex flex-col gap-5">
|
||||
<div>
|
||||
<SectionEyebrow>Saniyede gör</SectionEyebrow>
|
||||
<h2 className="mt-3 font-heading text-2xl font-bold text-slate-900">
|
||||
Sıralamana yakın programlar hangileri?
|
||||
</h2>
|
||||
<p className="mt-2 max-w-xl text-sm leading-6 text-slate-600">
|
||||
Puan türünü seç, başarı sıralamanı yaz; hayal, dengeli ve
|
||||
güvenli dilimlerindeki programları bu sayfadan ayrılmadan
|
||||
gör. Ücretsiz, üyelik gerekmez.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{siraFormu}
|
||||
|
||||
<ul
|
||||
aria-label="Neler alacaksın"
|
||||
className="flex flex-wrap gap-x-4 gap-y-2 border-t border-slate-100 pt-4"
|
||||
>
|
||||
{KANITLAR.map(({ ikon: Ikon, etiket }) => (
|
||||
<li
|
||||
key={etiket}
|
||||
className="inline-flex items-center gap-1.5 text-xs font-medium text-slate-600"
|
||||
>
|
||||
<Ikon className="size-3.5 text-primary" aria-hidden />
|
||||
{etiket}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<DrawerTitle
|
||||
className={
|
||||
siraAdimi
|
||||
? "px-10 text-center font-heading text-2xl font-bold text-slate-900"
|
||||
: "sr-only"
|
||||
}
|
||||
>
|
||||
Sıralamana yakın programlar hangileri?
|
||||
</DrawerTitle>
|
||||
<DrawerDescription
|
||||
className={
|
||||
siraAdimi
|
||||
? "mx-auto mt-1 max-w-md px-4 text-center text-sm text-slate-500"
|
||||
: "sr-only"
|
||||
}
|
||||
>
|
||||
Puan türünü seç, sıralamanı yaz; hayal, dengeli ve güvenli
|
||||
dilimlerindeki programları gerçek YÖK Atlas verisiyle bu sayfadan
|
||||
ayrılmadan gör. Ücretsiz, üyelik gerekmez.
|
||||
</DrawerDescription>
|
||||
{siraAdimi ? (
|
||||
<div className="mt-5">{siraFormu}</div>
|
||||
) : facetler && sira != null ? (
|
||||
<SihirbazAdimlarLazy
|
||||
key={`${sira}-${tur}`}
|
||||
@@ -447,18 +420,17 @@ export function CtaSiraForm({
|
||||
);
|
||||
}
|
||||
|
||||
// Kart varyantı sayfa sonundaki funnel CTA'sıdır: arama kartının koyu
|
||||
// yüzeyi + 24'lük piksel liste (DESIGN.md §10.8).
|
||||
// Kart varyantı sayfa sonundaki CTA bandıdır (DESIGN.md §10.8, CtaBandi).
|
||||
if (profil) {
|
||||
return (
|
||||
<PikselCtaYuzeyi metinOfseti={5}>
|
||||
<CtaBandi seed={5}>
|
||||
<h2 className="font-heading text-2xl font-bold text-balance sm:text-3xl">
|
||||
{baslik ?? "Sıralamana yakın programlar hangileri? Saniyede gör."}
|
||||
</h2>
|
||||
<div className="mt-3">
|
||||
<KayitliSira sira={profil.sira} tur={profil.tur} koyu />
|
||||
<KayitliSira sira={profil.sira} tur={profil.tur} beyaz />
|
||||
</div>
|
||||
<p className="mt-2 max-w-xl text-base leading-relaxed text-slate-300">
|
||||
<p className="mx-auto mt-2 max-w-xl text-base leading-relaxed text-white/90">
|
||||
Gerçek YÖK Atlas verisiyle hayal, dengeli ve güvenli dilimlerindeki
|
||||
programların hazır.
|
||||
</p>
|
||||
@@ -472,17 +444,17 @@ export function CtaSiraForm({
|
||||
<ArrowRight className="size-4" aria-hidden />
|
||||
</Link>
|
||||
</Button>
|
||||
</PikselCtaYuzeyi>
|
||||
</CtaBandi>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={formRef}>
|
||||
<PikselCtaYuzeyi metinOfseti={9}>
|
||||
<CtaBandi seed={9}>
|
||||
<h2 className="font-heading text-2xl font-bold text-balance sm:text-3xl">
|
||||
{baslik ?? "Sıralamana yakın programlar hangileri? Saniyede gör."}
|
||||
</h2>
|
||||
<p className="mt-3 max-w-xl text-base leading-relaxed text-slate-300">
|
||||
<p className="mx-auto mt-3 max-w-xl text-base leading-relaxed text-white/90">
|
||||
{aciklama ??
|
||||
"YKS başarı sıralamanı gir; gerçek YÖK Atlas verisiyle sıralamana uygun hayal, dengeli ve güvenli dilimlerindeki programları anında listele. Ücretsiz, üyelik gerekmez."}
|
||||
</p>
|
||||
@@ -497,7 +469,7 @@ export function CtaSiraForm({
|
||||
Sıralamanı gir, programları gör
|
||||
<ArrowRight className="size-4" aria-hidden />
|
||||
</Button>
|
||||
</PikselCtaYuzeyi>
|
||||
</CtaBandi>
|
||||
{cekmece}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -181,9 +181,9 @@ export function HeroForm() {
|
||||
aria-invalid={hata ? true : undefined}
|
||||
aria-describedby={hata ? "siralama-hata" : undefined}
|
||||
// flex-1 yalnızca yatay dizilimde (sm+): mobilde form flex-col olduğu
|
||||
// için flex-1 inputun yüksekliğini eziyordu. Mobilde geniş padding
|
||||
// + büyük yazı; sm ve üzeri eski görünüm.
|
||||
className="h-14 bg-background px-6 py-3 text-lg sm:h-12 sm:flex-1 sm:px-2.5 sm:py-1 sm:text-base"
|
||||
// için flex-1 inputun yüksekliğini eziyordu. Telefonda hero alanı
|
||||
// bir kademe iri (DESIGN.md §4.6); sm ve üzeri temel Input.
|
||||
className="h-14 text-lg sm:h-12 sm:flex-1 sm:text-base"
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ArrowRight } from "lucide-react";
|
||||
import { animate } from "motion/mini";
|
||||
import { useReducedMotion } from "motion/react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PikselCtaYuzeyi } from "@/components/piksel-cta-yuzeyi";
|
||||
import { CtaBandi } from "@/components/cta-bandi";
|
||||
import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
|
||||
import { olay, olayBirKez, siraKovasi } from "@/lib/analitik";
|
||||
import type { SihirbazFacetleri } from "@/types/yokatlas";
|
||||
@@ -279,43 +279,41 @@ export function SihirbazBolumu({
|
||||
</Button>
|
||||
</section>
|
||||
) : girisBekliyor ? (
|
||||
<div ref={hazirPanelRef} className="mt-16 min-w-0">
|
||||
<PikselCtaYuzeyi metinOfseti={3}>
|
||||
<h2 className="font-heading text-2xl font-bold text-balance sm:text-3xl">
|
||||
Seçimlerin hazır — Yapay Zeka listen bir adım uzakta
|
||||
</h2>
|
||||
<p className="mt-3 max-w-xl text-base leading-relaxed text-slate-300">
|
||||
Yukarıdaki tablo her zaman ücretsiz. 5 deneme kredinle 24
|
||||
tercihlik Yapay Zeka listeni de kurabilirsin: ücretsiz giriş yap,
|
||||
seçimlerin kayıtlı — listen otomatik kurulsun (üretim 3 kredi,
|
||||
kalan 2 kredinle danışmana soru sorarsın).
|
||||
</p>
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
className="mt-6 h-auto min-h-12 w-full cursor-pointer whitespace-normal bg-orange-500 px-4 py-3 text-white transition-[background-color,transform] duration-200 hover:bg-orange-600 active:scale-[0.97] sm:w-auto sm:px-8"
|
||||
<CtaBandi ref={hazirPanelRef} seed={13} className="mt-16 min-w-0">
|
||||
<h2 className="font-heading text-2xl font-bold text-balance sm:text-3xl">
|
||||
Seçimlerin hazır — Yapay Zeka listen bir adım uzakta
|
||||
</h2>
|
||||
<p className="mx-auto mt-3 max-w-xl text-base leading-relaxed text-white/90">
|
||||
Yukarıdaki tablo her zaman ücretsiz. 5 deneme kredinle 24
|
||||
tercihlik Yapay Zeka listeni de kurabilirsin: ücretsiz giriş yap,
|
||||
seçimlerin kayıtlı — listen otomatik kurulsun (üretim 3 kredi,
|
||||
kalan 2 kredinle danışmana soru sorarsın).
|
||||
</p>
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
className="mt-6 h-auto min-h-12 w-full cursor-pointer whitespace-normal bg-orange-500 px-4 py-3 text-white transition-[background-color,transform] duration-200 hover:bg-orange-600 active:scale-[0.97] sm:w-auto sm:px-8"
|
||||
>
|
||||
<Link
|
||||
href={girisUrl}
|
||||
onClick={() =>
|
||||
olay("giris_cta_tiklandi", { kaynak: "hazir_panel" })
|
||||
}
|
||||
>
|
||||
<Link
|
||||
href={girisUrl}
|
||||
onClick={() =>
|
||||
olay("giris_cta_tiklandi", { kaynak: "hazir_panel" })
|
||||
}
|
||||
>
|
||||
5 deneme kredinle Yapay Zeka listeni kur — giriş yap
|
||||
<ArrowRight className="size-4" aria-hidden />
|
||||
</Link>
|
||||
</Button>
|
||||
<p className="mt-3 text-sm text-slate-400">
|
||||
Kredi kartı gerekmez · Google ya da e-posta ile 30 saniyede
|
||||
</p>
|
||||
</PikselCtaYuzeyi>
|
||||
</div>
|
||||
5 deneme kredinle Yapay Zeka listeni kur — giriş yap
|
||||
<ArrowRight className="size-4" aria-hidden />
|
||||
</Link>
|
||||
</Button>
|
||||
<p className="mt-3 text-sm text-white/70">
|
||||
Kredi kartı gerekmez · Google ya da e-posta ile 30 saniyede
|
||||
</p>
|
||||
</CtaBandi>
|
||||
) : (
|
||||
<PikselCtaYuzeyi className="mt-16 min-w-0" metinOfseti={11}>
|
||||
<CtaBandi seed={19} className="mt-16 min-w-0">
|
||||
<h2 className="font-heading text-2xl font-bold text-balance sm:text-3xl">
|
||||
24 tercihi tek tek elemek yerine, yapay zekâ kursun
|
||||
</h2>
|
||||
<p className="mt-3 max-w-xl text-base leading-relaxed text-slate-300">
|
||||
<p className="mx-auto mt-3 max-w-xl text-base leading-relaxed text-white/90">
|
||||
Tablodan elle eklemek her zaman serbest. Ama üç adımda ilgi
|
||||
alanlarını ve şehirlerini söylersen, yapay zekâ hayal, dengeli ve
|
||||
güvenli dilimlerine dağıtılmış 24 tercihi — her satırı yerleşme
|
||||
@@ -329,7 +327,7 @@ export function SihirbazBolumu({
|
||||
Yapay Zeka listemi kur
|
||||
<ArrowRight className="size-4" aria-hidden />
|
||||
</Button>
|
||||
</PikselCtaYuzeyi>
|
||||
</CtaBandi>
|
||||
)}
|
||||
|
||||
{monteEdildi ? (
|
||||
|
||||
Reference in New Issue
Block a user