@moontra/moonui 6.16.0 → 6.17.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.
package/package.json
CHANGED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #354 — `--primary-subtle` token çifti ve kontrast değişmezi.
|
|
3
|
+
*
|
|
4
|
+
* BULGU: pro Sidebar'ın aktif öğesi `bg-primary/10 text-primary` kullanıyordu.
|
|
5
|
+
* Light temada bu bileşim **4.28:1** veriyor (WCAG AA metin için 4.5 ister) ve
|
|
6
|
+
* docs kabuğu bu Sidebar'ı kullandığı için ihlal HER sayfada basılıyordu.
|
|
7
|
+
* Dark tarafta `--primary` neredeyse beyaz olduğu için sorun yoktu (15.73:1).
|
|
8
|
+
*
|
|
9
|
+
* KÖK NEDEN: #313/#314/#323'te kurulan `--*-subtle` ailesi (yumuşak tint + koyu
|
|
10
|
+
* metin) `destructive/success/warning/info` için vardı ama **primary EKSİKTİ** —
|
|
11
|
+
* bu yüzden Sidebar ham `primary/10` kompozisyonuna düşmüştü.
|
|
12
|
+
*
|
|
13
|
+
* Bu test hem token'ların VARLIĞINI hem de KONTRAST oranlarını sabitler: değer
|
|
14
|
+
* değiştirilirse ve oran 4.5'in altına düşerse kırmızı olur.
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from 'fs'
|
|
17
|
+
import * as path from 'path'
|
|
18
|
+
|
|
19
|
+
const KOK = path.resolve(__dirname, '../../..')
|
|
20
|
+
const tokens = fs.readFileSync(path.join(KOK, 'styles/tokens.css'), 'utf8')
|
|
21
|
+
const preset = fs.readFileSync(path.resolve(KOK, '../tailwind-preset.js'), 'utf8')
|
|
22
|
+
|
|
23
|
+
/** "H S% L%" biçimindeki token değerini oku (light = :root, dark = .dark bloğu) */
|
|
24
|
+
function tokenOku(ad: string, blok: 'light' | 'dark'): string | null {
|
|
25
|
+
const kaynak =
|
|
26
|
+
blok === 'dark'
|
|
27
|
+
? tokens.slice(tokens.indexOf('.dark'))
|
|
28
|
+
: tokens.slice(0, tokens.indexOf('.dark'))
|
|
29
|
+
return kaynak.match(new RegExp(`--${ad}:\\s*([^;]+);`))?.[1].trim() ?? null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function hslToRgb(h: number, s: number, l: number): [number, number, number] {
|
|
33
|
+
h /= 360
|
|
34
|
+
s /= 100
|
|
35
|
+
l /= 100
|
|
36
|
+
const f = (n: number) => {
|
|
37
|
+
const k = (n + h * 12) % 12
|
|
38
|
+
const a = s * Math.min(l, 1 - l)
|
|
39
|
+
return l - a * Math.max(-1, Math.min(k - 3, Math.min(9 - k, 1)))
|
|
40
|
+
}
|
|
41
|
+
return [Math.round(255 * f(0)), Math.round(255 * f(8)), Math.round(255 * f(4))]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function bagilParlaklik([r, g, b]: [number, number, number]): number {
|
|
45
|
+
const c = (v: number) => {
|
|
46
|
+
const x = v / 255
|
|
47
|
+
return x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4)
|
|
48
|
+
}
|
|
49
|
+
return 0.2126 * c(r) + 0.7152 * c(g) + 0.0722 * c(b)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function kontrast(a: string, b: string): number {
|
|
53
|
+
const ayrıştır = (v: string): [number, number, number] => {
|
|
54
|
+
const [h, s, l] = v.split(/\s+/).map((x) => parseFloat(x))
|
|
55
|
+
return hslToRgb(h, s, l)
|
|
56
|
+
}
|
|
57
|
+
const la = bagilParlaklik(ayrıştır(a))
|
|
58
|
+
const lb = bagilParlaklik(ayrıştır(b))
|
|
59
|
+
const [hi, lo] = la > lb ? [la, lb] : [lb, la]
|
|
60
|
+
return (hi + 0.05) / (lo + 0.05)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
describe('#354 --primary-subtle token çifti', () => {
|
|
64
|
+
it('hesaplayıcı DOĞRU — bilinen bir çift beklenen oranı veriyor', () => {
|
|
65
|
+
// Sessiz-yeşil koruması: kontrast fonksiyonu bozulursa aşağıdaki testler
|
|
66
|
+
// anlamsız sayılarla yeşil geçebilir. `--info-subtle` çifti tokens.css'te
|
|
67
|
+
// 9.52:1 diye belgelenmiş; hesaplayıcı onu yeniden üretmeli.
|
|
68
|
+
const oran = kontrast(tokenOku('info-subtle', 'light')!, tokenOku('info-subtle-foreground', 'light')!)
|
|
69
|
+
expect(oran).toBeGreaterThan(9.3)
|
|
70
|
+
expect(oran).toBeLessThan(9.7)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it.each(['light', 'dark'] as const)('%s temada token çifti TANIMLI', (blok) => {
|
|
74
|
+
expect(tokenOku('primary-subtle', blok)).toBeTruthy()
|
|
75
|
+
expect(tokenOku('primary-subtle-foreground', blok)).toBeTruthy()
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it.each(['light', 'dark'] as const)('%s temada kontrast WCAG AA (4.5:1) geçiyor', (blok) => {
|
|
79
|
+
const oran = kontrast(tokenOku('primary-subtle', blok)!, tokenOku('primary-subtle-foreground', blok)!)
|
|
80
|
+
expect(oran).toBeGreaterThanOrEqual(4.5)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('light zemin, bugünkü `primary/10` görünümünü KORUYOR', () => {
|
|
84
|
+
// Zemin değişirse aktif öğenin görünümü değişir; bu bilinçli bir karar olmalı.
|
|
85
|
+
// #e7f0fe = beyaz üstüne %10 primary.
|
|
86
|
+
expect(tokenOku('primary-subtle', 'light')).toBe('217 92% 95%')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('preset sınıfları ÜRETİYOR (token tanımlamak yetmez)', () => {
|
|
90
|
+
// Bu oturumda `theme.extend.borderColor`ın nested renkleri GÖLGELEDİĞİ ve
|
|
91
|
+
// sınıfın sessizce hiç CSS üretmediği görülmüştü (#313/#314/#323).
|
|
92
|
+
expect(preset).toContain('hsl(var(--primary-subtle) / <alpha-value>)')
|
|
93
|
+
expect(preset).toContain('hsl(var(--primary-subtle-foreground) / <alpha-value>)')
|
|
94
|
+
})
|
|
95
|
+
})
|
package/src/styles/tokens.css
CHANGED
|
@@ -199,6 +199,17 @@
|
|
|
199
199
|
--info-subtle: 214 100% 97%;
|
|
200
200
|
--info-subtle-foreground: 224 64% 33%; /* 9.52:1 */
|
|
201
201
|
|
|
202
|
+
/* #354: `primary` ailesi bu listede EKSİKTİ. Pro Sidebar'ın aktif öğesi
|
|
203
|
+
"primary metin + primary/10 zemin" deseni kullanıyordu; light temada bu
|
|
204
|
+
4.28:1 veriyor (gereken 4.5) — yani docs kabuğu her sayfada bir WCAG AA
|
|
205
|
+
ihlali basıyordu. Dark tarafta `--primary` neredeyse beyaz olduğu için
|
|
206
|
+
sorun yoktu (15.73:1, ölçüldü).
|
|
207
|
+
|
|
208
|
+
Zemin, bugünkü `primary/10` bileşiminin BİREBİR HSL karşılığı (beyaz üstüne
|
|
209
|
+
%10 primary = #e7f0fe) → görünüm DEĞİŞMEZ, yalnız metin koyulaşır. */
|
|
210
|
+
--primary-subtle: 217 92% 95%;
|
|
211
|
+
--primary-subtle-foreground: 217.2 91.2% 40%; /* 6.21:1 */
|
|
212
|
+
|
|
202
213
|
--primary-50: 240 249 255;
|
|
203
214
|
|
|
204
215
|
/* Tailwind preset'i rounded-lg/md/sm'yi var(--radius)'a bagliyor.
|
|
@@ -474,6 +485,12 @@
|
|
|
474
485
|
--warning-subtle-foreground: 34 100% 92%; /* 14.43:1 */
|
|
475
486
|
--info-subtle: 217 80% 13%;
|
|
476
487
|
--info-subtle-foreground: 214 95% 93%; /* 14.11:1 */
|
|
488
|
+
|
|
489
|
+
/* #354: koyu temada mevcut görünüm KORUNUYOR — bugünkü bileşimin birebir
|
|
490
|
+
karşılığı (near-white/10 near-black üstüne). Dark zaten geçiyordu; buradaki
|
|
491
|
+
amaç token ailesini tamamlamak, dark'ın rengini değiştirmek değil. */
|
|
492
|
+
--primary-subtle: 225 18% 13%;
|
|
493
|
+
--primary-subtle-foreground: 210 40% 98%; /* 15.73:1 */
|
|
477
494
|
--border: 216 34% 17%;
|
|
478
495
|
/* #284: koyu temada form kontrolü kenarlığı 1.36 idi -> 40% ile ~3.1.
|
|
479
496
|
Not: --input switch.tsx:27'de kapalı track ZEMİNİ olarak da kullanılıyor;
|
package/tailwind-preset.js
CHANGED
|
@@ -76,6 +76,13 @@ module.exports = {
|
|
|
76
76
|
primary: {
|
|
77
77
|
DEFAULT: "hsl(var(--primary) / <alpha-value>)",
|
|
78
78
|
foreground: "hsl(var(--primary-foreground) / <alpha-value>)",
|
|
79
|
+
// #354: `primary` yumuşak-ton yüzeyi — `bg-primary-subtle` +
|
|
80
|
+
// `text-primary-subtle-foreground`. Aile destructive/success/warning/
|
|
81
|
+
// info için vardı, primary EKSİKTİ ve pro Sidebar'ın aktif öğesi bu
|
|
82
|
+
// yüzden ham `primary/10` kompozisyonuna düşüp light temada 4.28:1
|
|
83
|
+
// veriyordu.
|
|
84
|
+
subtle: "hsl(var(--primary-subtle) / <alpha-value>)",
|
|
85
|
+
"subtle-foreground": "hsl(var(--primary-subtle-foreground) / <alpha-value>)",
|
|
79
86
|
},
|
|
80
87
|
secondary: {
|
|
81
88
|
DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
|