tamga-ui 0.1.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/LICENSE +21 -0
- package/README.md +172 -0
- package/dist/blocks/count-row.d.ts +29 -0
- package/dist/blocks/count-row.d.ts.map +1 -0
- package/dist/blocks/count-row.js +19 -0
- package/dist/blocks/filter-bar.d.ts +123 -0
- package/dist/blocks/filter-bar.d.ts.map +1 -0
- package/dist/blocks/filter-bar.js +74 -0
- package/dist/blocks/index.d.ts +24 -0
- package/dist/blocks/index.d.ts.map +1 -0
- package/dist/blocks/index.js +23 -0
- package/dist/blocks/save-bar.d.ts +31 -0
- package/dist/blocks/save-bar.d.ts.map +1 -0
- package/dist/blocks/save-bar.js +21 -0
- package/dist/components/a11y.d.ts +21 -0
- package/dist/components/a11y.d.ts.map +1 -0
- package/dist/components/a11y.js +159 -0
- package/dist/components/advanced-input.d.ts +115 -0
- package/dist/components/advanced-input.d.ts.map +1 -0
- package/dist/components/advanced-input.js +189 -0
- package/dist/components/avatar.d.ts +63 -0
- package/dist/components/avatar.d.ts.map +1 -0
- package/dist/components/avatar.js +54 -0
- package/dist/components/badge.d.ts +37 -0
- package/dist/components/badge.d.ts.map +1 -0
- package/dist/components/badge.js +29 -0
- package/dist/components/bar.d.ts +32 -0
- package/dist/components/bar.d.ts.map +1 -0
- package/dist/components/bar.js +33 -0
- package/dist/components/button.d.ts +45 -0
- package/dist/components/button.d.ts.map +1 -0
- package/dist/components/button.js +56 -0
- package/dist/components/chart.d.ts +43 -0
- package/dist/components/chart.d.ts.map +1 -0
- package/dist/components/chart.js +41 -0
- package/dist/components/checkbox.d.ts +13 -0
- package/dist/components/checkbox.d.ts.map +1 -0
- package/dist/components/checkbox.js +16 -0
- package/dist/components/chrome.d.ts +87 -0
- package/dist/components/chrome.d.ts.map +1 -0
- package/dist/components/chrome.js +107 -0
- package/dist/components/combobox.d.ts +31 -0
- package/dist/components/combobox.d.ts.map +1 -0
- package/dist/components/combobox.js +62 -0
- package/dist/components/control-base.d.ts +7 -0
- package/dist/components/control-base.d.ts.map +1 -0
- package/dist/components/control-base.js +6 -0
- package/dist/components/data-table.d.ts +68 -0
- package/dist/components/data-table.d.ts.map +1 -0
- package/dist/components/data-table.js +50 -0
- package/dist/components/date-picker.d.ts +52 -0
- package/dist/components/date-picker.d.ts.map +1 -0
- package/dist/components/date-picker.js +73 -0
- package/dist/components/disclosure.d.ts +31 -0
- package/dist/components/disclosure.d.ts.map +1 -0
- package/dist/components/disclosure.js +32 -0
- package/dist/components/display.d.ts +188 -0
- package/dist/components/display.d.ts.map +1 -0
- package/dist/components/display.js +190 -0
- package/dist/components/empty-state.d.ts +150 -0
- package/dist/components/empty-state.d.ts.map +1 -0
- package/dist/components/empty-state.js +113 -0
- package/dist/components/error-state.d.ts +38 -0
- package/dist/components/error-state.d.ts.map +1 -0
- package/dist/components/error-state.js +16 -0
- package/dist/components/file-upload.d.ts +34 -0
- package/dist/components/file-upload.d.ts.map +1 -0
- package/dist/components/file-upload.js +34 -0
- package/dist/components/gauge.d.ts +47 -0
- package/dist/components/gauge.d.ts.map +1 -0
- package/dist/components/gauge.js +65 -0
- package/dist/components/icon.d.ts +32 -0
- package/dist/components/icon.d.ts.map +1 -0
- package/dist/components/icon.js +20 -0
- package/dist/components/icons.d.ts +19 -0
- package/dist/components/icons.d.ts.map +1 -0
- package/dist/components/icons.js +39 -0
- package/dist/components/input.d.ts +35 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.js +41 -0
- package/dist/components/layout.d.ts +133 -0
- package/dist/components/layout.d.ts.map +1 -0
- package/dist/components/layout.js +132 -0
- package/dist/components/link.d.ts +22 -0
- package/dist/components/link.d.ts.map +1 -0
- package/dist/components/link.js +2 -0
- package/dist/components/live-scope.d.ts +15 -0
- package/dist/components/live-scope.d.ts.map +1 -0
- package/dist/components/live-scope.js +67 -0
- package/dist/components/log-view.d.ts +34 -0
- package/dist/components/log-view.d.ts.map +1 -0
- package/dist/components/log-view.js +30 -0
- package/dist/components/number-input.d.ts +26 -0
- package/dist/components/number-input.d.ts.map +1 -0
- package/dist/components/number-input.js +57 -0
- package/dist/components/overlay.d.ts +257 -0
- package/dist/components/overlay.d.ts.map +1 -0
- package/dist/components/overlay.js +276 -0
- package/dist/components/pagination.d.ts +38 -0
- package/dist/components/pagination.d.ts.map +1 -0
- package/dist/components/pagination.js +46 -0
- package/dist/components/pie.d.ts +40 -0
- package/dist/components/pie.d.ts.map +1 -0
- package/dist/components/pie.js +74 -0
- package/dist/components/primitives.d.ts +96 -0
- package/dist/components/primitives.d.ts.map +1 -0
- package/dist/components/primitives.js +81 -0
- package/dist/components/progress.d.ts +29 -0
- package/dist/components/progress.d.ts.map +1 -0
- package/dist/components/progress.js +16 -0
- package/dist/components/radio-group.d.ts +17 -0
- package/dist/components/radio-group.d.ts.map +1 -0
- package/dist/components/radio-group.js +23 -0
- package/dist/components/rail-link.d.ts +24 -0
- package/dist/components/rail-link.d.ts.map +1 -0
- package/dist/components/rail-link.js +24 -0
- package/dist/components/rich-text.d.ts +51 -0
- package/dist/components/rich-text.d.ts.map +1 -0
- package/dist/components/rich-text.js +147 -0
- package/dist/components/section.d.ts +15 -0
- package/dist/components/section.d.ts.map +1 -0
- package/dist/components/section.js +8 -0
- package/dist/components/segmented.d.ts +13 -0
- package/dist/components/segmented.d.ts.map +1 -0
- package/dist/components/segmented.js +12 -0
- package/dist/components/skeleton.d.ts +69 -0
- package/dist/components/skeleton.d.ts.map +1 -0
- package/dist/components/skeleton.js +67 -0
- package/dist/components/slider.d.ts +18 -0
- package/dist/components/slider.d.ts.map +1 -0
- package/dist/components/slider.js +15 -0
- package/dist/components/sparkline.d.ts +17 -0
- package/dist/components/sparkline.d.ts.map +1 -0
- package/dist/components/sparkline.js +19 -0
- package/dist/components/spinner.d.ts +15 -0
- package/dist/components/spinner.d.ts.map +1 -0
- package/dist/components/spinner.js +22 -0
- package/dist/components/stacked-bar.d.ts +21 -0
- package/dist/components/stacked-bar.d.ts.map +1 -0
- package/dist/components/stacked-bar.js +41 -0
- package/dist/components/surface.d.ts +76 -0
- package/dist/components/surface.d.ts.map +1 -0
- package/dist/components/surface.js +49 -0
- package/dist/components/switch.d.ts +12 -0
- package/dist/components/switch.d.ts.map +1 -0
- package/dist/components/switch.js +13 -0
- package/dist/components/tabs.d.ts +47 -0
- package/dist/components/tabs.d.ts.map +1 -0
- package/dist/components/tabs.js +12 -0
- package/dist/components/timeline-strip.d.ts +19 -0
- package/dist/components/timeline-strip.d.ts.map +1 -0
- package/dist/components/timeline-strip.js +14 -0
- package/dist/components/tone.d.ts +58 -0
- package/dist/components/tone.d.ts.map +1 -0
- package/dist/components/tone.js +70 -0
- package/dist/components/tree-select.d.ts +26 -0
- package/dist/components/tree-select.d.ts.map +1 -0
- package/dist/components/tree-select.js +59 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -0
- package/dist/kit.css +2248 -0
- package/dist/lib/cn.d.ts +10 -0
- package/dist/lib/cn.d.ts.map +1 -0
- package/dist/lib/cn.js +12 -0
- package/dist/lib/color.d.ts +68 -0
- package/dist/lib/color.d.ts.map +1 -0
- package/dist/lib/color.js +134 -0
- package/dist/lib/palette.d.ts +84 -0
- package/dist/lib/palette.d.ts.map +1 -0
- package/dist/lib/palette.js +193 -0
- package/dist/lib/scroll-lock.d.ts +19 -0
- package/dist/lib/scroll-lock.d.ts.map +1 -0
- package/dist/lib/scroll-lock.js +33 -0
- package/dist/patterns/app-shell.d.ts +36 -0
- package/dist/patterns/app-shell.d.ts.map +1 -0
- package/dist/patterns/app-shell.js +14 -0
- package/dist/patterns/auth-template.d.ts +36 -0
- package/dist/patterns/auth-template.d.ts.map +1 -0
- package/dist/patterns/auth-template.js +40 -0
- package/dist/patterns/detail-template.d.ts +52 -0
- package/dist/patterns/detail-template.d.ts.map +1 -0
- package/dist/patterns/detail-template.js +11 -0
- package/dist/patterns/index.d.ts +18 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +17 -0
- package/dist/patterns/list-template.d.ts +32 -0
- package/dist/patterns/list-template.d.ts.map +1 -0
- package/dist/patterns/list-template.js +8 -0
- package/dist/patterns/overview-template.d.ts +26 -0
- package/dist/patterns/overview-template.d.ts.map +1 -0
- package/dist/patterns/overview-template.js +8 -0
- package/dist/patterns/public-template.d.ts +28 -0
- package/dist/patterns/public-template.d.ts.map +1 -0
- package/dist/patterns/public-template.js +7 -0
- package/dist/patterns/settings-template.d.ts +50 -0
- package/dist/patterns/settings-template.d.ts.map +1 -0
- package/dist/patterns/settings-template.js +33 -0
- package/dist/patterns/shared.d.ts +40 -0
- package/dist/patterns/shared.d.ts.map +1 -0
- package/dist/patterns/shared.js +29 -0
- package/dist/patterns/wizard-template.d.ts +28 -0
- package/dist/patterns/wizard-template.d.ts.map +1 -0
- package/dist/patterns/wizard-template.js +15 -0
- package/dist/styles.css +5 -0
- package/dist/theme.css +812 -0
- package/package.json +87 -0
package/dist/lib/cn.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ClassValue } from "clsx";
|
|
2
|
+
/**
|
|
3
|
+
* Joins class names and lets a caller's utility win over the component's own.
|
|
4
|
+
*
|
|
5
|
+
* Without the merge, `<Button className="px-8">` produces "px-4 px-8" and which
|
|
6
|
+
* one applies depends on their order in the stylesheet, not on which the caller
|
|
7
|
+
* asked for. twMerge drops the loser, so an override is an override.
|
|
8
|
+
*/
|
|
9
|
+
export declare function cn(...inputs: ClassValue[]): string;
|
|
10
|
+
//# sourceMappingURL=cn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cn.d.ts","sourceRoot":"","sources":["../../src/lib/cn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAG7C;;;;;;GAMG;AACH,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC"}
|
package/dist/lib/cn.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { clsx } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
/**
|
|
4
|
+
* Joins class names and lets a caller's utility win over the component's own.
|
|
5
|
+
*
|
|
6
|
+
* Without the merge, `<Button className="px-8">` produces "px-4 px-8" and which
|
|
7
|
+
* one applies depends on their order in the stylesheet, not on which the caller
|
|
8
|
+
* asked for. twMerge drops the loser, so an override is an override.
|
|
9
|
+
*/
|
|
10
|
+
export function cn(...inputs) {
|
|
11
|
+
return twMerge(clsx(inputs));
|
|
12
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renk matematiği: sRGB · OKLab · WCAG oranı · CIE L*.
|
|
3
|
+
*
|
|
4
|
+
* Bir bağımlılık değil çünkü ihtiyaç küçük ve KAPI'ya bağlı: paletin doğru
|
|
5
|
+
* olup olmadığını `check-token-contrast` ölçüyor, ve ölçenle üretenin aynı
|
|
6
|
+
* aritmetiği kullanması gerekiyor. `lib/color.test.ts` ikisinin aynı sayıyı
|
|
7
|
+
* verdiğini kanıtlıyor.
|
|
8
|
+
*
|
|
9
|
+
* NEDEN OKLCH. Palet "aynı açıklıkta, başka renk" üretmek zorunda: mor bir
|
|
10
|
+
* markanın zemini ile mavi bir markanın zemini AYNI açıklıkta olmalı, yoksa
|
|
11
|
+
* biri ötekinden koyu görünür ve kontrast eşikleri kayar. HSL bunu yapamıyor
|
|
12
|
+
* (HSL'de %50 sarı ile %50 mavi bambaşka parlaklıkta); OKLab algısal olarak
|
|
13
|
+
* düzgün, yani L sabit tutulduğunda açıklık gerçekten sabit kalıyor.
|
|
14
|
+
*/
|
|
15
|
+
export type Oklch = {
|
|
16
|
+
l: number;
|
|
17
|
+
c: number;
|
|
18
|
+
h: number;
|
|
19
|
+
};
|
|
20
|
+
/** `#abc` ya da `#aabbcc`. Kısa biçim de sayılıyor, boşluk sayılmıyor. */
|
|
21
|
+
export declare const gecerliHex: (v: string) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Hex → RGB, GEÇERSİZ GİRDİDE FIRLATARAK.
|
|
24
|
+
*
|
|
25
|
+
* Önce sessizce `NaN` üretiyordu ve palet gri bir şeye düşüyordu: bir kullanıcı
|
|
26
|
+
* hex kutusuna `#7c3aed` yazarken ara adımlarda (`#7`, `#7c`) panel griye
|
|
27
|
+
* dönüyor, sonra geri geliyordu. Hata vermeyen bir bozulma en pahalısı, çünkü
|
|
28
|
+
* sebebi rengin kendisinde aranıyor.
|
|
29
|
+
*
|
|
30
|
+
* Kit KATI, çağıran toleranslı: yarım yazılmış bir kodun ne anlama geldiği
|
|
31
|
+
* ekranın kararı (bkz. `gecerliHex`).
|
|
32
|
+
*/
|
|
33
|
+
export declare function hexRgb(hex: string): [number, number, number];
|
|
34
|
+
export declare const rgbHex: (r: number, g: number, b: number) => string;
|
|
35
|
+
/** WCAG bağıl parlaklık. Metin kontrastı bununla ölçülüyor. */
|
|
36
|
+
export declare function parlaklik(hex: string): number;
|
|
37
|
+
/** WCAG kontrast oranı. AA metin eşiği 4.5. */
|
|
38
|
+
export declare function oran(a: string, b: string): number;
|
|
39
|
+
/**
|
|
40
|
+
* CIE L* · algısal açıklık.
|
|
41
|
+
*
|
|
42
|
+
* İki YÜZEYİN ayrı okunup okunmadığı bununla ölçülüyor; kontrast oranı metin
|
|
43
|
+
* içindir, yüzey için değil. Bir kartın kenarı sayfadan ΔL* ≥ 10 ayrı olmalı.
|
|
44
|
+
*/
|
|
45
|
+
export declare function acikligi(hex: string): number;
|
|
46
|
+
export declare const dL: (a: string, b: string) => number;
|
|
47
|
+
export declare function hexOklch(hex: string): Oklch;
|
|
48
|
+
/**
|
|
49
|
+
* OKLCH → hex, GAMUT DIŞINA TAŞMADAN.
|
|
50
|
+
*
|
|
51
|
+
* Bir renk sRGB'ye sığmıyorsa kanalları kırpmak TONU kaydırıyor: mor bir yüz
|
|
52
|
+
* kırpıldığında maviye dönüyor ve marka rengi olmaktan çıkıyor. Onun yerine
|
|
53
|
+
* doyum (C) ikili aramayla sığana kadar düşürülüyor; ton ve açıklık korunuyor,
|
|
54
|
+
* yalnız canlılık gerektiği kadar azalıyor.
|
|
55
|
+
*/
|
|
56
|
+
export declare function oklchHex({ l, c, h }: Oklch): string;
|
|
57
|
+
/**
|
|
58
|
+
* Verilen ton ve doyumda, HEDEF ORANI tutturan açıklığı arar.
|
|
59
|
+
*
|
|
60
|
+
* Palet "şu formülle koyulaştır" demiyor, ÖLÇÜP arıyor: bir rengin beyaz
|
|
61
|
+
* mürekkebi 4.5'te taşıyıp taşımadığı tona göre değişiyor (sarı ile lacivert
|
|
62
|
+
* aynı L'de aynı oranı vermiyor). Formül tahmin eder, arama ölçer.
|
|
63
|
+
*
|
|
64
|
+
* `yon` "koyulaş" (-1) ya da "açıl" (+1): koyu temada aynı hedef ters yönden
|
|
65
|
+
* yakalanıyor.
|
|
66
|
+
*/
|
|
67
|
+
export declare function oranaGoreAcikligi(taban: Oklch, karsi: string, hedef: number, yon: -1 | 1): Oklch;
|
|
68
|
+
//# sourceMappingURL=color.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/lib/color.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,KAAK,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAKxD,0EAA0E;AAC1E,eAAO,MAAM,UAAU,MAAO,MAAM,KAAG,OAA0D,CAAC;AAElG;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAM5D;AAGD,eAAO,MAAM,MAAM,MAAO,MAAM,KAAK,MAAM,KAAK,MAAM,WACuB,CAAC;AAE9E,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,+CAA+C;AAC/C,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED,eAAO,MAAM,EAAE,MAAO,MAAM,KAAK,MAAM,WAAwC,CAAC;AAIhF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAiB3C;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,GAAG,MAAM,CA6BnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,GACV,KAAK,CAQP"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renk matematiği: sRGB · OKLab · WCAG oranı · CIE L*.
|
|
3
|
+
*
|
|
4
|
+
* Bir bağımlılık değil çünkü ihtiyaç küçük ve KAPI'ya bağlı: paletin doğru
|
|
5
|
+
* olup olmadığını `check-token-contrast` ölçüyor, ve ölçenle üretenin aynı
|
|
6
|
+
* aritmetiği kullanması gerekiyor. `lib/color.test.ts` ikisinin aynı sayıyı
|
|
7
|
+
* verdiğini kanıtlıyor.
|
|
8
|
+
*
|
|
9
|
+
* NEDEN OKLCH. Palet "aynı açıklıkta, başka renk" üretmek zorunda: mor bir
|
|
10
|
+
* markanın zemini ile mavi bir markanın zemini AYNI açıklıkta olmalı, yoksa
|
|
11
|
+
* biri ötekinden koyu görünür ve kontrast eşikleri kayar. HSL bunu yapamıyor
|
|
12
|
+
* (HSL'de %50 sarı ile %50 mavi bambaşka parlaklıkta); OKLab algısal olarak
|
|
13
|
+
* düzgün, yani L sabit tutulduğunda açıklık gerçekten sabit kalıyor.
|
|
14
|
+
*/
|
|
15
|
+
const srgbDoğrusal = (v) => (v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);
|
|
16
|
+
const doğrusalSrgb = (v) => (v <= 0.0031308 ? v * 12.92 : 1.055 * v ** (1 / 2.4) - 0.055);
|
|
17
|
+
/** `#abc` ya da `#aabbcc`. Kısa biçim de sayılıyor, boşluk sayılmıyor. */
|
|
18
|
+
export const gecerliHex = (v) => /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.test(v.trim());
|
|
19
|
+
/**
|
|
20
|
+
* Hex → RGB, GEÇERSİZ GİRDİDE FIRLATARAK.
|
|
21
|
+
*
|
|
22
|
+
* Önce sessizce `NaN` üretiyordu ve palet gri bir şeye düşüyordu: bir kullanıcı
|
|
23
|
+
* hex kutusuna `#7c3aed` yazarken ara adımlarda (`#7`, `#7c`) panel griye
|
|
24
|
+
* dönüyor, sonra geri geliyordu. Hata vermeyen bir bozulma en pahalısı, çünkü
|
|
25
|
+
* sebebi rengin kendisinde aranıyor.
|
|
26
|
+
*
|
|
27
|
+
* Kit KATI, çağıran toleranslı: yarım yazılmış bir kodun ne anlama geldiği
|
|
28
|
+
* ekranın kararı (bkz. `gecerliHex`).
|
|
29
|
+
*/
|
|
30
|
+
export function hexRgb(hex) {
|
|
31
|
+
if (!gecerliHex(hex))
|
|
32
|
+
throw new Error(`Geçersiz hex: ${JSON.stringify(hex)}`);
|
|
33
|
+
const s = hex.trim().replace("#", "");
|
|
34
|
+
const t = s.length === 3 ? [...s].map((c) => c + c).join("") : s;
|
|
35
|
+
const n = Number.parseInt(t, 16);
|
|
36
|
+
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
37
|
+
}
|
|
38
|
+
const kanal = (v) => Math.max(0, Math.min(255, Math.round(v * 255)));
|
|
39
|
+
export const rgbHex = (r, g, b) => "#" + [r, g, b].map((v) => kanal(v).toString(16).padStart(2, "0")).join("");
|
|
40
|
+
/** WCAG bağıl parlaklık. Metin kontrastı bununla ölçülüyor. */
|
|
41
|
+
export function parlaklik(hex) {
|
|
42
|
+
const [r, g, b] = hexRgb(hex).map((v) => srgbDoğrusal(v / 255));
|
|
43
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
44
|
+
}
|
|
45
|
+
/** WCAG kontrast oranı. AA metin eşiği 4.5. */
|
|
46
|
+
export function oran(a, b) {
|
|
47
|
+
const [ust, alt] = [parlaklik(a), parlaklik(b)].sort((x, y) => y - x);
|
|
48
|
+
return (ust + 0.05) / (alt + 0.05);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* CIE L* · algısal açıklık.
|
|
52
|
+
*
|
|
53
|
+
* İki YÜZEYİN ayrı okunup okunmadığı bununla ölçülüyor; kontrast oranı metin
|
|
54
|
+
* içindir, yüzey için değil. Bir kartın kenarı sayfadan ΔL* ≥ 10 ayrı olmalı.
|
|
55
|
+
*/
|
|
56
|
+
export function acikligi(hex) {
|
|
57
|
+
const y = parlaklik(hex);
|
|
58
|
+
return y > 216 / 24389 ? 116 * Math.cbrt(y) - 16 : (y * 24389) / 27;
|
|
59
|
+
}
|
|
60
|
+
export const dL = (a, b) => Math.abs(acikligi(a) - acikligi(b));
|
|
61
|
+
/* ---- OKLab ---- */
|
|
62
|
+
export function hexOklch(hex) {
|
|
63
|
+
const [r8, g8, b8] = hexRgb(hex);
|
|
64
|
+
const r = srgbDoğrusal(r8 / 255);
|
|
65
|
+
const g = srgbDoğrusal(g8 / 255);
|
|
66
|
+
const b = srgbDoğrusal(b8 / 255);
|
|
67
|
+
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
68
|
+
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
69
|
+
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
70
|
+
const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;
|
|
71
|
+
const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;
|
|
72
|
+
const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;
|
|
73
|
+
const c = Math.hypot(A, B);
|
|
74
|
+
const h = c < 1e-6 ? 0 : ((Math.atan2(B, A) * 180) / Math.PI + 360) % 360;
|
|
75
|
+
return { l: L, c, h };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* OKLCH → hex, GAMUT DIŞINA TAŞMADAN.
|
|
79
|
+
*
|
|
80
|
+
* Bir renk sRGB'ye sığmıyorsa kanalları kırpmak TONU kaydırıyor: mor bir yüz
|
|
81
|
+
* kırpıldığında maviye dönüyor ve marka rengi olmaktan çıkıyor. Onun yerine
|
|
82
|
+
* doyum (C) ikili aramayla sığana kadar düşürülüyor; ton ve açıklık korunuyor,
|
|
83
|
+
* yalnız canlılık gerektiği kadar azalıyor.
|
|
84
|
+
*/
|
|
85
|
+
export function oklchHex({ l, c, h }) {
|
|
86
|
+
const dene = (kroma) => {
|
|
87
|
+
const hr = (h * Math.PI) / 180;
|
|
88
|
+
const A = Math.cos(hr) * kroma;
|
|
89
|
+
const B = Math.sin(hr) * kroma;
|
|
90
|
+
const l_ = (l + 0.3963377774 * A + 0.2158037573 * B) ** 3;
|
|
91
|
+
const m_ = (l - 0.1055613458 * A - 0.0638541728 * B) ** 3;
|
|
92
|
+
const s_ = (l - 0.0894841775 * A - 1.291485548 * B) ** 3;
|
|
93
|
+
return [
|
|
94
|
+
doğrusalSrgb(+4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_),
|
|
95
|
+
doğrusalSrgb(-1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_),
|
|
96
|
+
doğrusalSrgb(-0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_),
|
|
97
|
+
];
|
|
98
|
+
};
|
|
99
|
+
const sigiyorMu = (v) => v.every((x) => x >= -0.001 && x <= 1.001);
|
|
100
|
+
let rgb = dene(c);
|
|
101
|
+
if (!sigiyorMu(rgb)) {
|
|
102
|
+
let alt = 0;
|
|
103
|
+
let ust = c;
|
|
104
|
+
for (let i = 0; i < 24; i++) {
|
|
105
|
+
const orta = (alt + ust) / 2;
|
|
106
|
+
if (sigiyorMu(dene(orta)))
|
|
107
|
+
alt = orta;
|
|
108
|
+
else
|
|
109
|
+
ust = orta;
|
|
110
|
+
}
|
|
111
|
+
rgb = dene(alt);
|
|
112
|
+
}
|
|
113
|
+
return rgbHex(...rgb);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Verilen ton ve doyumda, HEDEF ORANI tutturan açıklığı arar.
|
|
117
|
+
*
|
|
118
|
+
* Palet "şu formülle koyulaştır" demiyor, ÖLÇÜP arıyor: bir rengin beyaz
|
|
119
|
+
* mürekkebi 4.5'te taşıyıp taşımadığı tona göre değişiyor (sarı ile lacivert
|
|
120
|
+
* aynı L'de aynı oranı vermiyor). Formül tahmin eder, arama ölçer.
|
|
121
|
+
*
|
|
122
|
+
* `yon` "koyulaş" (-1) ya da "açıl" (+1): koyu temada aynı hedef ters yönden
|
|
123
|
+
* yakalanıyor.
|
|
124
|
+
*/
|
|
125
|
+
export function oranaGoreAcikligi(taban, karsi, hedef, yon) {
|
|
126
|
+
let en = { ...taban };
|
|
127
|
+
for (let i = 0; i < 60; i++) {
|
|
128
|
+
const aday = { ...taban, l: Math.max(0.02, Math.min(0.99, taban.l + yon * 0.01 * i)) };
|
|
129
|
+
en = aday;
|
|
130
|
+
if (oran(oklchHex(aday), karsi) >= hedef)
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
return en;
|
|
134
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { acikligi, dL, gecerliHex, oran } from "./color.js";
|
|
2
|
+
/**
|
|
3
|
+
* Tek bir marka renginden iki temalık palet.
|
|
4
|
+
*
|
|
5
|
+
* NEDEN ÜRETİLİYOR. Bugüne kadar bir müşterinin rengi elle geçiriliyordu: yüz,
|
|
6
|
+
* taban, mürekkep, seçili satır zemini ve koyu tema karşılıkları tek tek
|
|
7
|
+
* seçiliyordu. Beş token için makul, otuz altı token için değil, ve her seferinde
|
|
8
|
+
* kontrast eşiği elle kontrol ediliyordu.
|
|
9
|
+
*
|
|
10
|
+
* ZEMİN DE DÖNÜYOR. Bugünün zemini sıcak kâğıt (#f5f2ec) ve aksandan bağımsız;
|
|
11
|
+
* mor bir markanın yanında sarıya çalıyor. Nötrler markanın TONUNU çok düşük
|
|
12
|
+
* doyumla taşıyor: gri kalıyorlar ama markanın grisi oluyorlar.
|
|
13
|
+
*
|
|
14
|
+
* EŞİKLER TAHMİN DEĞİL ARAMA. Her renk "şu formülle koyulaştır" ile değil,
|
|
15
|
+
* hedef orana ulaşana kadar ÖLÇÜLEREK bulunuyor — çünkü bir tonun beyaz
|
|
16
|
+
* mürekkebi hangi açıklıkta taşıdığı tona göre değişiyor. Sonuç
|
|
17
|
+
* `check-token-contrast` ile aynı aritmetikten geçiyor (bkz. `lib/color.ts`).
|
|
18
|
+
*/
|
|
19
|
+
/** Bir temanın üreteceği token'lar. Adlar `theme.css`'teki kit ailesiyle birebir. */
|
|
20
|
+
export type Palet = {
|
|
21
|
+
page: string;
|
|
22
|
+
shell: string;
|
|
23
|
+
hover: string;
|
|
24
|
+
band: string;
|
|
25
|
+
rail: string;
|
|
26
|
+
sunk: string;
|
|
27
|
+
chartFill: string;
|
|
28
|
+
line: string;
|
|
29
|
+
edge: string;
|
|
30
|
+
tick: string;
|
|
31
|
+
ink: string;
|
|
32
|
+
inkSoft: string;
|
|
33
|
+
inkFaint: string;
|
|
34
|
+
accent: string;
|
|
35
|
+
accentHover: string;
|
|
36
|
+
accentActive: string;
|
|
37
|
+
accentInk: string;
|
|
38
|
+
accentLine: string;
|
|
39
|
+
accentBg: string;
|
|
40
|
+
accentShadow: string;
|
|
41
|
+
navIdle: string;
|
|
42
|
+
navHover: string;
|
|
43
|
+
navHoverBg: string;
|
|
44
|
+
};
|
|
45
|
+
export type PaletCifti = {
|
|
46
|
+
light: Palet;
|
|
47
|
+
dark: Palet;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Bir marka renginden iki tema.
|
|
51
|
+
*
|
|
52
|
+
* Geçersiz bir kodda FIRLATIYOR, sessizce griye düşmüyor: yarım yazılmış bir
|
|
53
|
+
* hex'in ne anlama geldiğine çağıran karar veriyor (`gecerliHex`).
|
|
54
|
+
*/
|
|
55
|
+
export declare function paletUret(markaHex: string): PaletCifti;
|
|
56
|
+
export { gecerliHex };
|
|
57
|
+
export type Olcum = {
|
|
58
|
+
ad: string;
|
|
59
|
+
tur: "oran" | "dL";
|
|
60
|
+
deger: number;
|
|
61
|
+
esik: number;
|
|
62
|
+
gecti: boolean;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Üretilen paleti `check-token-contrast` ile AYNI eşiklerden geçirir.
|
|
66
|
+
*
|
|
67
|
+
* Üreticiye güvenmek yerine ölçmek şart: bir formül bugün doğru olabilir ve
|
|
68
|
+
* yarın bir ton için yanlış olur. Marka rengi değiştiğinde bu liste yeniden
|
|
69
|
+
* koşuyor, ve geçmeyen bir palet build'i kırıyor.
|
|
70
|
+
*/
|
|
71
|
+
export declare function paletiOlc(p: Palet): Olcum[];
|
|
72
|
+
/**
|
|
73
|
+
* Bir paleti CSS değişkeni sözlüğüne çevirir: `{ "--color-accent": "#..." }`.
|
|
74
|
+
*
|
|
75
|
+
* NEDEN `paletCss` YETMİYOR: o bir STİL SAYFASI dizgisi üretiyor (`:root { … }`),
|
|
76
|
+
* ve bir stil sayfası ancak belgenin tamamına uygulanabiliyor. Bir paleti tek
|
|
77
|
+
* bir kutuya uygulamak (bir önizleme, bir tema seçici) ya da köke çalışma
|
|
78
|
+
* zamanında yazmak için gereken şey bir SÖZLÜK. Simge adları kalıtsal olduğu
|
|
79
|
+
* için bir kutuya yazılan palet, içindeki her bileşeni birlikte döndürüyor.
|
|
80
|
+
*/
|
|
81
|
+
export declare function paletStili(p: Palet): Record<string, string>;
|
|
82
|
+
export declare function paletCss(cift: PaletCifti): string;
|
|
83
|
+
export { acikligi, dL, oran };
|
|
84
|
+
//# sourceMappingURL=palette.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"palette.d.ts","sourceRoot":"","sources":["../../src/lib/palette.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAsB,IAAI,EAAc,MAAM,YAAY,CAAC;AAE5F;;;;;;;;;;;;;;;;GAgBG;AAEH,qFAAqF;AACrF,MAAM,MAAM,KAAK,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC;AAqHvD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAItD;AAED,OAAO,EAAE,UAAU,EAAE,CAAC;AAItB,MAAM,MAAM,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAEpG;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,CAsB3C;AAwBD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI3D;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAMjD;AAED,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { acikligi, dL, gecerliHex, hexOklch, oklchHex, oran } from "./color.js";
|
|
2
|
+
/**
|
|
3
|
+
* Nötrlerin AÇIKLIK basamakları, iki temada.
|
|
4
|
+
*
|
|
5
|
+
* Sayılar bugünkü paletten ölçüldü, uydurulmadı: `#f5f2ec` ile `#fdfcfa`
|
|
6
|
+
* arasındaki fark ne ise mor bir markada da o. Böylece marka değişince
|
|
7
|
+
* yüzeylerin BİRBİRİNE göre yeri sabit kalıyor, yalnız tonu dönüyor.
|
|
8
|
+
*/
|
|
9
|
+
const BASAMAK = {
|
|
10
|
+
light: { page: 0.962, shell: 0.991, hover: 0.933, sunk: 0.933, line: 0.891, edge: 0.845, tick: 0.771, ink: 0.246, inkSoft: 0.372, inkFaint: 0.516, navIdle: 0.523, navHover: 0.386 },
|
|
11
|
+
dark: { page: 0.166, shell: 0.212, hover: 0.262, sunk: 0.262, line: 0.302, edge: 0.372, tick: 0.409, ink: 0.951, inkSoft: 0.869, inkFaint: 0.643, navIdle: 0.606, navHover: 0.869 },
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Nötrlerin doyumu: gri kalacak kadar az, markayı taşıyacak kadar çok.
|
|
15
|
+
*
|
|
16
|
+
* YÜKSELEN YÜZEY TONSUZ, ve bu bir tutarsızlık değil bir ayrım. Marka rengi
|
|
17
|
+
* ZEMİNE giriyor: sayfa, şerit, ray, satır vurgusu. Kart ise zeminin üstünde
|
|
18
|
+
* DURAN şey, ve beyaza yakın kalması onu her markada aynı yükseklikte
|
|
19
|
+
* tutuyor — kartın rengi değil kenarı yükseltiyor (13 Yasa 1).
|
|
20
|
+
*
|
|
21
|
+
* Pratik karşılığı: müşteri rengini değiştirdiğinde sayfanın zemini dönüyor
|
|
22
|
+
* ama kenar çubuğu, üst şerit ve kartlar yerinde kalıyor. Panelin iskeleti
|
|
23
|
+
* sabit, rengi değişken.
|
|
24
|
+
*/
|
|
25
|
+
const NOTR_DOYUM = { zemin: 0.006, yuzey: 0, cizgi: 0.009, murekkep: 0.014 };
|
|
26
|
+
const notr = (h, l, c) => oklchHex({ l, c, h });
|
|
27
|
+
/**
|
|
28
|
+
* Yüzün üstündeki mürekkep: beyaz mı koyu mu.
|
|
29
|
+
*
|
|
30
|
+
* ÖLÇÜLEREK seçiliyor, kurala göre değil. Lacivert bir yüz beyaz mürekkep
|
|
31
|
+
* taşıyor, sarı bir yüz taşımıyor — ve "markalar koyu olur" varsayımı ilk sarı
|
|
32
|
+
* markada okunmayan bir düğme üretiyor.
|
|
33
|
+
*/
|
|
34
|
+
function yuzMurekkebi(yuz, acikNotr, koyuNotr) {
|
|
35
|
+
return oran(acikNotr, yuz) >= oran(koyuNotr, yuz) ? acikNotr : koyuNotr;
|
|
36
|
+
}
|
|
37
|
+
function temaUret(marka, koyu) {
|
|
38
|
+
const h = marka.h;
|
|
39
|
+
const b = koyu ? BASAMAK.dark : BASAMAK.light;
|
|
40
|
+
const page = notr(h, b.page, NOTR_DOYUM.zemin);
|
|
41
|
+
/* Kart · üst şerit · kenar çubuğu: markadan bağımsız. */
|
|
42
|
+
const shell = notr(h, b.shell, NOTR_DOYUM.yuzey);
|
|
43
|
+
const hover = notr(h, b.hover, NOTR_DOYUM.zemin);
|
|
44
|
+
const ink = notr(h, b.ink, NOTR_DOYUM.murekkep);
|
|
45
|
+
/* YÜZ: markanın KENDİ açıklığından başlıyor.
|
|
46
|
+
*
|
|
47
|
+
* İlk hâli sabit bir açıklıktan başlayıp beyaz mürekkep geçene kadar
|
|
48
|
+
* koyulaştırıyordu, ve sarı bir markayı `#8f6c00` yapıyordu: ölçüm geçiyor
|
|
49
|
+
* ama kimse ona sarı demiyor. Marka rengini tanınmaz hâle getiren bir palet
|
|
50
|
+
* üreticisi işini yapmıyor.
|
|
51
|
+
*
|
|
52
|
+
* Doğrusu: yüz markanın açıklığında kalıyor (kullanılabilir bir banda
|
|
53
|
+
* kırpılarak), MÜREKKEP ona göre seçiliyor. Lacivert bir yüz beyaz taşır,
|
|
54
|
+
* sarı bir yüz koyu taşır — ikisi de markasını koruyor. Yalnız hiçbir
|
|
55
|
+
* mürekkeple 4.5'e ulaşılamıyorsa yüz mürekkepten UZAĞA itiliyor. */
|
|
56
|
+
const band = koyu ? { alt: 0.58, ust: 0.82 } : { alt: 0.45, ust: 0.78 };
|
|
57
|
+
const yuzL = Math.max(band.alt, Math.min(band.ust, marka.l));
|
|
58
|
+
/* DOYUM KIRPILMIYOR. Bir süre `min(marka.c, 0.16)` vardı ve doygun bir marka
|
|
59
|
+
kırmızısını (C 0.215) `#c74a4a`ya soldurup markayı gözle görülür şekilde
|
|
60
|
+
değiştiriyordu. Kırpma gereksizdi: `oklchHex` zaten gamut'a sığmayan bir
|
|
61
|
+
rengi ikili aramayla sığdırıyor ve TONU koruyor. Kırpmak, çözülmüş bir
|
|
62
|
+
sorunu ikinci kez ve daha kötü çözmekti. */
|
|
63
|
+
const kroma = marka.c;
|
|
64
|
+
let yuz = oklchHex({ l: yuzL, c: kroma, h });
|
|
65
|
+
let murekkep = yuzMurekkebi(yuz, shell, ink);
|
|
66
|
+
for (let i = 1; i <= 40 && oran(yuz, murekkep) < 4.5; i++) {
|
|
67
|
+
/* Mürekkep açıksa yüz koyulaşır, koyuysa açılır: mesafe hep açılıyor. */
|
|
68
|
+
const yon = acikligi(murekkep) > 50 ? -1 : 1;
|
|
69
|
+
yuz = oklchHex({ l: Math.max(0.2, Math.min(0.92, yuzL + yon * 0.01 * i)), c: kroma, h });
|
|
70
|
+
murekkep = yuzMurekkebi(yuz, shell, ink);
|
|
71
|
+
}
|
|
72
|
+
const yuzOk = hexOklch(yuz);
|
|
73
|
+
const adim = (d) => oklchHex({ ...yuzOk, l: Math.max(0.06, Math.min(0.96, yuzOk.l + d)) });
|
|
74
|
+
/* ÇİZGİ RENGİ (bağlantı, odak halkası) sayfada AA geçmek zorunda: bir
|
|
75
|
+
bağlantı metindir. Yüz genelde geçmiyor, o yüzden ayrıca aranıyor. */
|
|
76
|
+
let cizgi = koyu ? adim(0.08) : adim(-0.18);
|
|
77
|
+
for (let i = 0; i < 40 && oran(cizgi, page) < 4.5; i++) {
|
|
78
|
+
cizgi = oklchHex({ ...yuzOk, l: Math.max(0.08, Math.min(0.95, yuzOk.l + (koyu ? 1 : -1) * (0.18 + 0.01 * i))) });
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
page,
|
|
82
|
+
shell,
|
|
83
|
+
hover,
|
|
84
|
+
band: page,
|
|
85
|
+
rail: page,
|
|
86
|
+
sunk: hover,
|
|
87
|
+
chartFill: hover,
|
|
88
|
+
line: notr(h, b.line, NOTR_DOYUM.cizgi),
|
|
89
|
+
edge: notr(h, b.edge, NOTR_DOYUM.cizgi),
|
|
90
|
+
tick: notr(h, b.tick, NOTR_DOYUM.cizgi),
|
|
91
|
+
ink,
|
|
92
|
+
inkSoft: notr(h, b.inkSoft, NOTR_DOYUM.murekkep),
|
|
93
|
+
inkFaint: notr(h, b.inkFaint, NOTR_DOYUM.murekkep),
|
|
94
|
+
accent: yuz,
|
|
95
|
+
accentHover: adim(koyu ? 0.05 : -0.06),
|
|
96
|
+
accentActive: adim(koyu ? -0.04 : -0.11),
|
|
97
|
+
accentInk: murekkep,
|
|
98
|
+
accentLine: cizgi,
|
|
99
|
+
/* Seçili satır zemini: sayfaya çok yakın, ama ayrı okunuyor. */
|
|
100
|
+
accentBg: oklchHex({ l: koyu ? b.page + 0.06 : b.page - 0.035, c: 0.03, h }),
|
|
101
|
+
/* Taban: yüzün ALTINDA duran koyu, doygun renk (13 Yasa 1). */
|
|
102
|
+
accentShadow: oklchHex({ l: koyu ? 0.42 : 0.28, c: Math.min(marka.c, 0.13), h }),
|
|
103
|
+
navIdle: notr(h, b.navIdle, NOTR_DOYUM.murekkep),
|
|
104
|
+
navHover: notr(h, b.navHover, NOTR_DOYUM.murekkep),
|
|
105
|
+
navHoverBg: notr(h, koyu ? b.hover : b.hover - 0.01, NOTR_DOYUM.zemin),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Bir marka renginden iki tema.
|
|
110
|
+
*
|
|
111
|
+
* Geçersiz bir kodda FIRLATIYOR, sessizce griye düşmüyor: yarım yazılmış bir
|
|
112
|
+
* hex'in ne anlama geldiğine çağıran karar veriyor (`gecerliHex`).
|
|
113
|
+
*/
|
|
114
|
+
export function paletUret(markaHex) {
|
|
115
|
+
if (!gecerliHex(markaHex))
|
|
116
|
+
throw new Error(`Geçersiz marka rengi: ${JSON.stringify(markaHex)}`);
|
|
117
|
+
const marka = hexOklch(markaHex);
|
|
118
|
+
return { light: temaUret(marka, false), dark: temaUret(marka, true) };
|
|
119
|
+
}
|
|
120
|
+
export { gecerliHex };
|
|
121
|
+
/**
|
|
122
|
+
* Üretilen paleti `check-token-contrast` ile AYNI eşiklerden geçirir.
|
|
123
|
+
*
|
|
124
|
+
* Üreticiye güvenmek yerine ölçmek şart: bir formül bugün doğru olabilir ve
|
|
125
|
+
* yarın bir ton için yanlış olur. Marka rengi değiştiğinde bu liste yeniden
|
|
126
|
+
* koşuyor, ve geçmeyen bir palet build'i kırıyor.
|
|
127
|
+
*/
|
|
128
|
+
export function paletiOlc(p) {
|
|
129
|
+
const metin = (ad, a, b, esik = 4.5) => {
|
|
130
|
+
const deger = Number(oran(a, b).toFixed(2));
|
|
131
|
+
return { ad, tur: "oran", deger, esik, gecti: deger >= esik };
|
|
132
|
+
};
|
|
133
|
+
const yuzey = (ad, a, b, esik) => {
|
|
134
|
+
const deger = Number(dL(a, b).toFixed(1));
|
|
135
|
+
return { ad, tur: "dL", deger, esik, gecti: deger >= esik };
|
|
136
|
+
};
|
|
137
|
+
return [
|
|
138
|
+
metin("ink · page", p.ink, p.page),
|
|
139
|
+
metin("ink · shell", p.ink, p.shell),
|
|
140
|
+
metin("inkSoft · page", p.inkSoft, p.page),
|
|
141
|
+
metin("inkFaint · page", p.inkFaint, p.page),
|
|
142
|
+
metin("inkFaint · sunk", p.inkFaint, p.sunk),
|
|
143
|
+
metin("accentInk · accent", p.accentInk, p.accent),
|
|
144
|
+
metin("accentLine · page", p.accentLine, p.page),
|
|
145
|
+
metin("navIdle · rail", p.navIdle, p.rail),
|
|
146
|
+
yuzey("edge · page", p.edge, p.page, 10),
|
|
147
|
+
yuzey("line · page", p.line, p.page, 4),
|
|
148
|
+
yuzey("accentBg · page", p.accentBg, p.page, 1.2),
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
/** `:root` bloğuna yapıştırılabilir CSS. */
|
|
152
|
+
/**
|
|
153
|
+
* Palet alanı → CSS simge adı.
|
|
154
|
+
*
|
|
155
|
+
* DIŞARI AÇIK, ÇÜNKÜ ÜÇÜNCÜ KOPYASI DOĞACAKTI. Bu eşleme `paletCss`in içinde
|
|
156
|
+
* özeldi; bir ürün paneli aynısını kendi dosyasına elle yazmıştı (rengi
|
|
157
|
+
* çalışma zamanında köke yazmak için), ve doküman sitesi de üçüncüsünü
|
|
158
|
+
* yazacaktı. Aynı yirmi iki satırın üç kopyası, kite bir simge eklendiği gün
|
|
159
|
+
* ikisinin sessizce eksik kalması demek.
|
|
160
|
+
*/
|
|
161
|
+
const AD = {
|
|
162
|
+
page: "--color-page", shell: "--color-shell", hover: "--color-hover", band: "--color-band",
|
|
163
|
+
rail: "--color-rail", sunk: "--color-sunk", chartFill: "--color-chart-fill",
|
|
164
|
+
line: "--color-line", edge: "--color-edge", tick: "--color-tick",
|
|
165
|
+
ink: "--color-ink", inkSoft: "--color-ink-soft", inkFaint: "--color-ink-faint",
|
|
166
|
+
accent: "--color-accent", accentHover: "--color-accent-hover",
|
|
167
|
+
accentActive: "--color-accent-active", accentInk: "--color-accent-ink",
|
|
168
|
+
accentLine: "--color-accent-line", accentBg: "--color-accent-bg",
|
|
169
|
+
accentShadow: "--color-accent-shadow", navIdle: "--color-nav-idle",
|
|
170
|
+
navHover: "--color-nav-hover", navHoverBg: "--color-nav-hover-bg",
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Bir paleti CSS değişkeni sözlüğüne çevirir: `{ "--color-accent": "#..." }`.
|
|
174
|
+
*
|
|
175
|
+
* NEDEN `paletCss` YETMİYOR: o bir STİL SAYFASI dizgisi üretiyor (`:root { … }`),
|
|
176
|
+
* ve bir stil sayfası ancak belgenin tamamına uygulanabiliyor. Bir paleti tek
|
|
177
|
+
* bir kutuya uygulamak (bir önizleme, bir tema seçici) ya da köke çalışma
|
|
178
|
+
* zamanında yazmak için gereken şey bir SÖZLÜK. Simge adları kalıtsal olduğu
|
|
179
|
+
* için bir kutuya yazılan palet, içindeki her bileşeni birlikte döndürüyor.
|
|
180
|
+
*/
|
|
181
|
+
export function paletStili(p) {
|
|
182
|
+
const stil = {};
|
|
183
|
+
for (const alan of Object.keys(AD))
|
|
184
|
+
stil[AD[alan]] = p[alan];
|
|
185
|
+
return stil;
|
|
186
|
+
}
|
|
187
|
+
export function paletCss(cift) {
|
|
188
|
+
const blok = (p) => Object.entries(paletStili(p))
|
|
189
|
+
.map(([ad, deger]) => ` ${ad}: ${deger};`)
|
|
190
|
+
.join("\n");
|
|
191
|
+
return `:root {\n${blok(cift.light)}\n}\n\n.dark {\n${blok(cift.dark)}\n}\n`;
|
|
192
|
+
}
|
|
193
|
+
export { acikligi, dL, oran };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal açıkken arkadaki sayfanın kaymasını durdurur.
|
|
3
|
+
*
|
|
4
|
+
* NEDEN VAR. `Sheet` ve `Dialog` odağı hapsediyordu ve Escape'i dinliyordu ama
|
|
5
|
+
* arkadaki sayfa kaymaya devam ediyordu. Kullanıcı panel açıkken tekerleği
|
|
6
|
+
* çevirdiğinde arkadaki liste kayıyor, panel kapandığında da kendini bambaşka
|
|
7
|
+
* bir yerde buluyordu. Odak zaten hapsedildiği için arkayla ETKİLEŞEMİYOR;
|
|
8
|
+
* kaydırabilmesi bir yetenek değil, bir kaçak.
|
|
9
|
+
*
|
|
10
|
+
* ÇUBUK GENİŞLİĞİ TELAFİ EDİLİYOR. `overflow: hidden` kaydırma çubuğunu
|
|
11
|
+
* kaldırıyor, ve çubuk kaybolunca sayfa o kadar genişleyip SIÇRIYOR. Kaybolan
|
|
12
|
+
* genişlik kadar sağdan dolgu veriliyor; kapanınca geri alınıyor.
|
|
13
|
+
*
|
|
14
|
+
* PANELİN KENDİSİ KAYAR. Bu kilit yalnız `body`ye bakıyor; panelin gövdesi
|
|
15
|
+
* kendi `overflow-y: auto` kabında, yani içeriği ekrandan uzunsa yine
|
|
16
|
+
* okunabiliyor. Her şeyi birden kilitlemek erişilebilirlik hatası olurdu.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useScrollLock(active: boolean): void;
|
|
19
|
+
//# sourceMappingURL=scroll-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-lock.d.ts","sourceRoot":"","sources":["../../src/lib/scroll-lock.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,QAc5C"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Modal açıkken arkadaki sayfanın kaymasını durdurur.
|
|
4
|
+
*
|
|
5
|
+
* NEDEN VAR. `Sheet` ve `Dialog` odağı hapsediyordu ve Escape'i dinliyordu ama
|
|
6
|
+
* arkadaki sayfa kaymaya devam ediyordu. Kullanıcı panel açıkken tekerleği
|
|
7
|
+
* çevirdiğinde arkadaki liste kayıyor, panel kapandığında da kendini bambaşka
|
|
8
|
+
* bir yerde buluyordu. Odak zaten hapsedildiği için arkayla ETKİLEŞEMİYOR;
|
|
9
|
+
* kaydırabilmesi bir yetenek değil, bir kaçak.
|
|
10
|
+
*
|
|
11
|
+
* ÇUBUK GENİŞLİĞİ TELAFİ EDİLİYOR. `overflow: hidden` kaydırma çubuğunu
|
|
12
|
+
* kaldırıyor, ve çubuk kaybolunca sayfa o kadar genişleyip SIÇRIYOR. Kaybolan
|
|
13
|
+
* genişlik kadar sağdan dolgu veriliyor; kapanınca geri alınıyor.
|
|
14
|
+
*
|
|
15
|
+
* PANELİN KENDİSİ KAYAR. Bu kilit yalnız `body`ye bakıyor; panelin gövdesi
|
|
16
|
+
* kendi `overflow-y: auto` kabında, yani içeriği ekrandan uzunsa yine
|
|
17
|
+
* okunabiliyor. Her şeyi birden kilitlemek erişilebilirlik hatası olurdu.
|
|
18
|
+
*/
|
|
19
|
+
export function useScrollLock(active) {
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!active)
|
|
22
|
+
return;
|
|
23
|
+
const { overflow, paddingRight } = document.body.style;
|
|
24
|
+
const bosluk = window.innerWidth - document.documentElement.clientWidth;
|
|
25
|
+
document.body.style.overflow = "hidden";
|
|
26
|
+
if (bosluk > 0)
|
|
27
|
+
document.body.style.paddingRight = `${bosluk}px`;
|
|
28
|
+
return () => {
|
|
29
|
+
document.body.style.overflow = overflow;
|
|
30
|
+
document.body.style.paddingRight = paddingRight;
|
|
31
|
+
};
|
|
32
|
+
}, [active]);
|
|
33
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
2
|
+
import { Icon } from "../components/icon.js";
|
|
3
|
+
import { type LinkComponent } from "./shared.js";
|
|
4
|
+
/**
|
|
5
|
+
* Oturum açmış her ekranın içinde durduğu çerçeve: ray, üst şerit, yüzey.
|
|
6
|
+
*
|
|
7
|
+
* Menü ve açık giriş DIŞARIDAN gelir; kabuk hiçbir ürünün sözlüğünü ve hiçbir
|
|
8
|
+
* yönlendiriciyi tanımaz. Gerekçe: docs/adr/0004-sablon-katmani.md
|
|
9
|
+
*/
|
|
10
|
+
export type NavEntry = {
|
|
11
|
+
key: string;
|
|
12
|
+
href: string;
|
|
13
|
+
/** Çevrilmiş etiket. Ray dar olduğu için ekranda değil, `title` ve etikette. */
|
|
14
|
+
label: string;
|
|
15
|
+
icon: ComponentProps<typeof Icon>["icon"];
|
|
16
|
+
};
|
|
17
|
+
export type AppShellProps = {
|
|
18
|
+
/** Rayın tepesindeki marka işareti; kendi bağlantısını çağıran veriyor. */
|
|
19
|
+
brand?: ReactNode;
|
|
20
|
+
nav: readonly NavEntry[];
|
|
21
|
+
/** Açık yol. Rota `usePathname()` geçiyor. */
|
|
22
|
+
activePath: string;
|
|
23
|
+
/**
|
|
24
|
+
* Üst şerit KÜRESEL BAĞLAM, iş değil. İçerik çağıranın; şablon yalnız yerini
|
|
25
|
+
* garanti ediyor.
|
|
26
|
+
*/
|
|
27
|
+
topbar?: ReactNode;
|
|
28
|
+
linkComponent?: LinkComponent;
|
|
29
|
+
labels: {
|
|
30
|
+
home: string;
|
|
31
|
+
primaryNav: string;
|
|
32
|
+
};
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
};
|
|
35
|
+
export declare function AppShell({ brand, nav, activePath, topbar, linkComponent: Link, labels, children, }: AppShellProps): import("react").JSX.Element;
|
|
36
|
+
//# sourceMappingURL=app-shell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-shell.d.ts","sourceRoot":"","sources":["../../src/patterns/app-shell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5D;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,2EAA2E;IAC3E,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,GAAG,EAAE,SAAS,QAAQ,EAAE,CAAC;IACzB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,GAAG,EACH,UAAU,EACV,MAAM,EACN,aAAa,EAAE,IAAgB,EAC/B,MAAM,EACN,QAAQ,GACT,EAAE,aAAa,+BAwDf"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Icon } from "../components/icon.js";
|
|
4
|
+
import { PlainLink } from "./shared.js";
|
|
5
|
+
export function AppShell({ brand, nav, activePath, topbar, linkComponent: Link = PlainLink, labels, children, }) {
|
|
6
|
+
/* Bir giriş kendi alt ağacına sahip: `/urunler`, `/urunler/42/fotograflar`
|
|
7
|
+
üzerinde de açık giriştir. Tam eşleşme yalnız kök yol için, yoksa kısa href
|
|
8
|
+
altındaki her rotayı yutar. */
|
|
9
|
+
const isCurrent = (href) => href === "/" ? activePath === href : activePath === href || activePath.startsWith(`${href}/`);
|
|
10
|
+
return (_jsxs("div", { className: "flex h-dvh w-full overflow-hidden bg-shell text-ink-soft", children: [_jsxs("aside", { className: "flex w-16 shrink-0 flex-col items-center gap-2 bg-shell px-1 py-4", children: [brand ? (_jsxs(_Fragment, { children: [_jsx("span", { "aria-label": labels.home, className: "mb-2 flex size-10 shrink-0 items-center justify-center text-ink", children: brand }), _jsx("span", { className: "h-0 w-6 border-t border-line" })] })) : null, _jsx("nav", { "aria-label": labels.primaryNav, className: "tamga-rail-scroll flex w-full flex-1 flex-col items-center gap-2 py-1", children: nav.map((entry) => {
|
|
11
|
+
const current = isCurrent(entry.href);
|
|
12
|
+
return (_jsx(Link, { href: entry.href, "aria-label": entry.label, title: entry.label, "aria-current": current ? "page" : undefined, "data-active": current, "data-nav": entry.key, className: "tamga-rail-link", children: _jsx(Icon, { icon: entry.icon, size: "base" }) }, entry.key));
|
|
13
|
+
}) })] }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("header", { className: "tamga-gutter flex items-center gap-4 py-4", children: topbar }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden p-4 pt-0", children: _jsx("main", { className: "tamga-surface h-full min-h-0 overflow-y-auto", children: children }) })] })] }));
|
|
14
|
+
}
|