@moontra/moonui 6.27.0 → 6.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,23 +43,23 @@ describe('Alert Components', () => {
43
43
  it('renders primary variant correctly', () => {
44
44
  render(<Alert variant="primary" data-testid="alert">Test</Alert>)
45
45
  const alert = screen.getByTestId('alert')
46
- expect(alert).toHaveClass('bg-primary/10')
47
- expect(alert).toHaveClass('text-primary')
46
+ expect(alert).toHaveClass('bg-primary-subtle')
47
+ expect(alert).toHaveClass('text-primary-subtle-foreground')
48
48
  expect(alert).toHaveClass('border-primary/30')
49
49
  })
50
50
 
51
51
  it('renders success variant correctly', () => {
52
52
  render(<Alert variant="success" data-testid="alert">Test</Alert>)
53
53
  const alert = screen.getByTestId('alert')
54
- expect(alert).toHaveClass('bg-success/10')
55
- expect(alert).toHaveClass('text-success')
54
+ expect(alert).toHaveClass('bg-success-subtle')
55
+ expect(alert).toHaveClass('text-success-subtle-foreground')
56
56
  expect(alert).toHaveClass('border-success/30')
57
57
  })
58
58
 
59
59
  it('renders warning variant correctly', () => {
60
60
  render(<Alert variant="warning" data-testid="alert">Test</Alert>)
61
61
  const alert = screen.getByTestId('alert')
62
- expect(alert).toHaveClass('bg-warning/10')
62
+ expect(alert).toHaveClass('bg-warning-subtle')
63
63
  // #630: metin `text-warning` idi ve `bg-warning/10` zemininde 1.98:1 ölçüldü —
64
64
  // AA'nın (4.5) çok altında. Yüzey rengi (`--warning`) DEĞİŞMEDİ; yalnız metin
65
65
  // aynı ailenin yüzey-üstü tonuna taşındı: 8.65 (light) / 15.71 (dark).
@@ -70,16 +70,16 @@ describe('Alert Components', () => {
70
70
  it('renders error variant correctly', () => {
71
71
  render(<Alert variant="error" data-testid="alert">Test</Alert>)
72
72
  const alert = screen.getByTestId('alert')
73
- expect(alert).toHaveClass('bg-destructive/10')
74
- expect(alert).toHaveClass('text-destructive')
73
+ expect(alert).toHaveClass('bg-destructive-subtle')
74
+ expect(alert).toHaveClass('text-destructive-subtle-foreground')
75
75
  expect(alert).toHaveClass('border-destructive/30')
76
76
  })
77
77
 
78
78
  it('renders info variant correctly', () => {
79
79
  render(<Alert variant="info" data-testid="alert">Test</Alert>)
80
80
  const alert = screen.getByTestId('alert')
81
- expect(alert).toHaveClass('bg-info/10')
82
- expect(alert).toHaveClass('text-info')
81
+ expect(alert).toHaveClass('bg-info-subtle')
82
+ expect(alert).toHaveClass('text-info-subtle-foreground')
83
83
  expect(alert).toHaveClass('border-info/30')
84
84
  })
85
85
 
@@ -377,7 +377,7 @@ describe('Alert Components', () => {
377
377
  const description = screen.getByTestId('alert-description')
378
378
  expect(description).toHaveClass('text-sm')
379
379
  expect(description).toHaveClass('leading-5')
380
- expect(description).toHaveClass('text-muted-foreground')
380
+ expect(description).toHaveClass('text-current')
381
381
  })
382
382
 
383
383
  it('forwards ref correctly', () => {
@@ -407,7 +407,7 @@ describe('Alert Components', () => {
407
407
  )
408
408
 
409
409
  const alert = screen.getByTestId('alert')
410
- expect(alert).toHaveClass('bg-success/10')
410
+ expect(alert).toHaveClass('bg-success-subtle')
411
411
  expect(alert).toHaveClass('py-4')
412
412
 
413
413
  expect(screen.getByText('Success!')).toBeInTheDocument()
@@ -432,7 +432,7 @@ describe('Alert Components', () => {
432
432
  )
433
433
 
434
434
  const alert = screen.getByTestId('alert')
435
- expect(alert).toHaveClass('bg-destructive/10')
435
+ expect(alert).toHaveClass('bg-destructive-subtle')
436
436
  expect(alert).toHaveClass('py-2')
437
437
  expect(alert).toHaveClass('rounded-lg')
438
438
  expect(alert).toHaveClass('pr-10')
@@ -149,11 +149,17 @@ describe('#368 koyu tema token kontrastları', () => {
149
149
  })
150
150
  })
151
151
 
152
- describe('kabul kriteri: AÇIK tema değerleri DEĞİŞMEDİ', () => {
152
+ // Bu blok #368'in KAPSAM kriteriydi: koyu tema düzeltilirken açık tema
153
+ // değerlerine dokunulmadığını kanıtlıyordu. Kalıcı bir "asla değişmez" kuralı
154
+ // DEĞİL — açık temanın kendi kontrast denetimi bu değerleri bilinçli olarak
155
+ // değiştirebilir. `--muted-foreground` 45% → 42% böyle güncellendi: #284 onu
156
+ // yalnız --muted/--background yüzeylerine göre ayarlamıştı, --secondary
157
+ // (#ebeff5) üstünde gerçek render'da 4.42 ölçüldü (AA 4.5 ister).
158
+ describe('kabul kriteri: AÇIK tema değerleri', () => {
153
159
  it.each([
154
160
  ['destructive', '0 84.2% 48%'],
155
161
  ['muted', '217.2 32.6% 96%'],
156
- ['muted-foreground', '215.4 16.3% 45%'],
162
+ ['muted-foreground', '215.4 16.3% 42%'],
157
163
  ['primary', '217.2 91.2% 51%'],
158
164
  ])('--%s açık temada sabit: %s', (name, expected) => {
159
165
  expect(tokenOku(name, 'light')).toBe(expected)
@@ -387,20 +387,30 @@ describe('Select Components', () => {
387
387
 
388
388
  rerender(<SelectItem value="item1" variant="destructive">Item</SelectItem>)
389
389
  item = screen.getByTestId('select-item')
390
- expect(item).toHaveClass('text-error')
390
+ expect(item).toHaveClass('text-destructive-subtle-foreground')
391
391
 
392
392
  rerender(<SelectItem value="item1" variant="success">Item</SelectItem>)
393
393
  item = screen.getByTestId('select-item')
394
- expect(item).toHaveClass('text-success')
394
+ expect(item).toHaveClass('text-success-subtle-foreground')
395
395
 
396
396
  // #623: `text-warning` (DEFAULT token) açık temada beyaz zeminde 2.14:1 ölçüldü —
397
397
  // AA'nın (4.5) ve hatta büyük-metin eşiğinin (3.0) altında. `--warning` aynı zamanda
398
398
  // `bg-warning` yüzeylerinde kullanıldığı için token'ın kendisi DEĞİŞTİRİLMEDİ;
399
- // metin rengi ölçülmüş bir light/dark çiftine taşındı: 4.92 (light) / 10.51 (dark).
399
+ // metin rengi ham kademe ikilisine taşınmıştı: 4.92 (light) / 10.51 (dark).
400
+ //
401
+ // Kalite koşusu 12 (#710 sınıfı): kardeş varyantlar `destructive` ve `success`
402
+ // `-subtle` token ailesine geçirilmişti, `warning` bu göçün DIŞINDA kalmıştı.
403
+ // Artık hizalandı. İki kazanım:
404
+ // 1. Kontrast 4.92 → 8.83 (light) / 10.51 → 14.43 (dark) — tokens.css'te yazılı
405
+ // 2. `dark:` override KALKTI: token zaten tema duyarlı olduğu için gereksizdi,
406
+ // üstelik `dark:` sınıfı tüketicinin kendi rengini eziyordu (#715'in kök nedeni)
407
+ // Aşağıdaki `not.toHaveClass` bilinçli bir regresyon kilidi — ham kademeye
408
+ // ya da `dark:` override'ına geri dönüş bu testi kırar.
400
409
  rerender(<SelectItem value="item1" variant="warning">Item</SelectItem>)
401
410
  item = screen.getByTestId('select-item')
402
- expect(item).toHaveClass('text-warning-700')
403
- expect(item).toHaveClass('dark:text-warning-500')
411
+ expect(item).toHaveClass('text-warning-subtle-foreground')
412
+ expect(item).not.toHaveClass('text-warning-700')
413
+ expect(item).not.toHaveClass('dark:text-warning-500')
404
414
  })
405
415
 
406
416
  it('applies size classes correctly', () => {
@@ -639,7 +649,7 @@ describe('Select Components', () => {
639
649
 
640
650
  const item = screen.getByTestId('select-item')
641
651
  expect(item).toHaveClass('py-2')
642
- expect(item).toHaveClass('text-success')
652
+ expect(item).toHaveClass('text-success-subtle-foreground')
643
653
  expect(item).toHaveClass('custom-item')
644
654
  expect(screen.getByTestId('right-icon')).toBeInTheDocument()
645
655
  expect(screen.getByTestId('custom-indicator')).toBeInTheDocument()
@@ -19,14 +19,22 @@ const alertVariants = cva(
19
19
  variants: {
20
20
  variant: {
21
21
  default: "bg-background text-foreground border-border",
22
- primary: "bg-primary/10 text-primary border-primary/30",
23
- success: "bg-success/10 text-success border-success/30",
24
- warning: "bg-warning/10 text-warning-subtle-foreground border-warning/30",
25
- error: "bg-destructive/10 text-destructive border-destructive/30",
22
+ // KONTRAST (açık tema): metin rengi ham `text-X` DEĞİL,
23
+ // `text-X-subtle-foreground` olmalı. `bg-X/10 + text-X` deseni
24
+ // açık temada ölçülen 4.11–4.45 veriyor ve WCAG AA eşiğini
25
+ // (4.5) geçmiyor. `-subtle-foreground` token'ları aynı yüzey
26
+ // için 8.20–9.52 sağlıyor (tokens.css'te oranlar yazılı) ve
27
+ // `bg-X-subtle`, `bg-X/10` ile AYNI görünümü verir
28
+ // (tokens.css:217) → görsel kimlik değişmez, yalnız metin
29
+ // koyulaşır. `warning` bu düzeltmeyi zaten taşıyordu.
30
+ primary: "bg-primary-subtle text-primary-subtle-foreground border-primary/30",
31
+ success: "bg-success-subtle text-success-subtle-foreground border-success/30",
32
+ warning: "bg-warning-subtle text-warning-subtle-foreground border-warning/30",
33
+ error: "bg-destructive-subtle text-destructive-subtle-foreground border-destructive/30",
26
34
  // `--info` token'ı (tokens.css) blue-500 ile birebir aynı değeri
27
35
  // taşır → görsel değişiklik yok, ancak varyant artık temaya ve
28
36
  // karanlık moda duyarlı. Kardeş varyantlarla simetrik.
29
- info: "bg-info/10 text-info border-info/30",
37
+ info: "bg-info-subtle text-info-subtle-foreground border-info/30",
30
38
  },
31
39
  size: {
32
40
  sm: "py-2 text-xs",
@@ -174,7 +182,12 @@ const AlertDescription = React.forwardRef<
174
182
  ref={ref}
175
183
  className={cn(
176
184
  "moonui-theme",
177
- "text-sm leading-5 text-muted-foreground",
185
+ // KONTRAST: sabit `text-muted-foreground` (#606f85) renkli varyant
186
+ // yüzeylerinde 4.31–4.48 veriyordu — başlık düzelse bile açıklama
187
+ // satırı eşiğin altında kalıyordu. `text-current` varyantın
188
+ // `-subtle-foreground` rengini miras alır; hiyerarşi renk yerine
189
+ // ağırlıkla (başlık `font-semibold`) ve hafif opaklıkla korunur.
190
+ "text-sm leading-5 text-current opacity-90",
178
191
  className
179
192
  )}
180
193
  {...props}
@@ -48,12 +48,36 @@ const badgeVariants = cva(
48
48
  "focus-visible:ring-gray-400/30 dark:focus-visible:ring-gray-500/40",
49
49
  "dark:bg-gray-700/50 dark:text-gray-50 dark:shadow-inner dark:shadow-gray-950/10",
50
50
  ],
51
+ // #708 kalem 3: `outline` varyantı TÜKETİCİYİ EZİYORDU — #715'in `button`da
52
+ // kapattığı deliğin birebir aynısı. `cn()` = `twMerge(clsx(...))`; tüketici
53
+ // `className="text-white"` yazınca twMerge ÖNEKSİZ `text-foreground`u eler,
54
+ // ama `dark:text-gray-100` AYRI modifier grubu olduğu için ELENMEZ ve koyu
55
+ // temada özgüllükle kazanır: (0,2,0) > (0,1,0).
56
+ //
57
+ // Ölçülen sonuç (docs "PRO" rozeti, `from-brand-600 to-brand-accent-600`
58
+ // gradyanı üstünde, `text-xs` = küçük metin → AA 4.5:1):
59
+ // light beyaz → en kötü uç 4.60 ✓ (tüketicinin istediği renk)
60
+ // dark gray-100 → en kötü uç 4.18 ✗ (varyantın dayattığı renk)
61
+ // A18-V 10/10 sayfada ölçtü; axe bunu `violations`a HİÇ yazmıyor (gradyan
62
+ // zemin → `incomplete` kovası), bu yüzden 11 koşu boyunca görülmedi.
63
+ //
64
+ // #715'in AYRIMI satır satır uygulandı:
65
+ // · `dark:bg-transparent` → öneksiz `bg-transparent` zaten aşağıda ve
66
+ // `outline`da başka `bg-*` yok (sıralama guard'ı da değil) → SAF TEKRAR,
67
+ // kaldırıldı.
68
+ // · `dark:text-gray-100` → öneksiz ikizinden (`--foreground` dark =
69
+ // rgb(225,231,239)) FARKLI bir değere gidiyordu, ama fark rozetin kendi
70
+ // okunurluğunda ölçülemeyecek kadar küçük (koyu kart üstünde 15.0 → 13.5;
71
+ // ikisi de AA'nın çok üstünde) ve karşılığında tüketicinin AA'yı GEÇEN
72
+ // `text-white`ını eziyordu → kaldırıldı.
73
+ // · `dark:backdrop-blur-sm` → öneksiz ikizi yok, koyu-temaya özgü gerçek
74
+ // bir efekt → KORUNDU.
51
75
  outline: [
52
76
  "border-gray-200 dark:border-gray-600",
53
77
  "bg-transparent text-foreground",
54
78
  "hover:border-gray-300 dark:hover:border-gray-500",
55
79
  "focus-visible:ring-gray-400/30 dark:focus-visible:ring-gray-500/40",
56
- "dark:text-gray-100 dark:bg-transparent dark:backdrop-blur-sm",
80
+ "dark:backdrop-blur-sm",
57
81
  ],
58
82
  // #544: `bg-error` (= --error, #ef4343) beyaz metinle 3.78 veriyordu (AA 4.5 ister),
59
83
  // `dark:bg-error/90` ise gerçek koyu kart (#111827) üstünde 4.43 ile kalıyordu.
@@ -33,11 +33,68 @@ const buttonVariants = cva(
33
33
  {
34
34
  variants: {
35
35
  variant: {
36
+ // #715 Dal 1 — GEREKSİZ `dark:` TEKRARI KALDIRILDI.
37
+ // `--primary` ve `--primary-foreground` `.dark` bloğunda ZATEN farklı değer
38
+ // alıyor (tokens.css:129/137 ↔ :545/546), yani öneksiz `bg-primary
39
+ // text-primary-foreground` iki temada da doğru çifti veriyor. `dark:` ikizi
40
+ // hiçbir şey KAZANDIRMIYOR, yalnız özgüllüğü şişiriyordu:
41
+ // .dark\:text-primary-foreground:is(.dark *) → (0,2,0)
42
+ // tüketicinin text-white → (0,1,0)
43
+ // `cn()` = twMerge: tüketici `text-white` yazınca twMerge öneksiz
44
+ // `text-primary-foreground`u ELER ama `dark:` ikizi AYRI modifier grubu
45
+ // olduğu için KALIR → koyu temada metin sessizce dark
46
+ // `--primary-foreground`a (222.2 47.4% 1.2% = rgb(2,2,5)) düşüyordu.
47
+ // Ölçüldü: beyaz metin beklerken 1.28:1 (docs/click-animations gradyan
48
+ // butonları). Öneksiz satır kaldığı için TEMA DAVRANIŞI DEĞİŞMEZ.
49
+ // Kardeş presedans: #533/#646 badge'de `dark:bg-primary/90`ı aynı gerekçeyle
50
+ // kaldırdı; button'a o turda uğranmamıştı.
51
+ //
52
+ // #715 Dal 3 — `primary`nin hover/active ALFA kompoziti AA altındaydı;
53
+ // `destructive`in (Dal 2) birebir aynı kusuru. `/90` ve `/80` zemini BEYAZLA
54
+ // karıştırıp rengi AÇIYOR, metin ise sabit `--primary-foreground` (#f8fafc):
55
+ // resting bg-primary #1067f4 → 4.70 ✓ (#442: geçiyor → DOKUNULMADI)
56
+ // hover bg-primary/90 #2876f5 → 4.01 ✗
57
+ // active bg-primary/80 #4085f6 → 3.40 ✗
58
+ // Koyu tema ZATEN geçiyor (near-white zemin + near-black metin: 19.80 /
59
+ // 15.96 / 12.50) → düzeltmenin onu bozmaması ŞART.
60
+ //
61
+ // ⚠️ Dal 2'nin çözümü (alfa yerine opak `error-NNN` kademesi) BURADA
62
+ // UYGULANAMAZ — `primary`nin skala ailesi YANLIŞ HUE'DA:
63
+ // --primary 217.2 91.2% 51% = #1067f4 (blue, h≈217)
64
+ // --primary-700 3 105 161 = #0369a1 (sky, h≈199)
65
+ // `--primary-50…950` (tokens.css:15-25) Tailwind SKY paleti; `--primary`nin
66
+ // koyusu DEĞİL, başka bir renk. Üstüne preset o kademeleri hiçbir utility'ye
67
+ // bağlamıyor → `bg-primary-700` bugün HİÇ CSS ÜRETMEYEN ölü sınıf (#713).
68
+ // Ölçülen adaylar ve RET gerekçeleri:
69
+ // · preset'e sky kademelerini bağla → açık temada 5.67 ✓, AMA (a) görünür
70
+ // hue kayması, (b) `bg-primary-600` (3.91 ✗) "primary'nin koyusu" sanılan
71
+ // yanıltıcı public API, (c) öneksiz sınıf koyu temada DA uygulanır ve
72
+ // near-black metinle 3.49'a ÇÖKER. → RET
73
+ // · `bg-blue-700` (6.41 ✓) → token'ı by-pass eder; tüketici `--primary`yi
74
+ // override edince hover mavide takılı kalır. → RET
75
+ // · `bg-primary-subtle-foreground` (#0950c3, 6.81 ✓, doğru hue, sınıf ZATEN
76
+ // üretiliyor) → FOREGROUND rolündeki token'ı ZEMİN yapmak iki rolü
77
+ // birbirine kilitler. → RET
78
+ // SEÇİLEN: koyu tonu `--primary`nin KENDİSİNDEN türet (`color-mix`, siyaha
79
+ // doğru). Tek öneksiz sınıf iki temada da doğru sonuç verir ve tüketici
80
+ // `--primary`yi değiştirdiğinde hover/active KENDİLİĞİNDEN uyar —
81
+ // tokens.css:120-128'in bilinçle koruduğu sözleşme bozulmaz.
82
+ // GERÇEK TARAYICIDA ÖLÇÜLDÜ (headless Chrome 140, piksel örneklemesi):
83
+ // hover 85% + black açık #0e57cf → 6.11 ✓ | koyu #d3d4d6 → 13.97 ✓
84
+ // active 75% + black açık #0c4db7 → 7.27 ✓ | koyu #babbbd → 10.79 ✓
85
+ // (Testteki kanal-bazlı model 1/255 yuvarlama farkıyla #0e58cf → 6.05 der;
86
+ // ikisi de eşiğin çok üstünde. `color-mix` gerçekten ÇÖZÜLÜYOR — tarayıcı
87
+ // `color(srgb 0.053652 0.342337 0.813348)` döndürüyor, yani sınıf ölü değil.)
88
+ // `dark:` GUARD GEREKMEZ: Dal 1'den sonra dizide hiç `dark:bg-*` yok; tek
89
+ // rakip öneksiz `bg-primary` (0,1,0) ve `hover:`/`active:` (0,2,0) onu yener.
90
+ // `[color:…]` ETİKETİ BİLEREK yazıldı: sınıfın renk olarak tanınmasını hem
91
+ // Tailwind'in hem twMerge'ün tip ÇIKARIMINA bırakmaz. (Etiketsiz form da
92
+ // bugün doğru çalışıyor — ikisi de deterministik probe'la ölçüldü — ama
93
+ // #715'in tamamı "sessizce yanlış sınıflandırılan sınıf" hikâyesidir.)
36
94
  primary: [
37
95
  "bg-primary text-primary-foreground",
38
- "dark:bg-primary dark:text-primary-foreground",
39
- "hover:bg-primary/90 dark:hover:bg-primary/90",
40
- "active:bg-primary/80 dark:active:bg-primary/80",
96
+ "hover:bg-[color:color-mix(in_srgb,hsl(var(--primary))_85%,black)]",
97
+ "active:bg-[color:color-mix(in_srgb,hsl(var(--primary))_75%,black)]",
41
98
  "shadow-sm hover:shadow-md",
42
99
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
43
100
  "transition-all duration-200",
@@ -51,12 +108,18 @@ const buttonVariants = cva(
51
108
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
52
109
  "shadow-sm hover:shadow-md",
53
110
  ],
111
+ // #715 Dal 1 — `outline`ın DÖRT `dark:` satırı da aynı token'ın tekrarıydı
112
+ // (`--background`/`--foreground`/`--input`/`--accent(-foreground)` hepsi
113
+ // `.dark` bloğunda tanımlı) → kaldırıldı. Kaskad davranışı korunuyor:
114
+ // Tailwind 3.4.17 çıktısında `hover:`/`active:` kuralları `dark:`ten ÖNCE
115
+ // yazılıyor, yani rakip `dark:bg-background` (0,2,0) gitmeden `hover:bg-accent`
116
+ // (0,2,0) koyu temada kaybederdi — bu yüzden satırlar TEK TEK değil,
117
+ // varyant bazında BÜTÜN olarak kaldırıldı.
54
118
  outline: [
55
119
  "bg-background text-foreground",
56
- "dark:bg-background dark:text-foreground",
57
- "border border-input dark:border-input",
58
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent dark:hover:text-accent-foreground",
59
- "active:bg-accent/80 dark:active:bg-accent/80",
120
+ "border border-input",
121
+ "hover:bg-accent hover:text-accent-foreground",
122
+ "active:bg-accent/80",
60
123
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
61
124
  "transition-all duration-200",
62
125
  ],
@@ -77,11 +140,34 @@ const buttonVariants = cva(
77
140
  // Not: `dark:bg-destructive` KALDIRILDI, yanına eklenmedi — iki `dark:bg-*`
78
141
  // utility'si aynı anda yazılırsa hangisinin kazanacağını sınıf dizgesi sırası
79
142
  // DEĞİL Tailwind'in kendi iç sıralaması belirler.
143
+ //
144
+ // #715 Dal 2 — #544 yalnız RESTING durumu ölçmüştü; alfa harmanlı DURUM
145
+ // dalları sayılmadı. Açık temada `/90` ve `/80` zemini BEYAZLA karıştırıp
146
+ // rengi AÇIYOR, yani AA'yı düşürüyor (metin sabit `--destructive-foreground`
147
+ // #f8fafc):
148
+ // resting bg-destructive #e11313 → 4.66 ✓ (#544'ün ölçtüğü)
149
+ // hover bg-destructive/90 #e42b2b → 4.29 ✗ (axe: 4.28, serious)
150
+ // active bg-destructive/80 #e74242 → 3.82 ✗
151
+ // Çözüm koyu dalın ZATEN kullandığı yaklaşım: alfa yerine opak `error`
152
+ // kademesi. Açık dal da aynı kademelere bağlandı:
153
+ // hover bg-error-700 #b91c1c → 6.18 ✓
154
+ // active bg-error-800 #991b1b → 7.94 ✓
155
+ // Yan fayda: durum ilerlemesi artık tutarlı KOYULAŞIYOR (e11313 → b91c1c →
156
+ // 991b1b); alfa sürümünde active, hover'dan DAHA AÇIK oluyordu.
157
+ // `--destructive` token'ına ve resting sınıfına DOKUNULMADI (#442).
158
+ //
159
+ // ⚠️ `dark:hover:`/`dark:active:` ikizleri öneksiziyle aynı değeri taşısa da
160
+ // KALDIRILAMAZ — `dark:bg-error-600` (resting, farklı değer) hâlâ gerekli ve
161
+ // Tailwind 3.4.17 `dark:`i `hover:`ten SONRA yazıyor (aynı özgüllük, 0,2,0):
162
+ // .hover\:bg-error-700:hover < .dark\:bg-error-600:is(.dark *)
163
+ // Öneki atarsak koyu temada hover, resting rengine geri düşer. `dark:hover:`
164
+ // (0,3,0) bu sıralamayı yener. Bu, Dal 1'deki "gereksiz tekrar"dan farklıdır:
165
+ // burada satırın ÖLÇÜLEBİLİR bir işlevi var.
80
166
  destructive: [
81
167
  "bg-destructive text-destructive-foreground",
82
- "dark:bg-error-600 dark:text-destructive-foreground",
83
- "hover:bg-destructive/90 dark:hover:bg-error-700",
84
- "active:bg-destructive/80 dark:active:bg-error-800",
168
+ "dark:bg-error-600",
169
+ "hover:bg-error-700 dark:hover:bg-error-700",
170
+ "active:bg-error-800 dark:active:bg-error-800",
85
171
  "shadow-sm hover:shadow-md",
86
172
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
87
173
  "transition-all duration-200",
@@ -92,10 +178,38 @@ const buttonVariants = cva(
92
178
  // dokunulmadı (#442 kuralı: geçen yüzeyde regresyon riski açma).
93
179
  // Dolgu opak `success` skalasına bağlandı: -600 hâlâ 3.15, ölçümle geçen
94
180
  // en açık kademe -700 (4.79).
181
+ // #715 Dal 1: `dark:text-success-foreground` gereksiz tekrardı (aynı token,
182
+ // `.dark` bloğunda tanımlı) → kaldırıldı. `dark:bg-success-700` FARKLI bir
183
+ // değere işaret ettiği için KALDI.
184
+ //
185
+ // #715 Dal 3 — AÇIK temanın hover/active alfası da AA altındaydı
186
+ // (metin sabit `--success-foreground` #f8fafc, zemin beyaz):
187
+ // resting bg-success #12823b → 4.69 ✓ (#442 → DOKUNULMADI)
188
+ // hover bg-success/90 #2a8f4f → 3.90 ✗
189
+ // active bg-success/80 #419b62 → 3.30 ✗
190
+ // Burada `destructive`in çözümü AYNEN geçerli, çünkü `--success-NNN`
191
+ // skalası `--success` ile AYNI hue'da (#15803d ↔ #12823b) — `primary`den
192
+ // ayrıldığı nokta tam da bu. Açık dal, KOYU dalın zaten kullandığı
193
+ // kademelere bağlandı (tek kaynak, tutarlı ilerleme):
194
+ // hover bg-success-800 #166534 → 6.81 ✓
195
+ // active bg-success-900 #14532d → 8.71 ✓
196
+ // -700 (4.79) BİLEREK seçilmedi: hem eşiğe fazla yakın hem de `bg-success`
197
+ // ile neredeyse aynı renk (#15803d ↔ #12823b) — hover geri bildirimi
198
+ // görünmez olurdu.
199
+ //
200
+ // ⚠️ `dark:hover:`/`dark:active:` ikizleri artık öneksiziyle AYNI değeri
201
+ // taşıyor ama KALDIRILAMAZ — `destructive`teki sıralama guard'ının aynısı.
202
+ // Tailwind 3.4.17 çıktısı (deterministik CLI probe'uyla doğrulandı) `dark:`i
203
+ // `hover:`ten SONRA yazıyor ve ikisi de (0,2,0):
204
+ // .hover\:bg-success-800:hover < .dark\:bg-success-700:is(.dark *)
205
+ // `dark:hover:` (0,3,0) bu sıralamayı yener. GERÇEK TARAYICIDA KANITLANDI:
206
+ // guard'sız ikinci bir buton üretilip koyu temada hover'landığında zemin
207
+ // rgb(21,128,61)'de KALDI (= resting/-700, hover HİÇ uygulanmadı);
208
+ // guard'lı buton rgb(22,101,52)'ye (-800) geçti.
95
209
  "bg-success text-success-foreground",
96
- "dark:bg-success-700 dark:text-success-foreground",
97
- "hover:bg-success/90 dark:hover:bg-success-800",
98
- "active:bg-success/80 dark:active:bg-success-900",
210
+ "dark:bg-success-700",
211
+ "hover:bg-success-800 dark:hover:bg-success-800",
212
+ "active:bg-success-900 dark:active:bg-success-900",
99
213
  "shadow-sm hover:shadow-md",
100
214
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
101
215
  "transition-all duration-200",
@@ -145,7 +259,10 @@ const buttonVariants = cva(
145
259
  // üretiyor, `--primary-foreground` ise koyu — ikisi birbirini yiyordu.
146
260
  // #354'ün sidebar'da kanıtlanmış çözümü: subtle token ÇİFTİ (zemin+metin
147
261
  // birlikte tanımlı, koyu temada koyu zemin + açık metin).
148
- "dark:bg-primary-subtle dark:text-primary-subtle-foreground",
262
+ // #715 Dal 1: bu çift ARTIK yukarıdaki öneksiz satırdan geliyor —
263
+ // `dark:bg-primary-subtle dark:text-primary-subtle-foreground` aynı token'ın
264
+ // tekrarıydı, kaldırıldı (#686'nın çözümü aynen KORUNUYOR, yalnız tek
265
+ // yerden yazılıyor). Hover/active dalları FARKLI alfa taşıdığı için KALDI.
149
266
  "dark:hover:bg-primary/30 dark:active:bg-primary/40",
150
267
  "transition-colors duration-200",
151
268
  ],
@@ -295,9 +295,9 @@ const SelectItem = React.forwardRef<
295
295
  /* Color variants */
296
296
  variant === "default" && "focus:bg-accent focus:text-accent-foreground dark:focus:bg-gray-700 dark:focus:text-gray-100",
297
297
  variant === "subtle" && "focus:bg-gray-100 dark:focus:bg-gray-800 focus:text-foreground dark:focus:text-gray-200",
298
- variant === "destructive" && "text-error dark:text-red-400 focus:bg-error/10 dark:focus:bg-red-900/20 focus:text-error dark:focus:text-red-300",
299
- variant === "success" && "text-success focus:bg-success/10 focus:text-success",
300
- variant === "warning" && "text-warning-700 dark:text-warning-500 focus:bg-warning/10 focus:text-warning-700 dark:focus:text-warning-500",
298
+ variant === "destructive" && "text-destructive-subtle-foreground focus:bg-destructive-subtle focus:text-destructive-subtle-foreground",
299
+ variant === "success" && "text-success-subtle-foreground focus:bg-success-subtle focus:text-success-subtle-foreground",
300
+ variant === "warning" && "text-warning-subtle-foreground focus:bg-warning-subtle focus:text-warning-subtle-foreground",
301
301
 
302
302
  className
303
303
  )}
@@ -136,14 +136,27 @@
136
136
  oradaki koyu foreground DOĞRU ve değiştirilmedi.) */
137
137
  --primary-foreground: 210 40% 98%;
138
138
  --secondary: 217.2 32.6% 94%;
139
- /* #284: --secondary rozetinde metin rolünde 4.26 (AA altı) idi -> 49% ile 4.54.
139
+ /* #284: --secondary rozetinde metin rolünde 4.26 (AA altı) idi -> 49%.
140
140
  --primary 51%te KALIR: o token zemin rolünde ve beyaz metinle 4.70 taşıyor.
141
- Aynı mavinin rolüne göre ayrışması --success (#280) ile aynı desendir. */
142
- --secondary-foreground: 217.2 91.2% 49%;
141
+ Aynı mavinin rolüne göre ayrışması --success (#280) ile aynı desendir.
142
+
143
+ %49 YETMEDİ — 44%'e çekildi. #284'ün hesabı 4.54 diyordu, ama gerçek
144
+ tarayıcı render'ı axe-core ile 4.45 ölçüldü (eşik 4.5): teorik HSL→RGB
145
+ hesabı ile tarayıcının ürettiği renk sınırda ayrışıyor (hesap rgb(11,98,239),
146
+ render #0e64ef). DERS: 4.5–4.6 aralığındaki "geçti" sonuçlarına güvenme,
147
+ gerçek render'da ölç. 44% teorik 5.37 verir ve ölçümde de geçer. */
148
+ --secondary-foreground: 217.2 91.2% 44%;
143
149
  --muted: 217.2 32.6% 96%;
144
150
  /* #284: --muted zemininde 4.31 (AA altı) idi -> 45% ile 4.56.
145
- --background üzerinde zaten 4.76 geçiyordu; kırılan yüzey bg-muted (kbd). */
146
- --muted-foreground: 215.4 16.3% 45%;
151
+ --background üzerinde zaten 4.76 geçiyordu; kırılan yüzey bg-muted (kbd).
152
+
153
+ %45 de YETMEDİ — 42%'ye çekildi. #284 yalnız --muted ve --background
154
+ yüzeylerine bakmış; --secondary (#ebeff5) HESABA KATILMAMIŞTI ve orada
155
+ gerçek render'da 4.42 ölçüldü (docs/components/card, "Secondary surface").
156
+ 42% üç yüzeyde de geçer: secondary 4.90 · muted 5.13 · background 5.66.
157
+ Kademe seçiminde teorik hesabın gerçek render'dan ~0.1 yüksek çıktığı
158
+ gözlendi (bkz. --secondary-foreground notu), o yüzden sınıra yaslanılmadı. */
159
+ --muted-foreground: 215.4 16.3% 42%;
147
160
  --accent: 217.2 32.6% 94%;
148
161
  /* #647: --accent-foreground 51% idi -> 4.26 (AA 4.5 altı). Zemin (--accent, 94%)
149
162
  --secondary ile BİREBİR aynı token değeri; #284'ün --secondary-foreground'da
@@ -262,7 +275,35 @@
262
275
  --bg-vivid-2: 328.6 85.5% 70.2%;
263
276
  --bg-vivid-3: 213.1 93.9% 67.8%;
264
277
  --bg-vivid-4: 158.1 64.4% 51.6%;
265
-
278
+
279
+ /* Yildiz alani — starfield (#720).
280
+ `--bg-space` YALNIZ dekoratif degildir: `children` metni bu zeminin ustune
281
+ dusuyor, yani kontrast hesabina GIRER. `--bg-star` ile ciftin ayrimi #481'de
282
+ olculmustu (beyaz-ustune-beyaz 1.045:1) — degerler o cozumun aynisidir. */
283
+ --bg-star: 215 27.9% 16.9%;
284
+ --bg-space: 210 20% 98%;
285
+
286
+ /* 3'lu nebula paleti — starfield (#720).
287
+ Saf DEKORATIF bulut katmani (uzerine metin dusmez) → AA'ya tabi degil.
288
+ ALFA DEGERIN PARCASIDIR: eski hex'ler `#db278610` gibi 8 haneliydi (alfa
289
+ 0x10/255 = 0.0627). Alfa dusurulurse katman 16x koyulasir; bu yuzden token
290
+ `H S% L% / A` biciminde tutulur — canvas `addColorStop` bu sozdizimini
291
+ kabul ediyor (Chrome 152'de olculdu). */
292
+ --bg-nebula-1: 328.3 71.4% 50.6% / 0.0627;
293
+ --bg-nebula-2: 188.7 94.5% 42.7% / 0.0627;
294
+ --bg-nebula-3: 45.4 93.4% 47.5% / 0.0627;
295
+
296
+ /* 5'li pastel mesh paleti — mesh-gradient (#720).
297
+ AYRI BIR SET, `--bg-effect-*` DEGIL: mesh'in kimligi cok acik pastellerdir
298
+ (L %89.8-94.7); EFFECT paleti (L %46.5-78) uzerine tasinsaydi component'in
299
+ gorunumu degisirdi. Token'a tasima gorunumu DEGISTIRMEZ kurali (bu blogun
300
+ bas yorumu) yeni bir grup gerektirdi. */
301
+ --bg-mesh-1: 325.7 77.8% 94.7%;
302
+ --bg-mesh-2: 268.6 100% 91.8%;
303
+ --bg-mesh-3: 250.5 95.2% 91.8%;
304
+ --bg-mesh-4: 226.5 100% 93.9%;
305
+ --bg-mesh-5: 325.9 84.6% 89.8%;
306
+
266
307
  /* === TYPOGRAPHY === */
267
308
 
268
309
  /* Font Families */
@@ -630,6 +671,22 @@
630
671
  --bg-vivid-2: 330.4 81.2% 60.4%;
631
672
  --bg-vivid-3: 217.2 91.2% 59.8%;
632
673
  --bg-vivid-4: 160.1 84.1% 39.4%;
674
+
675
+ /* starfield (#720) — koyu temada beyaz yildiz / siyah uzay */
676
+ --bg-star: 0 0% 100%;
677
+ --bg-space: 0 0% 0%;
678
+
679
+ /* starfield nebula (#720) — koyu temada neon, alfa 0x20/255 = 0.1255 */
680
+ --bg-nebula-1: 300 100% 50% / 0.1255;
681
+ --bg-nebula-2: 180 100% 50% / 0.1255;
682
+ --bg-nebula-3: 60 100% 50% / 0.1255;
683
+
684
+ /* mesh-gradient (#720) — koyu temada derin mor set */
685
+ --bg-mesh-1: 263.4 69.3% 42.2%;
686
+ --bg-mesh-2: 262.1 83.3% 57.8%;
687
+ --bg-mesh-3: 238.7 83.5% 66.7%;
688
+ --bg-mesh-4: 258.3 89.5% 66.3%;
689
+ --bg-mesh-5: 263.5 67.4% 34.9%;
633
690
  }
634
691
 
635
692
  /* === KEYFRAMES === */