feat(ux): sihirbaz adımları adlandı; Emil Kowalski ilkeleriyle hareket inceltmesi

- Adım göstergesi: Alan · Şehir · Öncelik; dolum transform ile soldan,
  güçlü ease-out 300 ms; adım değişimi ekran okuyucuya duyurulur
- Çekmecede "Listen tamam" satırı @starting-style ile küçük giriş
  (scale 0.95 + opaklık); hareketi azalt'ta yalnız opaklık
- Dilim sekmesindeki kayan hapta sekme kaldırıldı (bounce 0, 0.35 sn)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-24 01:27:11 +03:00
parent e29a0ceb9a
commit 169080df9d
3 changed files with 40 additions and 18 deletions

View File

@@ -150,7 +150,9 @@ export function ListeCekmecesi() {
{/* Durum ekran okuyucuya da duyurulur (WCAG 4.1.3) */}
<p role="status" className="mt-1 px-10 text-center text-sm text-slate-500">
{liste.length === MANUEL_LISTE_MAX ? (
<span className="inline-flex items-center gap-1.5 font-semibold text-primary">
// 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>

View File

@@ -215,7 +215,7 @@ export function ProgramTablosu({
transition={
hareketAzalt
? { duration: 0 }
: { type: "spring", duration: 0.4, bounce: 0.15 }
: { type: "spring", duration: 0.35, bounce: 0 }
}
aria-hidden
/>

View File

@@ -56,6 +56,8 @@ export function Cip({
);
}
const ADIM_ADLARI = ["Alan", "Şehir", "Öncelik"] as const;
// Haritada karşılığı olan iller (KKTC/yurtdışı kampüsler çip olarak kalır)
const HARITA_ILLERI = new Set(cities.map((c) => normalizeIlAdi(c.name)));
@@ -237,22 +239,40 @@ export function SihirbazAdimlar({
return (
<div className="flex flex-col gap-5">
<div className="flex items-center gap-1.5">
{[0, 1, 2].map((i) => (
{/* Adım göstergesi (DESIGN.md §10): üç parçalı çubuk + adların
kendisi; öğrenci nerede olduğunu ve ne kaldığını görür. Değişim
ekran okuyucuya duyurulur (WCAG 4.1.3). */}
<div>
<ol className="grid grid-cols-3 gap-2" aria-label="Sihirbaz adımları">
{ADIM_ADLARI.map((ad, i) => (
<li key={ad} className="flex flex-col gap-1.5">
{/* Dolum transform ile (yalnız transform/opaklık canlandırılır);
soldan dolar, güçlü ease-out 300 ms. */}
<span className="h-1.5 overflow-hidden rounded-full bg-slate-200" aria-hidden>
<span
key={i}
className={
i === adim
? "h-1.5 w-6 rounded-full bg-primary transition-[width,background-color] duration-200 ease-out"
: i < adim
? "h-1.5 w-3 rounded-full bg-primary/50 transition-[width,background-color] duration-200 ease-out"
: "h-1.5 w-1.5 rounded-full bg-slate-200 transition-[width,background-color] duration-200 ease-out"
}
className={`block h-full origin-left rounded-full bg-primary transition-transform duration-300 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none ${
i <= adim ? "scale-x-100" : "scale-x-0"
}`}
/>
))}
<span className="ml-2 text-sm font-medium text-slate-500">
Adım {adim + 1}/3
</span>
<span
className={`text-sm ${
i === adim
? "font-semibold text-slate-900"
: i < adim
? "font-medium text-slate-600"
: "text-slate-400"
}`}
aria-current={i === adim ? "step" : undefined}
>
{ad}
</span>
</li>
))}
</ol>
<p role="status" className="sr-only">
Adım {adim + 1} / 3: {ADIM_ADLARI[adim]}
</p>
</div>
<TransitionPanel