@ahmetilhn/hanui 1.1.1 → 2.0.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/README.md +131 -15
- package/build/index.cjs +18 -14
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +1600 -160
- package/build/index.js +3866 -1964
- package/build/index.js.map +1 -1
- package/build/styles.css +1 -1
- package/package.json +21 -6
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @ahmetilhn/hanui
|
|
2
2
|
|
|
3
3
|
Erişilebilir, tema güdümlü React bileşen kütüphanesi. SCSS Modules üzerine
|
|
4
|
-
kurulu; çalışma
|
|
4
|
+
kurulu; tek çalışma zamanı bağımlılığı ikon seti (`react-bootstrap-icons`).
|
|
5
5
|
|
|
6
6
|
- **Tek CSS dosyası, iki tema.** Açık ve koyu hazır gelir, her token dışarıdan
|
|
7
7
|
ezilebilir.
|
|
@@ -14,10 +14,11 @@ kurulu; çalışma zamanında **hiçbir UI bağımlılığı yok**.
|
|
|
14
14
|
duyurusu tarayıcıdan gelir.
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npm install @ahmetilhn/hanui @ahmetilhn/handy-utils
|
|
17
|
+
npm install @ahmetilhn/hanui @ahmetilhn/handy-utils react-bootstrap-icons
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
`react`, `react-dom
|
|
20
|
+
`react`, `react-dom`, `@ahmetilhn/handy-utils` ve `react-bootstrap-icons`
|
|
21
|
+
**peer** bağımlılıktır.
|
|
21
22
|
|
|
22
23
|
---
|
|
23
24
|
|
|
@@ -82,6 +83,9 @@ const THEME = {
|
|
|
82
83
|
light: { blue: '#0d6efd', 'blue-text': '#0a58ca' },
|
|
83
84
|
dark: { blue: '#6ea8fe' },
|
|
84
85
|
fonts: { heading: 'Archivo, sans-serif', body: 'Inter, sans-serif' },
|
|
86
|
+
// Ölçüler tema başına DEĞİL: bir markanın yuvarlaklığı açık temada
|
|
87
|
+
// 12 px, koyu temada 8 px olmaz.
|
|
88
|
+
metrics: { 'radius-md': '2px', 'radius-lg': '4px', 'duration-normal': '120ms' },
|
|
85
89
|
};
|
|
86
90
|
|
|
87
91
|
<HanuiProvider theme={THEME} linkComponent={NextLink}>
|
|
@@ -96,16 +100,65 @@ React'in dışından (bir `<head>` betiğinden, Storybook'tan) aynı işi
|
|
|
96
100
|
(`next/font`, `@font-face`, CDN) siz yaparsınız. Bir UI paketinin ağdan font
|
|
97
101
|
çekmesi, sizin ölçemediğiniz bir istek demek.
|
|
98
102
|
|
|
103
|
+
#### Ezilebilen ölçüler
|
|
104
|
+
|
|
105
|
+
`radius-*` · `space-0…9` · `font-size-2xs…4xl` · `leading-*` · `icon-xs…xl` ·
|
|
106
|
+
`duration-*` · `ease-*` — tam liste `METRIC_TOKENS` içinde ve dışa veriliyor.
|
|
107
|
+
|
|
108
|
+
Ezilemeyenler ve nedenleri: **kırılma noktaları** (`@media (max-width: var(--x))`
|
|
109
|
+
geçersiz CSS), **katman (`z-*`)** (yığılma sırası, marka kararı değil) ve
|
|
110
|
+
**dokunma hedefi** (44 px; yoğun kipte de küçülmemeli — WCAG 2.5.8).
|
|
111
|
+
|
|
112
|
+
### Bilgi yoğunluğu
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<html data-hanui-density="compact"></html>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Satır yüksekliği, dolgu ve punto bir kademe iner. Vitrin bir ekranda 8 satır,
|
|
119
|
+
operasyon paneli 80 satır gösteriyor; ikisini tek ölçekle karşılamanın yolu yok.
|
|
120
|
+
Kararı `initHanui({ density: 'compact' })` da verebilir.
|
|
121
|
+
|
|
122
|
+
**Yarıçap değişmez** (yuvarlaklık yoğunluğun değil markanın işi) ve **dokunma
|
|
123
|
+
hedefi küçülmez**: görsel kutu daralır, `tap-target` örtüsü 44 px kalır.
|
|
124
|
+
|
|
125
|
+
### CSS'imizi ezmek — `@layer hanui`
|
|
126
|
+
|
|
127
|
+
Kütüphane CSS'inin tamamı `@layer hanui` içinde. Katmanlı bir kural, katmansız
|
|
128
|
+
bir kurala **her zaman yenilir** — özgüllüğe ve sıraya bakılmaksızın:
|
|
129
|
+
|
|
130
|
+
```css
|
|
131
|
+
/* Bu kadarı yeter; `!important` ya da `.app .card .card` gerekmez. */
|
|
132
|
+
.my-card {
|
|
133
|
+
padding: 0;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Katman olmadan sizin sınıfınız ile bizimki aynı özgüllükte (0,1,0) yarışıyordu
|
|
138
|
+
ve kazananı **kaynak sırası** belirliyordu; o sıra da bundler'ınızın elinde —
|
|
139
|
+
geliştirme ile üretim aynı olmak zorunda değil.
|
|
140
|
+
|
|
99
141
|
### Temayı değiştirmek
|
|
100
142
|
|
|
101
143
|
```tsx
|
|
102
|
-
const { scheme, toggle, isReady } = useHanuiTheme();
|
|
144
|
+
const { scheme, preference, setScheme, toggle, isReady } = useHanuiTheme();
|
|
103
145
|
|
|
104
146
|
useEffect(() => {
|
|
105
|
-
if (isReady) localStorage.setItem('theme',
|
|
106
|
-
}, [
|
|
147
|
+
if (isReady) localStorage.setItem('theme', preference);
|
|
148
|
+
}, [preference, isReady]);
|
|
107
149
|
```
|
|
108
150
|
|
|
151
|
+
| Alan | |
|
|
152
|
+
| --- | --- |
|
|
153
|
+
| `scheme` | **ÇÖZÜLMÜŞ** tema — ekranda çizili olan (`light` \| `dark`) |
|
|
154
|
+
| `preference` | kullanıcının **SEÇİMİ** (`light` \| `dark` \| `system`) |
|
|
155
|
+
| `setScheme` | `'system'` verildiğinde açık seçim SİLİNİR |
|
|
156
|
+
|
|
157
|
+
Üç durumlu bir anahtar (Açık / Koyu / Sistem) `preference` okur; `scheme`
|
|
158
|
+
okusaydı "Sistem" seçiliyken düğme "Koyu"yu işaretli gösterirdi. `system` bir
|
|
159
|
+
değer değil **değerin yokluğu**: öznitelik silinir ve
|
|
160
|
+
`:not([data-hanui-theme])` sorgusu devreye girer.
|
|
161
|
+
|
|
109
162
|
Kalıcılık **kancanın işi değil**: seçimi `localStorage`a mı, sunucuda
|
|
110
163
|
okunabilsin diye bir çereze mi, kullanıcı profiline mi yazacağınız sizin
|
|
111
164
|
kararınız. Kütüphane `localStorage`a yazsaydı sunucu tarafı onu okuyamayacağı
|
|
@@ -120,13 +173,27 @@ uyuşmazlık üretiyordu.
|
|
|
120
173
|
|
|
121
174
|
```
|
|
122
175
|
theme/palette.ts → ham hex (TEK hex kaynağı)
|
|
123
|
-
theme/tokens.ts → anlamsal token →
|
|
176
|
+
theme/tokens.ts → anlamsal token → değer
|
|
177
|
+
LIGHT_THEME / DARK_THEME (renk, temaya bağlı)
|
|
178
|
+
METRIC_TOKENS (ölçü, temadan bağımsız)
|
|
179
|
+
COMPACT_DENSITY (yoğun kipin ezdikleri)
|
|
124
180
|
styles/*.generated → yukarıdakinden ÜRETİLİR (npm run tokens)
|
|
125
181
|
```
|
|
126
182
|
|
|
127
183
|
SCSS ve TypeScript tarafı elle tutulsaydı bir tarafta var olup diğerinde
|
|
128
184
|
olmayan bir token çıkardı — ve ayrışma sessiz: bileşen `var(--hanui-yok)`
|
|
129
|
-
okuyup rengini kaybediyor, derleme yeşil dönüyor.
|
|
185
|
+
okuyup rengini kaybediyor, derleme yeşil dönüyor. Nöbetçi
|
|
186
|
+
`theme/__tests__/tokens.contract.test.ts`.
|
|
187
|
+
|
|
188
|
+
**Ölçüler CSS değişkeni olduğu için SCSS aritmetiği çalışmaz.**
|
|
189
|
+
`$space-8 - $space-2` derlenmez — ve Sass hata da vermez: ifadeyi olduğu gibi
|
|
190
|
+
geçirip tarayıcıya `var(--a)-var(--b)` yazar, tarayıcı bütün bildirimi atar.
|
|
191
|
+
Hesap `calc()` içinde yazılır, negatif değer dahil:
|
|
192
|
+
|
|
193
|
+
```scss
|
|
194
|
+
padding-inline-start: calc(#{$space-8} - #{$space-2});
|
|
195
|
+
margin-inline: calc(-1 * #{$space-2});
|
|
196
|
+
```
|
|
130
197
|
|
|
131
198
|
---
|
|
132
199
|
|
|
@@ -292,6 +359,23 @@ işaretle de bildirilir: `Alert`in her tonunun kendi ikonu var, `Field`in zorunl
|
|
|
292
359
|
yıldızının yanında okunabilir bir metin, `RatingInput`in yıldızlarının altında
|
|
293
360
|
açıklama duruyor.
|
|
294
361
|
|
|
362
|
+
**İkonlar `react-bootstrap-icons`ten, DOLU sürümüyle.** Kabuk simgeleri
|
|
363
|
+
(`Modal`ın kapatma çarpısı, `Select`in oku, `Alert`in durum ikonu) bir zamanlar
|
|
364
|
+
kütüphanenin içinde elle çizilmiş yollardı; bir ikon setinin bakımını üstlenmek
|
|
365
|
+
hiçbir şey kazandırmıyordu — tüketici uygulamalar kendi içerik ikonlarını zaten
|
|
366
|
+
aynı setten alıyor ve paket `external`, yani ikinci bir kopya inmiyor. Dolu
|
|
367
|
+
(`*Fill`) sürüm kural: ince konturlu bir simge, tint zeminde ve küçük ölçüde
|
|
368
|
+
tek piksele inip kayboluyor. Fill eşi olmayan simgelerde (`X`, `Check`, `Plus`,
|
|
369
|
+
`Dash`) kalın `*Lg` sürümü kullanılır, chevron yerine dolu `Caret*Fill` —
|
|
370
|
+
böylece bütün kabuk aynı çizgi ağırlığında kalır. İçerik ikonları (kategori
|
|
371
|
+
simgesi, marka logosu) her zaman `ReactNode` prop'u olarak DIŞARIDAN gelir.
|
|
372
|
+
|
|
373
|
+
**İkon ölçüsü metin puntosundan ayrıdır.** İkonlar `1em` ile ölçeklenir ama
|
|
374
|
+
taşındıkları kutunun `font-size` değeri metin için seçilmiştir ve simge için
|
|
375
|
+
küçük kalır: 40 px'lik bir `IconButton`ın ortasındaki 16 px'lik simge tıklanabilir
|
|
376
|
+
görünmüyordu. Ölçek ayrı bir token dizisidir (`$icon-xs` … `$icon-xl`,
|
|
377
|
+
14/16/18/20/24 px) ve ikon taşıyan her kutu puntosunu oradan alır.
|
|
378
|
+
|
|
295
379
|
**"Yüklenemedi" ile "boş" ayrı durumlardır.** `DataTable`da `error` verilmişken
|
|
296
380
|
boş mesajı **asla** çizilmez; bir ağ hatasını "kayıt kalmamış" diye okutmak
|
|
297
381
|
kullanıcıya yanlış karar verdirir.
|
|
@@ -332,15 +416,47 @@ import NextLink from 'next/link';
|
|
|
332
416
|
```bash
|
|
333
417
|
nvm use # .nvmrc → 24
|
|
334
418
|
npm install
|
|
335
|
-
npm run verify # tokens → typecheck → lint →
|
|
419
|
+
npm run verify # tokens → kontrast → typecheck → lint → kapsam → build → boyut
|
|
420
|
+
npm run playground # bileşen galerisi → http://localhost:5273
|
|
336
421
|
```
|
|
337
422
|
|
|
338
|
-
| Komut
|
|
339
|
-
|
|
|
340
|
-
| `npm run tokens`
|
|
341
|
-
| `npm run build`
|
|
342
|
-
| `npm run test`
|
|
343
|
-
| `npm run
|
|
423
|
+
| Komut | |
|
|
424
|
+
| ------------------------- | --------------------------------------------------------------- |
|
|
425
|
+
| `npm run tokens` | SCSS token dosyalarını `theme/tokens.ts`ten üretir |
|
|
426
|
+
| `npm run build` | `build/` (ESM + CJS + `.d.ts` + `styles.css`) |
|
|
427
|
+
| `npm run test` | Jest + Testing Library + `jest-axe` |
|
|
428
|
+
| `npm run test:coverage` | kapsam kapısı — `helpers`/`hooks`/`theme` satır ≥ %80 |
|
|
429
|
+
| `npm run test:visual` | görsel regresyon (Playwright); `verify` İÇİNDE DEĞİL, aşağı bkz. |
|
|
430
|
+
| `npm run check:contrast` | WCAG kontrast ölçümü, iki temada 118 çift |
|
|
431
|
+
| `npm run size` | paket boyutu bütçesi (`size-limit`) |
|
|
432
|
+
| `npm run playground` | bileşen galerisi (tema · RTL · yoğunluk anahtarı) |
|
|
433
|
+
| `npm run lint` | ESLint (TS) + stylelint (SCSS) |
|
|
434
|
+
| `npm run typecheck` | `tsc --noEmit` |
|
|
435
|
+
|
|
436
|
+
### Nöbetçiler ne koruyor
|
|
437
|
+
|
|
438
|
+
- **Eksen taraması** — dışa verilen her bileşen, her anlamlı durumunda
|
|
439
|
+
(`components/__tests__/a11y.test.tsx`). Deftere girmeyen yeni bir bileşen
|
|
440
|
+
testi kırar.
|
|
441
|
+
- **Klavye sözleşmesi** — tuş matrisleri her bileşenin JSDoc'unda, nöbetçisi
|
|
442
|
+
`components/__tests__/keyboard.test.tsx`.
|
|
443
|
+
- **Kontrast** — metin ≥ 4,5:1, ikon ve odak halkası ≥ 3:1. Eşiği tutmayan bir
|
|
444
|
+
token derlemeyi kırar; düzeltme daima `palette.ts` düzeyinde yapılır.
|
|
445
|
+
- **Token sözleşmesi** — açık/koyu anahtar kümeleri ve üretilmiş SCSS kaynakla
|
|
446
|
+
eşleşir. Ayrışma bugüne kadar sessizdi.
|
|
447
|
+
- **Paket boyutu** — tek bileşen import eden bir uygulamanın ne indirdiği
|
|
448
|
+
ölçülür (`Badge` 2,7 kB, `Button` 3,4 kB, tüm paket 22,2 kB gzip).
|
|
449
|
+
- **Odak halkası ve hareket ölçeği** — stylelint, `focus-ring` mixin'i dışında
|
|
450
|
+
`outline` ve bileşen SCSS'inde ham süre değeri (`0.7s`) yazılmasını
|
|
451
|
+
engelliyor. Üç bileşen kendi halkasını, iki bileşen kendi süresini
|
|
452
|
+
yazıyordu.
|
|
453
|
+
|
|
454
|
+
**Görsel regresyon `verify` içinde değil**: ekran görüntüsü platforma bağlı
|
|
455
|
+
(yazı tipi tarama, alt piksel yumuşatma) ve macOS'te üretilmiş bir referans
|
|
456
|
+
ubuntu üzerinde koşan CI'da her dosyada kırmızı döner. Referanslar platform
|
|
457
|
+
başına saklanıyor (`e2e/__screenshots__/{platform}/`); CI'da açılacağı gün doğru
|
|
458
|
+
yol, Playwright'ın resmi konteynerinde koşup `linux` referanslarını orada
|
|
459
|
+
üretmek.
|
|
344
460
|
|
|
345
461
|
`master`a her push `npm publish` çalıştırır — **`package.json` sürümünü
|
|
346
462
|
yükseltmeyi unutmayın**, aynı sürüm ikinci kez yayımlanamaz ve iş akışı kırmızı
|