feat(tasarim): görsel dilin tek kaynağı DESIGN.md, proje skill'i ve tasarım denetim betiği
All checks were successful
Deploy / deploy (push) Successful in 5m33s

- docs/tasarim/DESIGN.md: 9 bölümlük görsel dil belgesi (renk rolleri, tipografi,
  tek kaynak bileşenlerin sınıf dizileri, yerleşim, yap/yapma, brief şablonu)
- kolaytercih-tasarim skill'i: UI işinde tetiklenir, belgeye götürür, örnek verir;
  tasarimci ve yazilimci ajanlarına skills: ile önyüklenir
- scripts/tasarim-denetim.ts: 13 kural (Badge, CTA'da Sparkles, palet dışı ton,
  gradyan metin, parlama, hareket spam'i, çarpı, font); pnpm lint'e bağlandı
- ui-ux-pro-max genel kataloğu skills'ten .agents/skills-devre-disi/ altına taşındı
- AGENTS.md: DESIGN.md kuralı; rozet sınıfı koda göre düzeltildi (font-bricolage yoktu)
- arama-funnel-karti: palet dışı text-cyan-300 → text-orange-500

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-23 14:44:35 +03:00
parent a42a1a9920
commit dc7861737b
29 changed files with 506 additions and 13 deletions

181
docs/tasarim/DESIGN.md Normal file
View File

@@ -0,0 +1,181 @@
# KolayTercih görsel dili (DESIGN.md)
Bu dosya sitenin görsel dilinin **tek yazılı kaynağıdır**. Yeni ekran, bileşen, modal, kart
ya da buton çizmeden önce okunur; mevcut arayüz değiştirilirken buna bakılır. Değerler koddan
çıkarılmıştır (`src/app/globals.css`, `src/app/layout.tsx`, `src/components/pixel-decor.tsx`,
`src/components/ui/*`, `src/app/page.tsx`); kodla çelişirse kod değil, bu dosya düzeltilir ve
çelişki raporlanır. Kural ihlallerini `pnpm tasarim:denetim` yakalar.
Biçim: Google Stitch'in DESIGN.md düzeni (9 bölüm). Sabit değerler Tailwind sınıfı olarak
verilir; kodda da sınıf kullanılır, hex/oklch elle yazılmaz.
---
## 1. Görsel tema ve atmosfer
**Bir cümle:** Beyaz, bol nefesli, mavi bilgi + turuncu aksiyon; her yüzeyde küçük bir
**piksel imzası** (hero'daki dot-matrix Türkiye haritasının dili) dolaşır.
- Zemin her zaman beyaz; koyu zemin yalnızca CTA bandı ve çekmece başlığı gibi tek bir
vurgu yüzeyinde (`PixelField` ile).
- İmza dekoratiftir ama rastgele değildir: deterministik ızgara (hücre 10, nokta 7.4, rx 2),
imleç yaklaşınca turuncuya ısınan pikseller (`usePixelHeat`), uzaklaşınca yavaş soğuma.
- Ton: öğrenciye "sen" diyen, sakin, vaatsiz. Süs yok, gösteriş yok; hareket yalnızca
bir şey söylüyorsa var.
- **Karanlık mod yoktur.** `dark:` sınıfı yazılmaz (`.dark` token'ları shadcn kalıntısıdır).
## 2. Renk paleti ve rolleri
| Rol | Sınıf | Nerede |
|---|---|---|
| Bilgi/vurgu mavisi (`primary`) | `text-primary`, `bg-primary/10`, `border-primary/30` | Rozet metni, ikon kutusu, hero'daki italik vurgu, linkler, kart hover kenarlığı |
| **Aksiyon turuncusu** | `bg-orange-500 hover:bg-orange-600 text-white` | Funnel CTA butonları. Bir ekranda **tek** turuncu buton |
| Piksel vurgusu | `fill-orange-500` (opaklık 0.55–0.85) | Yalnızca piksel süslerinin "sıcak" noktaları |
| Başlık metni | `text-slate-900` | h1–h3, kart başlıkları |
| Gövde metni | `text-slate-600` | Paragraf, açıklama |
| İkincil metin | `text-slate-500` | Meta bilgi, yardımcı satır |
| Silik metin | `text-slate-400` | İpucu, devre dışı, placeholder |
| Kenarlık | `border-slate-200` (landing kartlarında `border-slate-200/70`) | Kart, rozet, çarpı, giriş alanı |
| İç zemin | `bg-slate-50` | Kartın içindeki ikincil kutu, tablo başlığı |
| Hover zemini | `hover:bg-slate-100` | Çarpı, ghost buton |
| Risk: güvenli | `emerald-500` / `text-emerald-600` | **Her zaman "Güvenli" metniyle** |
| Risk: az riskli | `amber-500` / `text-amber-600` | **Her zaman "Az riskli" metniyle** |
| Risk: riskli | `red-500` / `text-red-600` | **Her zaman "Riskli" metniyle** |
| Uyarı kutusu | `border-amber-200 bg-amber-50 text-amber-900` | Bilgilendirme notu |
| Hata | `text-destructive`, `text-red-600` | Form hatası |
Kurallar:
- Nötr yalnızca **slate**. `gray`, `zinc`, `neutral`, `stone` yazılmaz.
- `indigo`, `purple`, `violet`, `fuchsia`, `pink`, `cyan`, `teal`, `lime`, `green`, `yellow`
yeni kullanım açmaz. (`bg-violet-50 text-violet-600` tek bir mevcut durum çipidir; yeni yere taşıma.)
- Gradyan metin (`bg-clip-text text-transparent`) ve parlama gölgesi (`shadow-[0_0_…]`) yok.
- Hex/oklch değeri sınıf içine yazılmaz (`text-[#…]`); token ya da Tailwind tonu kullanılır.
Risk renklerinin SVG karşılığı `src/lib/risk.ts` içindeki `RISK_PIN_RENK`'ten okunur.
- Renk tek başına anlam taşımaz: risk rengi her zaman metin etiketiyle birlikte gider
(`RISK_ETIKET`, `DILIM_ETIKET` — üçüncü dilimin görünen adı "Güvenli").
## 3. Tipografi
Dört yazı tipi, her birinin tek bir işi var:
| Sınıf | Yazı tipi | İş |
|---|---|---|
| `font-heading` | Cal Sans | Tüm başlıklar (h1–h3, kart başlığı). Her zaman `font-bold` ile |
| `font-sans` (varsayılan) | Clarity City 400–800 | Gövde, buton, form, tablo |
| `font-bricolage` | Bricolage Grotesque (opsz ekseni) | Vurgu: hero'da italik mavi span, küçük etiketler/çipler, sayısal rozetler |
| `font-mono` | Geist Mono | Yalnızca kod/markdown blokları. Arayüzde kullanılmaz |
Ölçek (kopyala, uydurma):
- Hero h1: `font-heading text-5xl font-bold tracking-[0.015em] sm:text-6xl`; vurgu kelimesi
`<span className="font-bricolage italic tracking-tighter font-bold text-primary">`
- Bölüm h2: `font-heading text-3xl font-bold sm:text-4xl` (istenirse `text-slate-900`)
- Panel/kart h3: `font-heading text-xl font-bold` · liste satırı başlığı: `font-heading text-lg font-bold text-slate-900`
- Küçük başlık: `font-heading text-base font-bold text-slate-900`
- Bölüm giriş paragrafı: `max-w-xl text-lg leading-relaxed text-slate-600`
- Gövde: `text-sm` (tablo, form, kart içi) ya da `text-base leading-relaxed` (açıklama)
- Etiket/rozet metni: `text-xs font-semibold`
- Yasak: Inter, Roboto, Arial, sistem fontu, serif, `font-[…]` keyfi font; tek kelimelik
büyük harf etiketler (uppercase yok, rozet metni büyük-küçük harfli yazılır).
## 4. Bileşen stilleri (tek kaynaklar)
Bir eleman iki yerde görünüyorsa stili tek kaynaktan gelir. Aşağıdakiler yeniden tasarlanmaz.
### 4.1 Buton — `src/components/ui/button.tsx`
- Her buton `rounded-full`; `h-9` varsayılan, `h-11`/`h-12` funnel CTA'ları (44 px dokunma hedefi), `size-9` ikon butonu.
- Odak: `focus-visible:ring-3 focus-visible:ring-ring/50`; basınca `active:translate-y-px`.
- **Turuncu CTA (kanonik):**
```tsx
<Button size="lg" className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600">
Listemi oluştur
<ArrowRight className="size-4" aria-hidden />
</Button>
```
Metin önce, ok sonda. Başta ikon yok. `Sparkles` yok. İstenirse `active:scale-[0.97]`.
- İkincil: `variant="outline"`; sessiz: `variant="ghost"`; link görünümü: `variant="link"`.
### 4.2 Rozet (bölüm başlığı üstündeki kapsül) — `SectionEyebrow`, `src/components/pixel-decor.tsx`
```tsx
<SectionEyebrow>Nasıl çalışır?</SectionEyebrow>
```
Sınıfı: `inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white px-3.5 py-1.5 text-xs font-semibold text-primary` + başta `PixelMark`.
`@/components/ui/badge` **kullanılmaz**; elle pill span yazılmaz. Filtre çipleri ve nav butonları bu kuralın dışındadır (onlar etkileşimlidir).
### 4.3 Modal kapatma çarpısı — `DialogContent` varsayılanı, `src/components/ui/dialog.tsx`
`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]` + `<XIcon className="size-4" aria-hidden />` + `sr-only "Kapat"`.
Özel yerleşim gerekiyorsa `showCloseButton={false}` ver ve **aynı sınıf setini** kopyala.
### 4.4 Kart ve kutu
- Landing/öne çıkan kart: `<Card className="relative h-full overflow-hidden border-slate-200/70 shadow-sm transition-all duration-300 hover:-translate-y-1 hover:border-primary/30 hover:shadow-lg hover:shadow-primary/5">` + sağ üstte `<PixelCorner className="absolute right-4 top-4" />`.
- Sade kutu (form, panel, liste satırı): `rounded-2xl border border-slate-200 bg-white` (+ `p-5`/`p-6`). Küçük iç kutu: `rounded-xl border border-slate-200 bg-slate-50`. Not kutusu: `rounded-lg`.
- İkon kutusu: `flex size-11 items-center justify-center rounded-xl bg-primary/10` içinde `size-5 text-primary` ikon.
- Gölge: `shadow-sm` varsayılan; hover'da `shadow-lg shadow-primary/5`. Başka gölge yok.
### 4.5 Risk çipi / etiketi
Nokta + metin: `<span className="size-2 rounded-full bg-emerald-500" />` yanında `text-xs font-semibold text-emerald-600`; metin `RISK_ETIKET`'ten. Renk tek başına asla.
### 4.6 Form
shadcn `Input`, `Select`, `Textarea`, `Slider`. Etiket her alanda; hata mesajı alanın altında, ne olduğunu ve ne yapılacağını söyler. Sayısal alanlarda uç değerler (0, negatif, 1.000.000 üstü) ele alınır.
### 4.7 İkonlar
Yalnızca `lucide-react`. Boyut: `size-4` buton/satır içi (varsayılan), `size-5` ikon kutusu, `size-3.5` küçük çip, `size-6` üstü yalnızca boş durum. Dekoratif ikon `aria-hidden`; tek başına ikon buton `aria-label` ya da `sr-only` metin taşır. Emoji ikon olarak kullanılmaz.
## 5. Yerleşim
- Sayfa kabı: `mx-auto max-w-6xl px-4`. Metin sütunu: `max-w-xl` (paragraf) / `max-w-2xl`–`max-w-3xl` (uzun içerik).
- Landing bölümü: `<section className="py-20 sm:py-24">`; alt sayfalar `py-12`–`py-16`.
- Bölüm başlık bloğu (kanonik):
```tsx
<div className="flex flex-col items-center gap-4 text-center">
<SectionEyebrow>Sorun</SectionEyebrow>
<h2 className="font-heading text-3xl font-bold sm:text-4xl">…</h2>
<p className="max-w-xl text-lg leading-relaxed text-slate-600">…</p>
</div>
```
Ardından içerik `mt-12`; kart ızgarası `grid gap-6 md:grid-cols-3` (yalnızca gerçekten üç eşdeğer öğe varsa; üçe tamamlamak için içerik uydurulmaz).
- Alt sayfa başlığının altında `PagePixelDivider` (`mt-6`/`mt-8`), her sayfada **farklı seed**.
- Landing bölümleri arasında çizgi yok; `PixelDivider` (`mx-auto w-56`, farklı seed).
- Boşluk ölçeği: 4 → 6 → 8 → 12 → 16/20 → 24. Ara değer (`mt-7`, `gap-5`) yeni yerde açılmaz.
- Mobil önce (trafiğin ~%90'ı 375 px): uzun üniversite/program adları `min-w-0` + sarma ya da `truncate`; tablo yatay kaydırmaz, kart olur; sabit alt çubuk klavyeyle çakışmaz; `vh` yerine `dvh`.
## 6. Derinlik ve katman
Derinlik gölgeyle değil **piksel katmanı ve hareket farkıyla** verilir: başlık bloğu gövdeden hafif hızlı akar (`Parallax strength={16}` başlık, `7` içerik, `26` ayraç). Kartlar hover'da 4 px yükselir. Modal arka planı `bg-black/10` + hafif blur. Yığın: içerik < sabit bar < çekmece/modal (`z-50`).
## 7. Yap / Yapma
| Yap | Yapma (AI izi) |
|---|---|
| Rozet için `SectionEyebrow` | `Badge` import'u, elle `rounded-full border … px-3.5 py-1.5` span |
| CTA: metin + sondaki `ArrowRight` | Başta `Sparkles`/yıldız ikonu, "✨" |
| Nötr = slate; vurgu = primary mavi; aksiyon = orange-500 | `indigo/purple` primary, mor→mavi gradyan, `gray/zinc` nötr |
| Kart: `rounded-2xl border-slate-200 bg-white shadow-sm` | Her şeyde `rounded-3xl`, camsı `backdrop-blur` kartlar, parlama gölgesi |
| Hareket: `Reveal` (bölüm girişi), piksel ısısı, `kt-unblur`; `animate-ping` yalnızca "şu anda canlı" noktasında | Her elemanda `fade-up`, `whileHover scale 1.05`, `animate-bounce` |
| Başlık `font-heading`, vurgu `font-bricolage` | Inter/Roboto/serif, tek kelimelik BÜYÜK HARF etiket |
| Risk rengi + metin etiketi | Renk tek başına, "yerleşirsin" gibi vaat cümlesi |
| Üç kart yalnızca üç gerçek öğe varsa | Hero + üç kart iskeleti refleksi |
| Beyaz zemin, tek koyu vurgu yüzeyi | İstenmemiş karanlık mod, `dark:` sınıfı |
| Kapatma çarpısı `DialogContent` varsayılanı | Başka boyut/renkte çarpı |
## 8. Duyarlılık
- Kırılma noktaları: 375 (temel), 640 `sm:`, 768 `md:`, 1024 `lg:`. Her bileşen önce 375'te tasarlanır.
- Dokunma hedefi en az 44 px (`h-11`, `size-11`); satır içi küçük buton `h-9` yalnızca masaüstü tablo aksiyonlarında.
- Başlık ölçeği bir kademe düşer (`text-3xl` → `sm:text-4xl`), gövde 16 px altına inmez.
- Modal genişliği `max-w-[calc(100%-2rem)]`; çekmece (`Drawer`) mobilde alttan açılır.
- `prefers-reduced-motion`: her animasyonun sade karşılığı var (`motion-reduce:animate-none`, `.kt-unblur-sade`).
## 9. Ajan için brief şablonu
Yeni bir UI işi istenince önce şu dört satır doldurulur, sonra kod yazılır:
```
Amaç: Kim, hangi ekranda, ne yapacak? (ör. veli, /paket, fiyatı karşılaştırıp satın alacak)
Referans: Sitedeki en yakın mevcut ekran/bileşen (dosya yolu). Yoksa "yok" yaz ve ana sayfa bölüm bloğunu al.
Tek kaynak: Kullanılacak kanonik parçalar (SectionEyebrow, Button+ArrowRight, Card+PixelCorner, PagePixelDivider…)
Durumlar: boş / yükleniyor / hata / başarılı / girişsiz / kredisiz — hangileri var?
```
"Modern", "temiz", "şık" gibi yön belirtmeyen kelimeler kullanılmaz; yön = bu dosya.
İş bitince: `pnpm tasarim:denetim` sıfır hata + 375 px'te tek bakış (taşma, üst üste binme, dokunma hedefi).