@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 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 zamanında **hiçbir UI bağımlılığı yok**.
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` ve `@ahmetilhn/handy-utils` **peer** bağımlılıktır.
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', scheme);
106
- }, [scheme, isReady]);
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 → hex (açık + koyu)
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 → test → build
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` | SCSS token dosyalarını `theme/tokens.ts`ten üretir |
341
- | `npm run build` | `build/` (ESM + CJS + `.d.ts` + `styles.css`) |
342
- | `npm run test` | Jest + Testing Library |
343
- | `npm run typecheck` | `tsc --noEmit` |
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ı