@comitor/ui 0.1.0 → 0.2.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 +222 -41
- package/dist/components/composite/avatar-group.js +1 -1
- package/dist/components/composite/avatar-group.js.map +1 -1
- package/dist/components/composite/combobox.js +2 -2
- package/dist/components/composite/combobox.js.map +1 -1
- package/dist/components/composite/confirm-dialog.js +1 -1
- package/dist/components/composite/confirm-dialog.js.map +1 -1
- package/dist/components/composite/copy-button.js +1 -1
- package/dist/components/composite/copy-button.js.map +1 -1
- package/dist/components/composite/data-table.d.ts +17 -8
- package/dist/components/composite/data-table.js +8 -8
- package/dist/components/composite/data-table.js.map +1 -1
- package/dist/components/composite/filter-chips.js +6 -5
- package/dist/components/composite/filter-chips.js.map +1 -1
- package/dist/components/composite/form-field.js +3 -1
- package/dist/components/composite/form-field.js.map +1 -1
- package/dist/components/composite/form-section.js +5 -5
- package/dist/components/composite/form-section.js.map +1 -1
- package/dist/components/composite/inline-edit.js +1 -1
- package/dist/components/composite/inline-edit.js.map +1 -1
- package/dist/components/composite/keyboard-hint.d.ts +16 -0
- package/dist/components/composite/keyboard-hint.js +8 -6
- package/dist/components/composite/keyboard-hint.js.map +1 -1
- package/dist/components/composite/letter-avatar.d.ts +10 -18
- package/dist/components/composite/letter-avatar.js +6 -17
- package/dist/components/composite/letter-avatar.js.map +1 -1
- package/dist/components/composite/search-filter-bar.d.ts +41 -3
- package/dist/components/composite/search-filter-bar.js +27 -15
- package/dist/components/composite/search-filter-bar.js.map +1 -1
- package/dist/components/composite/stat-card.d.ts +16 -1
- package/dist/components/composite/stat-card.js +16 -9
- package/dist/components/composite/stat-card.js.map +1 -1
- package/dist/components/composite/status-config.d.ts +69 -16
- package/dist/components/composite/status-config.js +10 -10
- package/dist/components/composite/status-config.js.map +1 -1
- package/dist/components/composite/status-pill.js +5 -1
- package/dist/components/composite/status-pill.js.map +1 -1
- package/dist/components/composite/table-pagination.d.ts +46 -4
- package/dist/components/composite/table-pagination.js +27 -24
- package/dist/components/composite/table-pagination.js.map +1 -1
- package/dist/components/composite/text-utils.d.ts +12 -11
- package/dist/components/composite/text-utils.js +7 -16
- package/dist/components/composite/text-utils.js.map +1 -1
- package/dist/components/composite/toast.js +16 -6
- package/dist/components/composite/toast.js.map +1 -1
- package/dist/components/ui/alert.js +1 -1
- package/dist/components/ui/alert.js.map +1 -1
- package/dist/components/ui/badge.js +4 -4
- package/dist/components/ui/badge.js.map +1 -1
- package/dist/components/ui/button.js +2 -2
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/calendar.d.ts +41 -0
- package/dist/components/ui/calendar.js +1 -1
- package/dist/components/ui/calendar.js.map +1 -1
- package/dist/components/ui/checkbox.js +3 -3
- package/dist/components/ui/checkbox.js.map +1 -1
- package/dist/components/ui/command.js +1 -1
- package/dist/components/ui/command.js.map +1 -1
- package/dist/components/ui/context-menu.js +1 -1
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/components/ui/drawer.js +1 -1
- package/dist/components/ui/drawer.js.map +1 -1
- package/dist/components/ui/dropdown-menu.js +1 -1
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/empty.js +1 -1
- package/dist/components/ui/empty.js.map +1 -1
- package/dist/components/ui/field.js +4 -4
- package/dist/components/ui/field.js.map +1 -1
- package/dist/components/ui/form.js +8 -2
- package/dist/components/ui/form.js.map +1 -1
- package/dist/components/ui/input-group.js +3 -3
- package/dist/components/ui/input-group.js.map +1 -1
- package/dist/components/ui/input-otp.js +1 -1
- package/dist/components/ui/input-otp.js.map +1 -1
- package/dist/components/ui/input.js +3 -3
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/item.js +1 -1
- package/dist/components/ui/item.js.map +1 -1
- package/dist/components/ui/menubar.js +1 -1
- package/dist/components/ui/menubar.js.map +1 -1
- package/dist/components/ui/navigation-menu.js +1 -1
- package/dist/components/ui/navigation-menu.js.map +1 -1
- package/dist/components/ui/pagination.js +1 -1
- package/dist/components/ui/pagination.js.map +1 -1
- package/dist/components/ui/progress.d.ts +26 -0
- package/dist/components/ui/progress.js +5 -2
- package/dist/components/ui/progress.js.map +1 -1
- package/dist/components/ui/radio-group.js +2 -2
- package/dist/components/ui/radio-group.js.map +1 -1
- package/dist/components/ui/resizable.d.ts +14 -0
- package/dist/components/ui/resizable.js +2 -2
- package/dist/components/ui/resizable.js.map +1 -1
- package/dist/components/ui/scroll-area.js +1 -1
- package/dist/components/ui/scroll-area.js.map +1 -1
- package/dist/components/ui/select.d.ts +33 -0
- package/dist/components/ui/select.js +3 -3
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/slider.d.ts +19 -0
- package/dist/components/ui/slider.js +3 -3
- package/dist/components/ui/slider.js.map +1 -1
- package/dist/components/ui/sonner.d.ts +21 -3
- package/dist/components/ui/sonner.js +1 -2
- package/dist/components/ui/sonner.js.map +1 -1
- package/dist/components/ui/switch.js +2 -2
- package/dist/components/ui/switch.js.map +1 -1
- package/dist/components/ui/table.d.ts +16 -0
- package/dist/components/ui/table.js +1 -1
- package/dist/components/ui/table.js.map +1 -1
- package/dist/components/ui/tabs.js +1 -1
- package/dist/components/ui/tabs.js.map +1 -1
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/textarea.js.map +1 -1
- package/dist/components/ui/toggle.js +1 -1
- package/dist/components/ui/toggle.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +17 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/avatar-tone.d.ts +126 -0
- package/dist/lib/avatar-tone.js +68 -0
- package/dist/lib/avatar-tone.js.map +1 -0
- package/dist/shell/app-header.js +12 -2
- package/dist/shell/app-header.js.map +1 -1
- package/dist/shell/app-launcher.js +2 -2
- package/dist/shell/app-launcher.js.map +1 -1
- package/dist/shell/index.d.ts +3 -3
- package/dist/shell/shell-context.d.ts +1 -7
- package/dist/shell/shell-context.js +1 -19
- package/dist/shell/shell-context.js.map +1 -1
- package/dist/shell/sidebar-group.js +5 -3
- package/dist/shell/sidebar-group.js.map +1 -1
- package/dist/shell/sidebar-menu-item.js +12 -4
- package/dist/shell/sidebar-menu-item.js.map +1 -1
- package/dist/shell/types.d.ts +17 -1
- package/dist/shell/types.js.map +1 -1
- package/dist/shell/user-menu.js +3 -2
- package/dist/shell/user-menu.js.map +1 -1
- package/dist/shell/utils.d.ts +10 -22
- package/dist/shell/utils.js +3 -11
- package/dist/shell/utils.js.map +1 -1
- package/dist/shell/workspace-switcher.js +23 -8
- package/dist/shell/workspace-switcher.js.map +1 -1
- package/dist/tokens.d.ts +165 -17
- package/dist/tokens.js +122 -25
- package/dist/tokens.js.map +1 -1
- package/package.json +4 -2
- package/styles.css +312 -22
package/dist/tokens.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { AvatarToneColors } from './lib/avatar-tone.js';
|
|
2
|
+
export { getAvatarToneIndex } from './lib/avatar-tone.js';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Design token dạng TS object — dành cho nơi KHÔNG có Tailwind: canvas, biểu đồ tuỳ biến,
|
|
3
6
|
* email HTML, xuất PDF, (sau này) React Native.
|
|
@@ -7,7 +10,18 @@
|
|
|
7
10
|
*
|
|
8
11
|
* Trong môi trường có Tailwind, LUÔN dùng class token (`bg-primary`, `text-ash-500`),
|
|
9
12
|
* đừng đọc hex từ đây rồi nhét vào style inline: làm vậy là mất dark mode.
|
|
13
|
+
*
|
|
14
|
+
* Hợp đồng ba vai trò của màu (giống hệt `styles.css`; bảng đầy đủ ở docs/hop-dong-mau.md),
|
|
15
|
+
* đọc tên là biết dùng ở đâu:
|
|
16
|
+
* `x` → tô NỀN (fill đặc hoặc tint /5–/20 — trần chung an toàn cho mọi tone)
|
|
17
|
+
* `xForeground` → chữ/icon NẰM TRÊN nền `x` ĐẶC — đo với `x`, ngưỡng 4,5:1
|
|
18
|
+
* `xInk` → chính màu đó khi ĐI LÊN nền trang hoặc lên tint của chính nó (chữ, icon
|
|
19
|
+
* rời, viền, vạch active) — đo với nền đó
|
|
20
|
+
*
|
|
21
|
+
* Cố ý KHÔNG mirror (xem docs/quyet-dinh-tich-hop.md §21): `--elevation-*` (chuỗi box-shadow chỉ
|
|
22
|
+
* có nghĩa trong CSS), `--app-accent*` (giá trị thuộc về APP, không phải gói), `--font-*`.
|
|
10
23
|
*/
|
|
24
|
+
|
|
11
25
|
/** Thang GOLD 7 bậc — trục màu chính của brand Comitor. */
|
|
12
26
|
declare const gold: {
|
|
13
27
|
readonly 50: "#FBF5DC";
|
|
@@ -40,8 +54,12 @@ declare const ash: {
|
|
|
40
54
|
readonly 900: "#16181D";
|
|
41
55
|
};
|
|
42
56
|
/**
|
|
43
|
-
* Màu
|
|
44
|
-
* bản `light`
|
|
57
|
+
* Màu phụ — vai trò TÔ NỀN (fill đặc hoặc tint). Bản `dark` được nâng sáng vì trên nền #0E1116
|
|
58
|
+
* bản `light` chìm nghỉm.
|
|
59
|
+
*
|
|
60
|
+
* ⚠ ĐỪNG dùng bảng này làm màu CHỮ ở light: nó được chọn để tô nền nên quá nhạt — dùng
|
|
61
|
+
* `accentInk` ngay dưới. `accent.dark.navy` giữ nguyên #0F1E3D chỉ để mirror đúng `styles.css`
|
|
62
|
+
* (khối `.dark` không override `--navy`); vai trò navy ở dark do `accentInk.dark.navy` gánh.
|
|
45
63
|
*/
|
|
46
64
|
declare const accent: {
|
|
47
65
|
readonly light: {
|
|
@@ -59,6 +77,65 @@ declare const accent: {
|
|
|
59
77
|
readonly yellow: "#FACC15";
|
|
60
78
|
};
|
|
61
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Bậc INK của màu phụ — mirror `--teal-ink` / `--green-ink` / `--red-ink` / `--yellow-ink` /
|
|
82
|
+
* `--navy-ink`. Đây là màu phụ khi nó làm **CHỮ/ICON/VIỀN trên nền trang** (hoặc trên tint
|
|
83
|
+
* /5–/20 của chính nó), tức vai trò NGƯỢC với `accent`.
|
|
84
|
+
*
|
|
85
|
+
* Hợp đồng đặt tên của cả gói: `--x` tô nền · `--x-foreground` là chữ NẰM TRÊN nền `--x` ·
|
|
86
|
+
* `--x-ink` là chính màu đó ĐI LÊN nền trang.
|
|
87
|
+
*
|
|
88
|
+
* Bản `light` là bậc tối đã đóng băng (kết quả của `color-mix(in srgb, --<màu>, --ink 30/40%)`
|
|
89
|
+
* mà trước đây mỗi component tự tính lấy); bản `dark` chính là `accent.dark` vì bên đó màu phụ
|
|
90
|
+
* đã đủ sáng để làm chữ. Đo trên tint 15% của chính nó — nền thật mà chữ initials avatar và
|
|
91
|
+
* badge tonal đứng lên: teal 5,54 · green 5,60 · red 5,83:1 ở light (bản chưa có ink: 3,44 /
|
|
92
|
+
* 2,69 / 3,57:1 ✗); 7,12 / 7,44 / 5,18:1 ở dark.
|
|
93
|
+
*/
|
|
94
|
+
declare const accentInk: {
|
|
95
|
+
readonly light: {
|
|
96
|
+
readonly navy: "#0F1E3D";
|
|
97
|
+
readonly teal: "#1A6668";
|
|
98
|
+
readonly red: "#9A3537";
|
|
99
|
+
readonly green: "#216A48";
|
|
100
|
+
/** = gold[500]: vàng không có bậc tối riêng, mượn thang gold cùng sắc. */
|
|
101
|
+
readonly yellow: "#7A5800";
|
|
102
|
+
};
|
|
103
|
+
readonly dark: {
|
|
104
|
+
/** navy nguyên bản chìm vào nền dark (1,15:1) → bậc sáng riêng; cũng là `--chart-3` ở dark. */
|
|
105
|
+
readonly navy: "#7EA2E8";
|
|
106
|
+
readonly teal: "#2DD4BF";
|
|
107
|
+
readonly red: "#F87171";
|
|
108
|
+
readonly green: "#4ADE80";
|
|
109
|
+
readonly yellow: "#FACC15";
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Bậc FOREGROUND của màu phụ — mirror `--teal-foreground` / `--green-foreground` /
|
|
114
|
+
* `--yellow-foreground` / `--navy-foreground`. Đây là chữ/icon nằm **TRÊN nền `accent.*` ĐẶC**,
|
|
115
|
+
* tức vai trò thứ ba, khác hẳn `accentInk` (màu đó đi lên nền TRANG).
|
|
116
|
+
*
|
|
117
|
+
* Cả ba sắc teal/green/yellow đều sáng ở light và còn sáng hơn ở dark → chữ là MỰC ở cả hai
|
|
118
|
+
* theme, một giá trị đủ dùng: teal 4,56:1 (light) / 10,16:1 (dark) · green 6,04 / 10,85 ·
|
|
119
|
+
* yellow 11,92 / 12,35. Navy thì ngược lại — chữ TRẮNG, 16,49:1 ở cả hai theme.
|
|
120
|
+
*
|
|
121
|
+
* ⚠ CỐ Ý KHÔNG có `red`: nền `accent.light.red` #D64545 đặc không mang được chữ ở light — trắng
|
|
122
|
+
* 4,38:1 ✗ và mực 4,32:1 ✗. Nền đỏ đặc có chữ phải dùng `status.*.destructive` (#C93B3B) cùng
|
|
123
|
+
* `destructiveForeground` = 5,03:1 ✓. `accent.*.red` chỉ để tô TINT và làm màu chart.
|
|
124
|
+
*/
|
|
125
|
+
declare const accentForeground: {
|
|
126
|
+
readonly light: {
|
|
127
|
+
readonly navy: "#FFFFFF";
|
|
128
|
+
readonly teal: "#0E1116";
|
|
129
|
+
readonly green: "#0E1116";
|
|
130
|
+
readonly yellow: "#0E1116";
|
|
131
|
+
};
|
|
132
|
+
readonly dark: {
|
|
133
|
+
readonly navy: "#FFFFFF";
|
|
134
|
+
readonly teal: "#0E1116";
|
|
135
|
+
readonly green: "#0E1116";
|
|
136
|
+
readonly yellow: "#0E1116";
|
|
137
|
+
};
|
|
138
|
+
};
|
|
62
139
|
/** Semantic token của shadcn — mirror khối `:root` / `.dark` trong styles.css. */
|
|
63
140
|
declare const semantic: {
|
|
64
141
|
readonly light: {
|
|
@@ -70,17 +147,43 @@ declare const semantic: {
|
|
|
70
147
|
readonly popoverForeground: "#0E1116";
|
|
71
148
|
readonly primary: "#E8B824";
|
|
72
149
|
readonly primaryForeground: "#0E1116";
|
|
150
|
+
/** Bậc INK của brand — gold khi phải ĐI LÊN nền trang (rãnh Switch khi BẬT, ô Checkbox đã
|
|
151
|
+
* chọn). `primary` #E8B824 chỉ 1,86:1 với nền trắng ✗; bậc này = `gold[500]` = 6,51:1 ✓. */
|
|
152
|
+
readonly primaryInk: "#7A5800";
|
|
73
153
|
readonly secondary: "#EDE8D8";
|
|
74
154
|
readonly secondaryForeground: "#0E1116";
|
|
75
155
|
readonly muted: "#F7F7F8";
|
|
76
|
-
|
|
156
|
+
/** Bậc chữ phụ — là CHỮ nên ngưỡng AA 4,5:1: 5,23:1 trên nền trắng, 4,89:1 trên `muted`.
|
|
157
|
+
* KHÔNG còn bằng `ash[500]` (#7E8290 chỉ 3,83:1 ✗) — bậc bảng màu và token semantic tách nhau. */
|
|
158
|
+
readonly mutedForeground: "#686C7A";
|
|
77
159
|
readonly accent: "#FBF5DC";
|
|
78
160
|
readonly accentForeground: "#0E1116";
|
|
79
|
-
readonly destructive: "#
|
|
161
|
+
readonly destructive: "#C93B3B";
|
|
80
162
|
readonly destructiveForeground: "#FFFFFF";
|
|
163
|
+
/** Chữ báo lỗi trên nền trang — KHÁC `destructive` (nền) và `destructiveForeground` (chữ
|
|
164
|
+
* trắng trên nền đó). 7,15:1 trên nền trắng. */
|
|
165
|
+
readonly destructiveInk: "#9A3537";
|
|
166
|
+
/** RANH GIỚI TRANG TRÍ (vạch chia, viền thẻ nghỉ) — 1,16:1, cố ý: 1.4.11 không đòi tương
|
|
167
|
+
* phản cho thứ không mang thông tin. Ranh giới của CONTROL đi qua `controlEdge`. */
|
|
81
168
|
readonly border: "#EDEEF1";
|
|
82
|
-
|
|
169
|
+
/** = `controlEdge`: viền ô nhập là ranh giới nhận dạng control (1.4.11) → 3,83:1 ✓. */
|
|
170
|
+
readonly input: "#7E8290";
|
|
83
171
|
readonly ring: "#E8B824";
|
|
172
|
+
/** Ranh giới của control lúc NGHỈ (viền Input, ô Checkbox chưa chọn, rãnh Switch khi TẮT) —
|
|
173
|
+
* WCAG 1.4.11, ràng buộc HAI PHÍA. Một giá trị cho cả hai theme: 3,83:1 với nền trắng và
|
|
174
|
+
* 3,83:1 với `controlKnob` (trắng ở light); 4,94:1 với nền dark và với knob #0E1116. */
|
|
175
|
+
readonly controlEdge: "#7E8290";
|
|
176
|
+
/** Cùng vai trò nhưng ở HOVER/ACTIVE — phải ĐẬM hơn lúc nghỉ. = `ash[700]`, 10,55:1 trên nền
|
|
177
|
+
* trắng · 9,86:1 trên `muted`. */
|
|
178
|
+
readonly controlEdgeStrong: "#3A3F4A";
|
|
179
|
+
/** Nền control khi BẬT/ĐÃ CHỌN (rãnh Switch bật, ô Checkbox đã chọn, chấm Radio) = `primaryInk`.
|
|
180
|
+
* Ràng buộc hai phía: 6,51:1 với nền trang VÀ 6,51:1 với `controlKnob` ở light. `primary`
|
|
181
|
+
* #E8B824 chỉ 1,86:1 cả hai phía ✗. */
|
|
182
|
+
readonly controlOn: "#7A5800";
|
|
183
|
+
/** Núm Switch / dấu check / chấm Radio — mang màu NỀN TRANG nên luôn tương phản đúng chiều
|
|
184
|
+
* với cả rãnh BẬT (6,51:1) lẫn rãnh TẮT (3,83:1). KHÔNG dùng `primaryForeground` (mực): mực
|
|
185
|
+
* trên `controlOn` #7A5800 chỉ 1,86:1 ✗. */
|
|
186
|
+
readonly controlKnob: "#FFFFFF";
|
|
84
187
|
readonly sidebar: "#FAFAF9";
|
|
85
188
|
readonly sidebarForeground: "#0E1116";
|
|
86
189
|
readonly sidebarBorder: "#EDEEF1";
|
|
@@ -96,17 +199,33 @@ declare const semantic: {
|
|
|
96
199
|
readonly popoverForeground: "#F7F7F8";
|
|
97
200
|
readonly primary: "#F5D060";
|
|
98
201
|
readonly primaryForeground: "#0E1116";
|
|
202
|
+
/** Ở dark `primary` vốn đã là bậc sáng (12,67:1 trên nền `background`) nên ink = chính nó. */
|
|
203
|
+
readonly primaryInk: "#F5D060";
|
|
99
204
|
readonly secondary: "#1E2128";
|
|
100
205
|
readonly secondaryForeground: "#F7F7F8";
|
|
101
206
|
readonly muted: "#1E2128";
|
|
102
|
-
|
|
207
|
+
/** Sáng hơn bản light: chữ phụ ở dark hay nằm trên `card`/`muted`. 5,35:1 trên `card`,
|
|
208
|
+
* 4,86:1 trên `muted` (giá trị cũ #7E8290 chỉ 4,21:1 trên `muted` ✗). */
|
|
209
|
+
readonly mutedForeground: "#888D9B";
|
|
103
210
|
readonly accent: "#1E2128";
|
|
104
211
|
readonly accentForeground: "#F7F7F8";
|
|
105
|
-
readonly destructive: "#
|
|
212
|
+
readonly destructive: "#C93B3B";
|
|
106
213
|
readonly destructiveForeground: "#FFFFFF";
|
|
214
|
+
/** 6,42:1 trên `card` — `destructive` dùng làm chữ ở dark chỉ được 3,53:1 ✗. */
|
|
215
|
+
readonly destructiveInk: "#F87171";
|
|
216
|
+
/** TRANG TRÍ, 1,17:1 trên `background` và 1,10:1 trên `card` — cố ý, xem nhánh light. */
|
|
107
217
|
readonly border: "#1E2128";
|
|
108
|
-
|
|
218
|
+
/** = `controlEdge`, ghim một giá trị cho cả hai theme — 4,94:1 trên `background` ✓. */
|
|
219
|
+
readonly input: "#7E8290";
|
|
109
220
|
readonly ring: "#F5D060";
|
|
221
|
+
/** Ghim đúng giá trị của light — xem chú thích ở nhánh light. */
|
|
222
|
+
readonly controlEdge: "#7E8290";
|
|
223
|
+
/** = `ash[300]` (lật chiều so với light): 11,65:1 trên `background` · 10,94:1 trên `card`. */
|
|
224
|
+
readonly controlEdgeStrong: "#C8CBD3";
|
|
225
|
+
/** = `primaryInk` của dark; 12,67:1 với nền trang VÀ với `controlKnob` (#0E1116). */
|
|
226
|
+
readonly controlOn: "#F5D060";
|
|
227
|
+
/** = `background` của dark. Núm trắng cứng sẽ chỉ được 1,49:1 với rãnh BẬT ✗. */
|
|
228
|
+
readonly controlKnob: "#0E1116";
|
|
110
229
|
readonly sidebar: "#16181D";
|
|
111
230
|
readonly sidebarForeground: "#F7F7F8";
|
|
112
231
|
readonly sidebarBorder: "#1E2128";
|
|
@@ -114,17 +233,36 @@ declare const semantic: {
|
|
|
114
233
|
readonly sidebarAccentForeground: "#F7F7F8";
|
|
115
234
|
};
|
|
116
235
|
};
|
|
117
|
-
/**
|
|
236
|
+
/**
|
|
237
|
+
* Alias trạng thái — success/warning/info trỏ vào màu phụ, destructive giữ nguyên 2 chế độ.
|
|
238
|
+
*
|
|
239
|
+
* Ba lớp KHÁC nhau, đừng gộp:
|
|
240
|
+
* • `destructive` (#C93B3B) — màu NỀN (nút xoá), đủ đậm cho chữ trắng ở cả hai theme (5,03:1);
|
|
241
|
+
* • `destructiveForeground` (#FFFFFF) — chữ NẰM TRÊN nền đó;
|
|
242
|
+
* • `destructiveInk` — màu CHỮ đỏ trên nền trang (dòng lỗi). Dùng nhầm `destructive` cho vai
|
|
243
|
+
* trò này thì ở dark chỉ được 3,53:1, dùng `accent.light.red` thì ở light chỉ 4,38:1.
|
|
244
|
+
*
|
|
245
|
+
* `successForeground` / `warningForeground` / `infoForeground` là MỰC ở CẢ hai theme (không phải
|
|
246
|
+
* trắng ở light rồi lật ở dark như bản 0.1.0): ba nền trạng thái đều là sắc sáng.
|
|
247
|
+
* success mực 6,04:1 (light) · 10,85:1 (dark) [trắng chỉ 3,13 / 1,74:1 ✗]
|
|
248
|
+
* info mực 4,56:1 (light) · 10,16:1 (dark) [trắng chỉ 4,14 / 1,86:1 ✗]
|
|
249
|
+
* warning mực 11,92:1 (light) · 12,35:1 (dark) [trắng chỉ 1,59 / 1,53:1 ✗]
|
|
250
|
+
* ⚠ `info` ở light là cặp chặt nhất gói (4,56:1, dư 0,06) — đổi `accent.light.teal` là phải đo lại.
|
|
251
|
+
*/
|
|
118
252
|
declare const status: {
|
|
119
253
|
readonly light: {
|
|
120
254
|
readonly success: "#2EA56A";
|
|
121
|
-
readonly successForeground: "#
|
|
255
|
+
readonly successForeground: "#0E1116";
|
|
122
256
|
readonly warning: "#F2C94C";
|
|
123
257
|
readonly warningForeground: "#0E1116";
|
|
124
258
|
readonly info: "#1F8A8B";
|
|
125
|
-
readonly infoForeground: "#
|
|
126
|
-
readonly destructive: "#
|
|
259
|
+
readonly infoForeground: "#0E1116";
|
|
260
|
+
readonly destructive: "#C93B3B";
|
|
127
261
|
readonly destructiveForeground: "#FFFFFF";
|
|
262
|
+
readonly successInk: "#216A48";
|
|
263
|
+
readonly warningInk: "#7A5800";
|
|
264
|
+
readonly infoInk: "#1A6668";
|
|
265
|
+
readonly destructiveInk: "#9A3537";
|
|
128
266
|
};
|
|
129
267
|
readonly dark: {
|
|
130
268
|
readonly success: "#4ADE80";
|
|
@@ -133,8 +271,12 @@ declare const status: {
|
|
|
133
271
|
readonly warningForeground: "#0E1116";
|
|
134
272
|
readonly info: "#2DD4BF";
|
|
135
273
|
readonly infoForeground: "#0E1116";
|
|
136
|
-
readonly destructive: "#
|
|
274
|
+
readonly destructive: "#C93B3B";
|
|
137
275
|
readonly destructiveForeground: "#FFFFFF";
|
|
276
|
+
readonly successInk: "#4ADE80";
|
|
277
|
+
readonly warningInk: "#FACC15";
|
|
278
|
+
readonly infoInk: "#2DD4BF";
|
|
279
|
+
readonly destructiveInk: "#F87171";
|
|
138
280
|
};
|
|
139
281
|
};
|
|
140
282
|
/** Bảng màu biểu đồ (chart-1…chart-5). Thứ tự = thứ tự series mặc định. */
|
|
@@ -142,8 +284,15 @@ declare const chart: {
|
|
|
142
284
|
readonly light: readonly ["#E8B824", "#1F8A8B", "#0F1E3D", "#2EA56A", "#D64545"];
|
|
143
285
|
readonly dark: readonly ["#F5D060", "#2DD4BF", "#7EA2E8", "#4ADE80", "#F87171"];
|
|
144
286
|
};
|
|
145
|
-
/**
|
|
146
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Cặp nền/chữ (hex) của avatar cho một tên, theo theme.
|
|
289
|
+
*
|
|
290
|
+
* Tên hàm nói đúng thứ nó trả về: `...Colors` = hex (canvas/email/PDF), còn bản trả về chuỗi
|
|
291
|
+
* class Tailwind cho DOM là `getAvatarToneClasses` ở `@comitor/ui`. Cùng một tên người luôn ra
|
|
292
|
+
* cùng một tone ở cả hai đường vì chỉ số do MỘT hàm tính (`getAvatarToneIndex`).
|
|
293
|
+
*/
|
|
294
|
+
declare function getAvatarToneColors(name: string, mode?: ThemeMode): AvatarToneColors;
|
|
295
|
+
|
|
147
296
|
/** Số đo khung ứng dụng — mirror --sidebar-width / --sidebar-width-icon / --header-height. */
|
|
148
297
|
declare const layout: {
|
|
149
298
|
/** px — --header-height 3.5rem */
|
|
@@ -159,7 +308,6 @@ declare const layout: {
|
|
|
159
308
|
readonly tablet: 1024;
|
|
160
309
|
readonly desktop: 1280;
|
|
161
310
|
};
|
|
162
|
-
readonly pageSizes: readonly [20, 50, 100];
|
|
163
311
|
/** px — kích thước tối thiểu của vùng chạm trên mobile (WCAG 2.5.5) */
|
|
164
312
|
readonly minTapTarget: 44;
|
|
165
313
|
};
|
|
@@ -170,4 +318,4 @@ declare const typography: {
|
|
|
170
318
|
};
|
|
171
319
|
type ThemeMode = "light" | "dark";
|
|
172
320
|
|
|
173
|
-
export { type ThemeMode, accent,
|
|
321
|
+
export { AvatarToneColors, type ThemeMode, accent, accentForeground, accentInk, ash, brandNeutral, chart, getAvatarToneColors, gold, layout, semantic, status, typography };
|
package/dist/tokens.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AVATAR_TONES, getAvatarToneIndex } from "./lib/avatar-tone.js";
|
|
1
2
|
const gold = {
|
|
2
3
|
50: "#FBF5DC",
|
|
3
4
|
100: "#F8E89D",
|
|
@@ -38,6 +39,38 @@ const accent = {
|
|
|
38
39
|
yellow: "#FACC15"
|
|
39
40
|
}
|
|
40
41
|
};
|
|
42
|
+
const accentInk = {
|
|
43
|
+
light: {
|
|
44
|
+
navy: accent.light.navy,
|
|
45
|
+
teal: "#1A6668",
|
|
46
|
+
red: "#9A3537",
|
|
47
|
+
green: "#216A48",
|
|
48
|
+
/** = gold[500]: vàng không có bậc tối riêng, mượn thang gold cùng sắc. */
|
|
49
|
+
yellow: gold[500]
|
|
50
|
+
},
|
|
51
|
+
dark: {
|
|
52
|
+
/** navy nguyên bản chìm vào nền dark (1,15:1) → bậc sáng riêng; cũng là `--chart-3` ở dark. */
|
|
53
|
+
navy: "#7EA2E8",
|
|
54
|
+
teal: accent.dark.teal,
|
|
55
|
+
red: accent.dark.red,
|
|
56
|
+
green: accent.dark.green,
|
|
57
|
+
yellow: accent.dark.yellow
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const accentForeground = {
|
|
61
|
+
light: {
|
|
62
|
+
navy: "#FFFFFF",
|
|
63
|
+
teal: "#0E1116",
|
|
64
|
+
green: "#0E1116",
|
|
65
|
+
yellow: "#0E1116"
|
|
66
|
+
},
|
|
67
|
+
dark: {
|
|
68
|
+
navy: "#FFFFFF",
|
|
69
|
+
teal: "#0E1116",
|
|
70
|
+
green: "#0E1116",
|
|
71
|
+
yellow: "#0E1116"
|
|
72
|
+
}
|
|
73
|
+
};
|
|
41
74
|
const semantic = {
|
|
42
75
|
light: {
|
|
43
76
|
background: "#FFFFFF",
|
|
@@ -48,17 +81,43 @@ const semantic = {
|
|
|
48
81
|
popoverForeground: "#0E1116",
|
|
49
82
|
primary: "#E8B824",
|
|
50
83
|
primaryForeground: "#0E1116",
|
|
84
|
+
/** Bậc INK của brand — gold khi phải ĐI LÊN nền trang (rãnh Switch khi BẬT, ô Checkbox đã
|
|
85
|
+
* chọn). `primary` #E8B824 chỉ 1,86:1 với nền trắng ✗; bậc này = `gold[500]` = 6,51:1 ✓. */
|
|
86
|
+
primaryInk: gold[500],
|
|
51
87
|
secondary: "#EDE8D8",
|
|
52
88
|
secondaryForeground: "#0E1116",
|
|
53
89
|
muted: "#F7F7F8",
|
|
54
|
-
|
|
90
|
+
/** Bậc chữ phụ — là CHỮ nên ngưỡng AA 4,5:1: 5,23:1 trên nền trắng, 4,89:1 trên `muted`.
|
|
91
|
+
* KHÔNG còn bằng `ash[500]` (#7E8290 chỉ 3,83:1 ✗) — bậc bảng màu và token semantic tách nhau. */
|
|
92
|
+
mutedForeground: "#686C7A",
|
|
55
93
|
accent: "#FBF5DC",
|
|
56
94
|
accentForeground: "#0E1116",
|
|
57
|
-
destructive: "#
|
|
95
|
+
destructive: "#C93B3B",
|
|
58
96
|
destructiveForeground: "#FFFFFF",
|
|
97
|
+
/** Chữ báo lỗi trên nền trang — KHÁC `destructive` (nền) và `destructiveForeground` (chữ
|
|
98
|
+
* trắng trên nền đó). 7,15:1 trên nền trắng. */
|
|
99
|
+
destructiveInk: accentInk.light.red,
|
|
100
|
+
/** RANH GIỚI TRANG TRÍ (vạch chia, viền thẻ nghỉ) — 1,16:1, cố ý: 1.4.11 không đòi tương
|
|
101
|
+
* phản cho thứ không mang thông tin. Ranh giới của CONTROL đi qua `controlEdge`. */
|
|
59
102
|
border: "#EDEEF1",
|
|
60
|
-
|
|
103
|
+
/** = `controlEdge`: viền ô nhập là ranh giới nhận dạng control (1.4.11) → 3,83:1 ✓. */
|
|
104
|
+
input: "#7E8290",
|
|
61
105
|
ring: "#E8B824",
|
|
106
|
+
/** Ranh giới của control lúc NGHỈ (viền Input, ô Checkbox chưa chọn, rãnh Switch khi TẮT) —
|
|
107
|
+
* WCAG 1.4.11, ràng buộc HAI PHÍA. Một giá trị cho cả hai theme: 3,83:1 với nền trắng và
|
|
108
|
+
* 3,83:1 với `controlKnob` (trắng ở light); 4,94:1 với nền dark và với knob #0E1116. */
|
|
109
|
+
controlEdge: "#7E8290",
|
|
110
|
+
/** Cùng vai trò nhưng ở HOVER/ACTIVE — phải ĐẬM hơn lúc nghỉ. = `ash[700]`, 10,55:1 trên nền
|
|
111
|
+
* trắng · 9,86:1 trên `muted`. */
|
|
112
|
+
controlEdgeStrong: ash[700],
|
|
113
|
+
/** Nền control khi BẬT/ĐÃ CHỌN (rãnh Switch bật, ô Checkbox đã chọn, chấm Radio) = `primaryInk`.
|
|
114
|
+
* Ràng buộc hai phía: 6,51:1 với nền trang VÀ 6,51:1 với `controlKnob` ở light. `primary`
|
|
115
|
+
* #E8B824 chỉ 1,86:1 cả hai phía ✗. */
|
|
116
|
+
controlOn: gold[500],
|
|
117
|
+
/** Núm Switch / dấu check / chấm Radio — mang màu NỀN TRANG nên luôn tương phản đúng chiều
|
|
118
|
+
* với cả rãnh BẬT (6,51:1) lẫn rãnh TẮT (3,83:1). KHÔNG dùng `primaryForeground` (mực): mực
|
|
119
|
+
* trên `controlOn` #7A5800 chỉ 1,86:1 ✗. */
|
|
120
|
+
controlKnob: "#FFFFFF",
|
|
62
121
|
sidebar: "#FAFAF9",
|
|
63
122
|
sidebarForeground: "#0E1116",
|
|
64
123
|
sidebarBorder: "#EDEEF1",
|
|
@@ -74,17 +133,33 @@ const semantic = {
|
|
|
74
133
|
popoverForeground: "#F7F7F8",
|
|
75
134
|
primary: "#F5D060",
|
|
76
135
|
primaryForeground: "#0E1116",
|
|
136
|
+
/** Ở dark `primary` vốn đã là bậc sáng (12,67:1 trên nền `background`) nên ink = chính nó. */
|
|
137
|
+
primaryInk: "#F5D060",
|
|
77
138
|
secondary: "#1E2128",
|
|
78
139
|
secondaryForeground: "#F7F7F8",
|
|
79
140
|
muted: "#1E2128",
|
|
80
|
-
|
|
141
|
+
/** Sáng hơn bản light: chữ phụ ở dark hay nằm trên `card`/`muted`. 5,35:1 trên `card`,
|
|
142
|
+
* 4,86:1 trên `muted` (giá trị cũ #7E8290 chỉ 4,21:1 trên `muted` ✗). */
|
|
143
|
+
mutedForeground: "#888D9B",
|
|
81
144
|
accent: "#1E2128",
|
|
82
145
|
accentForeground: "#F7F7F8",
|
|
83
|
-
destructive: "#
|
|
146
|
+
destructive: "#C93B3B",
|
|
84
147
|
destructiveForeground: "#FFFFFF",
|
|
148
|
+
/** 6,42:1 trên `card` — `destructive` dùng làm chữ ở dark chỉ được 3,53:1 ✗. */
|
|
149
|
+
destructiveInk: accentInk.dark.red,
|
|
150
|
+
/** TRANG TRÍ, 1,17:1 trên `background` và 1,10:1 trên `card` — cố ý, xem nhánh light. */
|
|
85
151
|
border: "#1E2128",
|
|
86
|
-
|
|
152
|
+
/** = `controlEdge`, ghim một giá trị cho cả hai theme — 4,94:1 trên `background` ✓. */
|
|
153
|
+
input: "#7E8290",
|
|
87
154
|
ring: "#F5D060",
|
|
155
|
+
/** Ghim đúng giá trị của light — xem chú thích ở nhánh light. */
|
|
156
|
+
controlEdge: "#7E8290",
|
|
157
|
+
/** = `ash[300]` (lật chiều so với light): 11,65:1 trên `background` · 10,94:1 trên `card`. */
|
|
158
|
+
controlEdgeStrong: ash[300],
|
|
159
|
+
/** = `primaryInk` của dark; 12,67:1 với nền trang VÀ với `controlKnob` (#0E1116). */
|
|
160
|
+
controlOn: "#F5D060",
|
|
161
|
+
/** = `background` của dark. Núm trắng cứng sẽ chỉ được 1,49:1 với rãnh BẬT ✗. */
|
|
162
|
+
controlKnob: "#0E1116",
|
|
88
163
|
sidebar: "#16181D",
|
|
89
164
|
sidebarForeground: "#F7F7F8",
|
|
90
165
|
sidebarBorder: "#1E2128",
|
|
@@ -95,13 +170,17 @@ const semantic = {
|
|
|
95
170
|
const status = {
|
|
96
171
|
light: {
|
|
97
172
|
success: accent.light.green,
|
|
98
|
-
successForeground: "#
|
|
173
|
+
successForeground: "#0E1116",
|
|
99
174
|
warning: accent.light.yellow,
|
|
100
175
|
warningForeground: "#0E1116",
|
|
101
176
|
info: accent.light.teal,
|
|
102
|
-
infoForeground: "#
|
|
103
|
-
destructive: "#
|
|
104
|
-
destructiveForeground: "#FFFFFF"
|
|
177
|
+
infoForeground: "#0E1116",
|
|
178
|
+
destructive: "#C93B3B",
|
|
179
|
+
destructiveForeground: "#FFFFFF",
|
|
180
|
+
successInk: accentInk.light.green,
|
|
181
|
+
warningInk: accentInk.light.yellow,
|
|
182
|
+
infoInk: accentInk.light.teal,
|
|
183
|
+
destructiveInk: accentInk.light.red
|
|
105
184
|
},
|
|
106
185
|
dark: {
|
|
107
186
|
success: accent.dark.green,
|
|
@@ -110,24 +189,40 @@ const status = {
|
|
|
110
189
|
warningForeground: "#0E1116",
|
|
111
190
|
info: accent.dark.teal,
|
|
112
191
|
infoForeground: "#0E1116",
|
|
113
|
-
destructive: "#
|
|
114
|
-
destructiveForeground: "#FFFFFF"
|
|
192
|
+
destructive: "#C93B3B",
|
|
193
|
+
destructiveForeground: "#FFFFFF",
|
|
194
|
+
successInk: accentInk.dark.green,
|
|
195
|
+
warningInk: accentInk.dark.yellow,
|
|
196
|
+
infoInk: accentInk.dark.teal,
|
|
197
|
+
destructiveInk: accentInk.dark.red
|
|
115
198
|
}
|
|
116
199
|
};
|
|
117
200
|
const chart = {
|
|
118
201
|
light: [gold[300], accent.light.teal, accent.light.navy, accent.light.green, accent.light.red],
|
|
119
|
-
dark: [gold[200], accent.dark.teal,
|
|
202
|
+
dark: [gold[200], accent.dark.teal, accentInk.dark.navy, accent.dark.green, accent.dark.red]
|
|
203
|
+
};
|
|
204
|
+
const avatarTonePalette = {
|
|
205
|
+
light: {
|
|
206
|
+
gold,
|
|
207
|
+
ash,
|
|
208
|
+
brandNeutral,
|
|
209
|
+
accent: accent.light,
|
|
210
|
+
accentInk: accentInk.light,
|
|
211
|
+
card: semantic.light.card
|
|
212
|
+
},
|
|
213
|
+
dark: {
|
|
214
|
+
gold,
|
|
215
|
+
ash,
|
|
216
|
+
brandNeutral,
|
|
217
|
+
accent: accent.dark,
|
|
218
|
+
accentInk: accentInk.dark,
|
|
219
|
+
card: semantic.dark.card
|
|
220
|
+
}
|
|
120
221
|
};
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
accent.light.navy,
|
|
126
|
-
accent.light.green,
|
|
127
|
-
accent.light.red,
|
|
128
|
-
ash[500],
|
|
129
|
-
brandNeutral.inkSoft
|
|
130
|
-
];
|
|
222
|
+
function getAvatarToneColors(name, mode = "light") {
|
|
223
|
+
const tone = AVATAR_TONES[getAvatarToneIndex(name)] ?? AVATAR_TONES[0];
|
|
224
|
+
return tone[mode](avatarTonePalette[mode]);
|
|
225
|
+
}
|
|
131
226
|
const layout = {
|
|
132
227
|
/** px — --header-height 3.5rem */
|
|
133
228
|
headerHeight: 56,
|
|
@@ -138,7 +233,6 @@ const layout = {
|
|
|
138
233
|
/** px — --radius 0.375rem */
|
|
139
234
|
radius: 6,
|
|
140
235
|
breakpoints: { mobile: 768, tablet: 1024, desktop: 1280 },
|
|
141
|
-
pageSizes: [20, 50, 100],
|
|
142
236
|
/** px — kích thước tối thiểu của vùng chạm trên mobile (WCAG 2.5.5) */
|
|
143
237
|
minTapTarget: 44
|
|
144
238
|
};
|
|
@@ -148,10 +242,13 @@ const typography = {
|
|
|
148
242
|
};
|
|
149
243
|
export {
|
|
150
244
|
accent,
|
|
245
|
+
accentForeground,
|
|
246
|
+
accentInk,
|
|
151
247
|
ash,
|
|
152
|
-
avatarPalette,
|
|
153
248
|
brandNeutral,
|
|
154
249
|
chart,
|
|
250
|
+
getAvatarToneColors,
|
|
251
|
+
getAvatarToneIndex,
|
|
155
252
|
gold,
|
|
156
253
|
layout,
|
|
157
254
|
semantic,
|
package/dist/tokens.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tokens.ts"],"sourcesContent":["/**\n * Design token dạng TS object — dành cho nơi KHÔNG có Tailwind: canvas, biểu đồ tuỳ biến,\n * email HTML, xuất PDF, (sau này) React Native.\n *\n * ⚠ Nguồn sự thật của giá trị là `styles.css` (CSS variable). File này chỉ MIRROR hằng số.\n * Sửa màu ở styles.css thì phải sửa kèm ở đây — và ngược lại.\n *\n * Trong môi trường có Tailwind, LUÔN dùng class token (`bg-primary`, `text-ash-500`),\n * đừng đọc hex từ đây rồi nhét vào style inline: làm vậy là mất dark mode.\n */\n\n/** Thang GOLD 7 bậc — trục màu chính của brand Comitor. */\nexport const gold = {\n 50: \"#FBF5DC\",\n 100: \"#F8E89D\",\n 200: \"#F5D060\",\n /** primary ở light mode */\n 300: \"#E8B824\",\n 400: \"#B88A00\",\n 500: \"#7A5800\",\n 600: \"#5C4006\"\n} as const;\n\n/** Trung tính brand (khác thang ash — đây là màu \"giấy/mực\" của brand book). */\nexport const brandNeutral = {\n ink: \"#0E1116\",\n inkSoft: \"#3A3F4A\",\n paper: \"#FBF7EE\",\n warm: \"#EDE8D8\"\n} as const;\n\n/**\n * Thang xám Comitor — namespace `ash`, KHÔNG phải `gray`.\n * Chỉ có 6 bậc; các bậc khuyết (200/400/600/800) cố ý không khai để không lẫn với\n * thang gray dựng sẵn của Tailwind (xem chú thích trong styles.css).\n */\nexport const ash = {\n 50: \"#F7F7F8\",\n 100: \"#EDEEF1\",\n 300: \"#C8CBD3\",\n 500: \"#7E8290\",\n 700: \"#3A3F4A\",\n 900: \"#16181D\"\n} as const;\n\n/**\n * Màu phụ. Bản `dark` được nâng sáng để đạt tương phản AA khi làm CHỮ trên nền #0E1116 —\n * bản `light` quá tối cho việc đó.\n */\nexport const accent = {\n light: {\n navy: \"#0F1E3D\",\n teal: \"#1F8A8B\",\n red: \"#D64545\",\n green: \"#2EA56A\",\n yellow: \"#F2C94C\"\n },\n dark: {\n navy: \"#0F1E3D\",\n teal: \"#2DD4BF\",\n red: \"#F87171\",\n green: \"#4ADE80\",\n yellow: \"#FACC15\"\n }\n} as const;\n\n/** Semantic token của shadcn — mirror khối `:root` / `.dark` trong styles.css. */\nexport const semantic = {\n light: {\n background: \"#FFFFFF\",\n foreground: \"#0E1116\",\n card: \"#FFFFFF\",\n cardForeground: \"#0E1116\",\n popover: \"#FFFFFF\",\n popoverForeground: \"#0E1116\",\n primary: \"#E8B824\",\n primaryForeground: \"#0E1116\",\n secondary: \"#EDE8D8\",\n secondaryForeground: \"#0E1116\",\n muted: \"#F7F7F8\",\n mutedForeground: \"#7E8290\",\n accent: \"#FBF5DC\",\n accentForeground: \"#0E1116\",\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\",\n border: \"#EDEEF1\",\n input: \"#EDEEF1\",\n ring: \"#E8B824\",\n sidebar: \"#FAFAF9\",\n sidebarForeground: \"#0E1116\",\n sidebarBorder: \"#EDEEF1\",\n sidebarAccent: \"#FBF5DC\",\n sidebarAccentForeground: \"#0E1116\"\n },\n dark: {\n background: \"#0E1116\",\n foreground: \"#F7F7F8\",\n card: \"#16181D\",\n cardForeground: \"#F7F7F8\",\n popover: \"#16181D\",\n popoverForeground: \"#F7F7F8\",\n primary: \"#F5D060\",\n primaryForeground: \"#0E1116\",\n secondary: \"#1E2128\",\n secondaryForeground: \"#F7F7F8\",\n muted: \"#1E2128\",\n mutedForeground: \"#7E8290\",\n accent: \"#1E2128\",\n accentForeground: \"#F7F7F8\",\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\",\n border: \"#1E2128\",\n input: \"#1E2128\",\n ring: \"#F5D060\",\n sidebar: \"#16181D\",\n sidebarForeground: \"#F7F7F8\",\n sidebarBorder: \"#1E2128\",\n sidebarAccent: \"#1E2128\",\n sidebarAccentForeground: \"#F7F7F8\"\n }\n} as const;\n\n/** Alias trạng thái — success/warning/info trỏ vào màu phụ, destructive giữ nguyên 2 chế độ. */\nexport const status = {\n light: {\n success: accent.light.green,\n successForeground: \"#FFFFFF\",\n warning: accent.light.yellow,\n warningForeground: \"#0E1116\",\n info: accent.light.teal,\n infoForeground: \"#FFFFFF\",\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\"\n },\n dark: {\n success: accent.dark.green,\n successForeground: \"#0E1116\",\n warning: accent.dark.yellow,\n warningForeground: \"#0E1116\",\n info: accent.dark.teal,\n infoForeground: \"#0E1116\",\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\"\n }\n} as const;\n\n/** Bảng màu biểu đồ (chart-1…chart-5). Thứ tự = thứ tự series mặc định. */\nexport const chart = {\n light: [gold[300], accent.light.teal, accent.light.navy, accent.light.green, accent.light.red],\n dark: [gold[200], accent.dark.teal, \"#7EA2E8\", accent.dark.green, accent.dark.red]\n} as const;\n\n/** Màu avatar chữ cái — chọn deterministic theo tên, lấy từ token nên luôn hợp tông brand. */\nexport const avatarPalette = [\n gold[400],\n gold[600],\n accent.light.teal,\n accent.light.navy,\n accent.light.green,\n accent.light.red,\n ash[500],\n brandNeutral.inkSoft\n] as const;\n\n/** Số đo khung ứng dụng — mirror --sidebar-width / --sidebar-width-icon / --header-height. */\nexport const layout = {\n /** px — --header-height 3.5rem */\n headerHeight: 56,\n /** px — --sidebar-width 16rem */\n sidebarWidth: 256,\n /** px — --sidebar-width-icon 3.5rem */\n sidebarCollapsedWidth: 56,\n /** px — --radius 0.375rem */\n radius: 6,\n breakpoints: { mobile: 768, tablet: 1024, desktop: 1280 },\n pageSizes: [20, 50, 100],\n /** px — kích thước tối thiểu của vùng chạm trên mobile (WCAG 2.5.5) */\n minTapTarget: 44\n} as const;\n\n/** Font stack — app phải tự nạp Inter (subsets latin + vietnamese) và gán --font-inter. */\nexport const typography = {\n fontSans: '\"Inter\", system-ui, sans-serif',\n fontMono: '\"Geist Mono\", ui-monospace, monospace'\n} as const;\n\nexport type ThemeMode = \"light\" | \"dark\";\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/tokens.ts"],"sourcesContent":["/**\n * Design token dạng TS object — dành cho nơi KHÔNG có Tailwind: canvas, biểu đồ tuỳ biến,\n * email HTML, xuất PDF, (sau này) React Native.\n *\n * ⚠ Nguồn sự thật của giá trị là `styles.css` (CSS variable). File này chỉ MIRROR hằng số.\n * Sửa màu ở styles.css thì phải sửa kèm ở đây — và ngược lại.\n *\n * Trong môi trường có Tailwind, LUÔN dùng class token (`bg-primary`, `text-ash-500`),\n * đừng đọc hex từ đây rồi nhét vào style inline: làm vậy là mất dark mode.\n *\n * Hợp đồng ba vai trò của màu (giống hệt `styles.css`; bảng đầy đủ ở docs/hop-dong-mau.md),\n * đọc tên là biết dùng ở đâu:\n * `x` → tô NỀN (fill đặc hoặc tint /5–/20 — trần chung an toàn cho mọi tone)\n * `xForeground` → chữ/icon NẰM TRÊN nền `x` ĐẶC — đo với `x`, ngưỡng 4,5:1\n * `xInk` → chính màu đó khi ĐI LÊN nền trang hoặc lên tint của chính nó (chữ, icon\n * rời, viền, vạch active) — đo với nền đó\n *\n * Cố ý KHÔNG mirror (xem docs/quyet-dinh-tich-hop.md §21): `--elevation-*` (chuỗi box-shadow chỉ\n * có nghĩa trong CSS), `--app-accent*` (giá trị thuộc về APP, không phải gói), `--font-*`.\n */\n\nimport { AVATAR_TONES, type AvatarToneColors, type AvatarTonePalette, getAvatarToneIndex } from \"./lib/avatar-tone.js\";\n\n/** Thang GOLD 7 bậc — trục màu chính của brand Comitor. */\nexport const gold = {\n 50: \"#FBF5DC\",\n 100: \"#F8E89D\",\n 200: \"#F5D060\",\n /** primary ở light mode */\n 300: \"#E8B824\",\n 400: \"#B88A00\",\n 500: \"#7A5800\",\n 600: \"#5C4006\"\n} as const;\n\n/** Trung tính brand (khác thang ash — đây là màu \"giấy/mực\" của brand book). */\nexport const brandNeutral = {\n ink: \"#0E1116\",\n inkSoft: \"#3A3F4A\",\n paper: \"#FBF7EE\",\n warm: \"#EDE8D8\"\n} as const;\n\n/**\n * Thang xám Comitor — namespace `ash`, KHÔNG phải `gray`.\n * Chỉ có 6 bậc; các bậc khuyết (200/400/600/800) cố ý không khai để không lẫn với\n * thang gray dựng sẵn của Tailwind (xem chú thích trong styles.css).\n */\nexport const ash = {\n 50: \"#F7F7F8\",\n 100: \"#EDEEF1\",\n 300: \"#C8CBD3\",\n 500: \"#7E8290\",\n 700: \"#3A3F4A\",\n 900: \"#16181D\"\n} as const;\n\n/**\n * Màu phụ — vai trò TÔ NỀN (fill đặc hoặc tint). Bản `dark` được nâng sáng vì trên nền #0E1116\n * bản `light` chìm nghỉm.\n *\n * ⚠ ĐỪNG dùng bảng này làm màu CHỮ ở light: nó được chọn để tô nền nên quá nhạt — dùng\n * `accentInk` ngay dưới. `accent.dark.navy` giữ nguyên #0F1E3D chỉ để mirror đúng `styles.css`\n * (khối `.dark` không override `--navy`); vai trò navy ở dark do `accentInk.dark.navy` gánh.\n */\nexport const accent = {\n light: {\n navy: \"#0F1E3D\",\n teal: \"#1F8A8B\",\n red: \"#D64545\",\n green: \"#2EA56A\",\n yellow: \"#F2C94C\"\n },\n dark: {\n navy: \"#0F1E3D\",\n teal: \"#2DD4BF\",\n red: \"#F87171\",\n green: \"#4ADE80\",\n yellow: \"#FACC15\"\n }\n} as const;\n\n/**\n * Bậc INK của màu phụ — mirror `--teal-ink` / `--green-ink` / `--red-ink` / `--yellow-ink` /\n * `--navy-ink`. Đây là màu phụ khi nó làm **CHỮ/ICON/VIỀN trên nền trang** (hoặc trên tint\n * /5–/20 của chính nó), tức vai trò NGƯỢC với `accent`.\n *\n * Hợp đồng đặt tên của cả gói: `--x` tô nền · `--x-foreground` là chữ NẰM TRÊN nền `--x` ·\n * `--x-ink` là chính màu đó ĐI LÊN nền trang.\n *\n * Bản `light` là bậc tối đã đóng băng (kết quả của `color-mix(in srgb, --<màu>, --ink 30/40%)`\n * mà trước đây mỗi component tự tính lấy); bản `dark` chính là `accent.dark` vì bên đó màu phụ\n * đã đủ sáng để làm chữ. Đo trên tint 15% của chính nó — nền thật mà chữ initials avatar và\n * badge tonal đứng lên: teal 5,54 · green 5,60 · red 5,83:1 ở light (bản chưa có ink: 3,44 /\n * 2,69 / 3,57:1 ✗); 7,12 / 7,44 / 5,18:1 ở dark.\n */\nexport const accentInk = {\n light: {\n navy: accent.light.navy,\n teal: \"#1A6668\",\n red: \"#9A3537\",\n green: \"#216A48\",\n /** = gold[500]: vàng không có bậc tối riêng, mượn thang gold cùng sắc. */\n yellow: gold[500]\n },\n dark: {\n /** navy nguyên bản chìm vào nền dark (1,15:1) → bậc sáng riêng; cũng là `--chart-3` ở dark. */\n navy: \"#7EA2E8\",\n teal: accent.dark.teal,\n red: accent.dark.red,\n green: accent.dark.green,\n yellow: accent.dark.yellow\n }\n} as const;\n\n/**\n * Bậc FOREGROUND của màu phụ — mirror `--teal-foreground` / `--green-foreground` /\n * `--yellow-foreground` / `--navy-foreground`. Đây là chữ/icon nằm **TRÊN nền `accent.*` ĐẶC**,\n * tức vai trò thứ ba, khác hẳn `accentInk` (màu đó đi lên nền TRANG).\n *\n * Cả ba sắc teal/green/yellow đều sáng ở light và còn sáng hơn ở dark → chữ là MỰC ở cả hai\n * theme, một giá trị đủ dùng: teal 4,56:1 (light) / 10,16:1 (dark) · green 6,04 / 10,85 ·\n * yellow 11,92 / 12,35. Navy thì ngược lại — chữ TRẮNG, 16,49:1 ở cả hai theme.\n *\n * ⚠ CỐ Ý KHÔNG có `red`: nền `accent.light.red` #D64545 đặc không mang được chữ ở light — trắng\n * 4,38:1 ✗ và mực 4,32:1 ✗. Nền đỏ đặc có chữ phải dùng `status.*.destructive` (#C93B3B) cùng\n * `destructiveForeground` = 5,03:1 ✓. `accent.*.red` chỉ để tô TINT và làm màu chart.\n */\nexport const accentForeground = {\n light: {\n navy: \"#FFFFFF\",\n teal: \"#0E1116\",\n green: \"#0E1116\",\n yellow: \"#0E1116\"\n },\n dark: {\n navy: \"#FFFFFF\",\n teal: \"#0E1116\",\n green: \"#0E1116\",\n yellow: \"#0E1116\"\n }\n} as const;\n\n/** Semantic token của shadcn — mirror khối `:root` / `.dark` trong styles.css. */\nexport const semantic = {\n light: {\n background: \"#FFFFFF\",\n foreground: \"#0E1116\",\n card: \"#FFFFFF\",\n cardForeground: \"#0E1116\",\n popover: \"#FFFFFF\",\n popoverForeground: \"#0E1116\",\n primary: \"#E8B824\",\n primaryForeground: \"#0E1116\",\n /** Bậc INK của brand — gold khi phải ĐI LÊN nền trang (rãnh Switch khi BẬT, ô Checkbox đã\n * chọn). `primary` #E8B824 chỉ 1,86:1 với nền trắng ✗; bậc này = `gold[500]` = 6,51:1 ✓. */\n primaryInk: gold[500],\n secondary: \"#EDE8D8\",\n secondaryForeground: \"#0E1116\",\n muted: \"#F7F7F8\",\n /** Bậc chữ phụ — là CHỮ nên ngưỡng AA 4,5:1: 5,23:1 trên nền trắng, 4,89:1 trên `muted`.\n * KHÔNG còn bằng `ash[500]` (#7E8290 chỉ 3,83:1 ✗) — bậc bảng màu và token semantic tách nhau. */\n mutedForeground: \"#686C7A\",\n accent: \"#FBF5DC\",\n accentForeground: \"#0E1116\",\n destructive: \"#C93B3B\",\n destructiveForeground: \"#FFFFFF\",\n /** Chữ báo lỗi trên nền trang — KHÁC `destructive` (nền) và `destructiveForeground` (chữ\n * trắng trên nền đó). 7,15:1 trên nền trắng. */\n destructiveInk: accentInk.light.red,\n /** RANH GIỚI TRANG TRÍ (vạch chia, viền thẻ nghỉ) — 1,16:1, cố ý: 1.4.11 không đòi tương\n * phản cho thứ không mang thông tin. Ranh giới của CONTROL đi qua `controlEdge`. */\n border: \"#EDEEF1\",\n /** = `controlEdge`: viền ô nhập là ranh giới nhận dạng control (1.4.11) → 3,83:1 ✓. */\n input: \"#7E8290\",\n ring: \"#E8B824\",\n /** Ranh giới của control lúc NGHỈ (viền Input, ô Checkbox chưa chọn, rãnh Switch khi TẮT) —\n * WCAG 1.4.11, ràng buộc HAI PHÍA. Một giá trị cho cả hai theme: 3,83:1 với nền trắng và\n * 3,83:1 với `controlKnob` (trắng ở light); 4,94:1 với nền dark và với knob #0E1116. */\n controlEdge: \"#7E8290\",\n /** Cùng vai trò nhưng ở HOVER/ACTIVE — phải ĐẬM hơn lúc nghỉ. = `ash[700]`, 10,55:1 trên nền\n * trắng · 9,86:1 trên `muted`. */\n controlEdgeStrong: ash[700],\n /** Nền control khi BẬT/ĐÃ CHỌN (rãnh Switch bật, ô Checkbox đã chọn, chấm Radio) = `primaryInk`.\n * Ràng buộc hai phía: 6,51:1 với nền trang VÀ 6,51:1 với `controlKnob` ở light. `primary`\n * #E8B824 chỉ 1,86:1 cả hai phía ✗. */\n controlOn: gold[500],\n /** Núm Switch / dấu check / chấm Radio — mang màu NỀN TRANG nên luôn tương phản đúng chiều\n * với cả rãnh BẬT (6,51:1) lẫn rãnh TẮT (3,83:1). KHÔNG dùng `primaryForeground` (mực): mực\n * trên `controlOn` #7A5800 chỉ 1,86:1 ✗. */\n controlKnob: \"#FFFFFF\",\n sidebar: \"#FAFAF9\",\n sidebarForeground: \"#0E1116\",\n sidebarBorder: \"#EDEEF1\",\n sidebarAccent: \"#FBF5DC\",\n sidebarAccentForeground: \"#0E1116\"\n },\n dark: {\n background: \"#0E1116\",\n foreground: \"#F7F7F8\",\n card: \"#16181D\",\n cardForeground: \"#F7F7F8\",\n popover: \"#16181D\",\n popoverForeground: \"#F7F7F8\",\n primary: \"#F5D060\",\n primaryForeground: \"#0E1116\",\n /** Ở dark `primary` vốn đã là bậc sáng (12,67:1 trên nền `background`) nên ink = chính nó. */\n primaryInk: \"#F5D060\",\n secondary: \"#1E2128\",\n secondaryForeground: \"#F7F7F8\",\n muted: \"#1E2128\",\n /** Sáng hơn bản light: chữ phụ ở dark hay nằm trên `card`/`muted`. 5,35:1 trên `card`,\n * 4,86:1 trên `muted` (giá trị cũ #7E8290 chỉ 4,21:1 trên `muted` ✗). */\n mutedForeground: \"#888D9B\",\n accent: \"#1E2128\",\n accentForeground: \"#F7F7F8\",\n destructive: \"#C93B3B\",\n destructiveForeground: \"#FFFFFF\",\n /** 6,42:1 trên `card` — `destructive` dùng làm chữ ở dark chỉ được 3,53:1 ✗. */\n destructiveInk: accentInk.dark.red,\n /** TRANG TRÍ, 1,17:1 trên `background` và 1,10:1 trên `card` — cố ý, xem nhánh light. */\n border: \"#1E2128\",\n /** = `controlEdge`, ghim một giá trị cho cả hai theme — 4,94:1 trên `background` ✓. */\n input: \"#7E8290\",\n ring: \"#F5D060\",\n /** Ghim đúng giá trị của light — xem chú thích ở nhánh light. */\n controlEdge: \"#7E8290\",\n /** = `ash[300]` (lật chiều so với light): 11,65:1 trên `background` · 10,94:1 trên `card`. */\n controlEdgeStrong: ash[300],\n /** = `primaryInk` của dark; 12,67:1 với nền trang VÀ với `controlKnob` (#0E1116). */\n controlOn: \"#F5D060\",\n /** = `background` của dark. Núm trắng cứng sẽ chỉ được 1,49:1 với rãnh BẬT ✗. */\n controlKnob: \"#0E1116\",\n sidebar: \"#16181D\",\n sidebarForeground: \"#F7F7F8\",\n sidebarBorder: \"#1E2128\",\n sidebarAccent: \"#1E2128\",\n sidebarAccentForeground: \"#F7F7F8\"\n }\n} as const;\n\n/**\n * Alias trạng thái — success/warning/info trỏ vào màu phụ, destructive giữ nguyên 2 chế độ.\n *\n * Ba lớp KHÁC nhau, đừng gộp:\n * • `destructive` (#C93B3B) — màu NỀN (nút xoá), đủ đậm cho chữ trắng ở cả hai theme (5,03:1);\n * • `destructiveForeground` (#FFFFFF) — chữ NẰM TRÊN nền đó;\n * • `destructiveInk` — màu CHỮ đỏ trên nền trang (dòng lỗi). Dùng nhầm `destructive` cho vai\n * trò này thì ở dark chỉ được 3,53:1, dùng `accent.light.red` thì ở light chỉ 4,38:1.\n *\n * `successForeground` / `warningForeground` / `infoForeground` là MỰC ở CẢ hai theme (không phải\n * trắng ở light rồi lật ở dark như bản 0.1.0): ba nền trạng thái đều là sắc sáng.\n * success mực 6,04:1 (light) · 10,85:1 (dark) [trắng chỉ 3,13 / 1,74:1 ✗]\n * info mực 4,56:1 (light) · 10,16:1 (dark) [trắng chỉ 4,14 / 1,86:1 ✗]\n * warning mực 11,92:1 (light) · 12,35:1 (dark) [trắng chỉ 1,59 / 1,53:1 ✗]\n * ⚠ `info` ở light là cặp chặt nhất gói (4,56:1, dư 0,06) — đổi `accent.light.teal` là phải đo lại.\n */\nexport const status = {\n light: {\n success: accent.light.green,\n successForeground: \"#0E1116\",\n warning: accent.light.yellow,\n warningForeground: \"#0E1116\",\n info: accent.light.teal,\n infoForeground: \"#0E1116\",\n destructive: \"#C93B3B\",\n destructiveForeground: \"#FFFFFF\",\n successInk: accentInk.light.green,\n warningInk: accentInk.light.yellow,\n infoInk: accentInk.light.teal,\n destructiveInk: accentInk.light.red\n },\n dark: {\n success: accent.dark.green,\n successForeground: \"#0E1116\",\n warning: accent.dark.yellow,\n warningForeground: \"#0E1116\",\n info: accent.dark.teal,\n infoForeground: \"#0E1116\",\n destructive: \"#C93B3B\",\n destructiveForeground: \"#FFFFFF\",\n successInk: accentInk.dark.green,\n warningInk: accentInk.dark.yellow,\n infoInk: accentInk.dark.teal,\n destructiveInk: accentInk.dark.red\n }\n} as const;\n\n/** Bảng màu biểu đồ (chart-1…chart-5). Thứ tự = thứ tự series mặc định. */\nexport const chart = {\n light: [gold[300], accent.light.teal, accent.light.navy, accent.light.green, accent.light.red],\n dark: [gold[200], accent.dark.teal, accentInk.dark.navy, accent.dark.green, accent.dark.red]\n} as const;\n\n/**\n * Màu avatar chữ cái (hex đục) cho nơi KHÔNG có Tailwind: canvas, email HTML, PDF.\n *\n * ⚠ KHÔNG phải bảng màu thứ hai. Bảng tone DUY NHẤT của cả gói nằm ở `lib/avatar-tone.ts`, mỗi\n * tone mang cả chuỗi class (cho DOM) lẫn công thức hex tương ứng; ở đây chỉ nạp bảng màu vào công\n * thức đó. Web và email vì vậy dùng chung một thứ tự tone, một hàm băm, một cách chuẩn hoá tên —\n * không còn hai bảng để lệch nhau.\n */\nconst avatarTonePalette = {\n light: {\n gold,\n ash,\n brandNeutral,\n accent: accent.light,\n accentInk: accentInk.light,\n card: semantic.light.card\n },\n dark: {\n gold,\n ash,\n brandNeutral,\n accent: accent.dark,\n accentInk: accentInk.dark,\n card: semantic.dark.card\n }\n} satisfies Record<ThemeMode, AvatarTonePalette>;\n\n/**\n * Cặp nền/chữ (hex) của avatar cho một tên, theo theme.\n *\n * Tên hàm nói đúng thứ nó trả về: `...Colors` = hex (canvas/email/PDF), còn bản trả về chuỗi\n * class Tailwind cho DOM là `getAvatarToneClasses` ở `@comitor/ui`. Cùng một tên người luôn ra\n * cùng một tone ở cả hai đường vì chỉ số do MỘT hàm tính (`getAvatarToneIndex`).\n */\nexport function getAvatarToneColors(name: string, mode: ThemeMode = \"light\"): AvatarToneColors {\n // `noUncheckedIndexedAccess` bật → phải có nhánh lùi, dù chỉ số luôn nằm trong [0, 8).\n const tone = AVATAR_TONES[getAvatarToneIndex(name)] ?? AVATAR_TONES[0];\n return tone[mode](avatarTonePalette[mode]);\n}\n\nexport type { AvatarToneColors };\nexport { getAvatarToneIndex };\n\n/** Số đo khung ứng dụng — mirror --sidebar-width / --sidebar-width-icon / --header-height. */\nexport const layout = {\n /** px — --header-height 3.5rem */\n headerHeight: 56,\n /** px — --sidebar-width 16rem */\n sidebarWidth: 256,\n /** px — --sidebar-width-icon 3.5rem */\n sidebarCollapsedWidth: 56,\n /** px — --radius 0.375rem */\n radius: 6,\n breakpoints: { mobile: 768, tablet: 1024, desktop: 1280 },\n /** px — kích thước tối thiểu của vùng chạm trên mobile (WCAG 2.5.5) */\n minTapTarget: 44\n} as const;\n\n/** Font stack — app phải tự nạp Inter (subsets latin + vietnamese) và gán --font-inter. */\nexport const typography = {\n fontSans: '\"Inter\", system-ui, sans-serif',\n fontMono: '\"Geist Mono\", ui-monospace, monospace'\n} as const;\n\nexport type ThemeMode = \"light\" | \"dark\";\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comitor/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Comitor design system — Shadcn/UI tùy biến theo brand Comitor + component dùng chung cho hệ sinh thái SaaS (Tasks, Chat, CRM, HR…)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -62,9 +62,11 @@
|
|
|
62
62
|
"typecheck": "tsc --noEmit",
|
|
63
63
|
"lint": "biome check .",
|
|
64
64
|
"lint:fix": "biome check --write .",
|
|
65
|
+
"lint:a11y": "node scripts/check-contrast.mjs",
|
|
66
|
+
"test:cong-a11y": "node scripts/a11y/tu-kiem.mjs",
|
|
65
67
|
"verify:directives": "node scripts/verify-directives.mjs",
|
|
66
68
|
"check:exports": "publint --strict && attw --pack . --profile esm-only --exclude-entrypoints styles.css",
|
|
67
|
-
"prepublishOnly": "pnpm run typecheck && pnpm run build && pnpm run verify:directives"
|
|
69
|
+
"prepublishOnly": "pnpm run typecheck && pnpm run build && pnpm run verify:directives && pnpm run test:cong-a11y && pnpm run lint:a11y"
|
|
68
70
|
},
|
|
69
71
|
"peerDependencies": {
|
|
70
72
|
"lucide-react": ">=0.540.0",
|