@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.
Files changed (146) hide show
  1. package/README.md +222 -41
  2. package/dist/components/composite/avatar-group.js +1 -1
  3. package/dist/components/composite/avatar-group.js.map +1 -1
  4. package/dist/components/composite/combobox.js +2 -2
  5. package/dist/components/composite/combobox.js.map +1 -1
  6. package/dist/components/composite/confirm-dialog.js +1 -1
  7. package/dist/components/composite/confirm-dialog.js.map +1 -1
  8. package/dist/components/composite/copy-button.js +1 -1
  9. package/dist/components/composite/copy-button.js.map +1 -1
  10. package/dist/components/composite/data-table.d.ts +17 -8
  11. package/dist/components/composite/data-table.js +8 -8
  12. package/dist/components/composite/data-table.js.map +1 -1
  13. package/dist/components/composite/filter-chips.js +6 -5
  14. package/dist/components/composite/filter-chips.js.map +1 -1
  15. package/dist/components/composite/form-field.js +3 -1
  16. package/dist/components/composite/form-field.js.map +1 -1
  17. package/dist/components/composite/form-section.js +5 -5
  18. package/dist/components/composite/form-section.js.map +1 -1
  19. package/dist/components/composite/inline-edit.js +1 -1
  20. package/dist/components/composite/inline-edit.js.map +1 -1
  21. package/dist/components/composite/keyboard-hint.d.ts +16 -0
  22. package/dist/components/composite/keyboard-hint.js +8 -6
  23. package/dist/components/composite/keyboard-hint.js.map +1 -1
  24. package/dist/components/composite/letter-avatar.d.ts +10 -18
  25. package/dist/components/composite/letter-avatar.js +6 -17
  26. package/dist/components/composite/letter-avatar.js.map +1 -1
  27. package/dist/components/composite/search-filter-bar.d.ts +41 -3
  28. package/dist/components/composite/search-filter-bar.js +27 -15
  29. package/dist/components/composite/search-filter-bar.js.map +1 -1
  30. package/dist/components/composite/stat-card.d.ts +16 -1
  31. package/dist/components/composite/stat-card.js +16 -9
  32. package/dist/components/composite/stat-card.js.map +1 -1
  33. package/dist/components/composite/status-config.d.ts +69 -16
  34. package/dist/components/composite/status-config.js +10 -10
  35. package/dist/components/composite/status-config.js.map +1 -1
  36. package/dist/components/composite/status-pill.js +5 -1
  37. package/dist/components/composite/status-pill.js.map +1 -1
  38. package/dist/components/composite/table-pagination.d.ts +46 -4
  39. package/dist/components/composite/table-pagination.js +27 -24
  40. package/dist/components/composite/table-pagination.js.map +1 -1
  41. package/dist/components/composite/text-utils.d.ts +12 -11
  42. package/dist/components/composite/text-utils.js +7 -16
  43. package/dist/components/composite/text-utils.js.map +1 -1
  44. package/dist/components/composite/toast.js +16 -6
  45. package/dist/components/composite/toast.js.map +1 -1
  46. package/dist/components/ui/alert.js +1 -1
  47. package/dist/components/ui/alert.js.map +1 -1
  48. package/dist/components/ui/badge.js +4 -4
  49. package/dist/components/ui/badge.js.map +1 -1
  50. package/dist/components/ui/button.js +2 -2
  51. package/dist/components/ui/button.js.map +1 -1
  52. package/dist/components/ui/calendar.d.ts +41 -0
  53. package/dist/components/ui/calendar.js +1 -1
  54. package/dist/components/ui/calendar.js.map +1 -1
  55. package/dist/components/ui/checkbox.js +3 -3
  56. package/dist/components/ui/checkbox.js.map +1 -1
  57. package/dist/components/ui/command.js +1 -1
  58. package/dist/components/ui/command.js.map +1 -1
  59. package/dist/components/ui/context-menu.js +1 -1
  60. package/dist/components/ui/context-menu.js.map +1 -1
  61. package/dist/components/ui/drawer.js +1 -1
  62. package/dist/components/ui/drawer.js.map +1 -1
  63. package/dist/components/ui/dropdown-menu.js +1 -1
  64. package/dist/components/ui/dropdown-menu.js.map +1 -1
  65. package/dist/components/ui/empty.js +1 -1
  66. package/dist/components/ui/empty.js.map +1 -1
  67. package/dist/components/ui/field.js +4 -4
  68. package/dist/components/ui/field.js.map +1 -1
  69. package/dist/components/ui/form.js +8 -2
  70. package/dist/components/ui/form.js.map +1 -1
  71. package/dist/components/ui/input-group.js +3 -3
  72. package/dist/components/ui/input-group.js.map +1 -1
  73. package/dist/components/ui/input-otp.js +1 -1
  74. package/dist/components/ui/input-otp.js.map +1 -1
  75. package/dist/components/ui/input.js +3 -3
  76. package/dist/components/ui/input.js.map +1 -1
  77. package/dist/components/ui/item.js +1 -1
  78. package/dist/components/ui/item.js.map +1 -1
  79. package/dist/components/ui/menubar.js +1 -1
  80. package/dist/components/ui/menubar.js.map +1 -1
  81. package/dist/components/ui/navigation-menu.js +1 -1
  82. package/dist/components/ui/navigation-menu.js.map +1 -1
  83. package/dist/components/ui/pagination.js +1 -1
  84. package/dist/components/ui/pagination.js.map +1 -1
  85. package/dist/components/ui/progress.d.ts +26 -0
  86. package/dist/components/ui/progress.js +5 -2
  87. package/dist/components/ui/progress.js.map +1 -1
  88. package/dist/components/ui/radio-group.js +2 -2
  89. package/dist/components/ui/radio-group.js.map +1 -1
  90. package/dist/components/ui/resizable.d.ts +14 -0
  91. package/dist/components/ui/resizable.js +2 -2
  92. package/dist/components/ui/resizable.js.map +1 -1
  93. package/dist/components/ui/scroll-area.js +1 -1
  94. package/dist/components/ui/scroll-area.js.map +1 -1
  95. package/dist/components/ui/select.d.ts +33 -0
  96. package/dist/components/ui/select.js +3 -3
  97. package/dist/components/ui/select.js.map +1 -1
  98. package/dist/components/ui/slider.d.ts +19 -0
  99. package/dist/components/ui/slider.js +3 -3
  100. package/dist/components/ui/slider.js.map +1 -1
  101. package/dist/components/ui/sonner.d.ts +21 -3
  102. package/dist/components/ui/sonner.js +1 -2
  103. package/dist/components/ui/sonner.js.map +1 -1
  104. package/dist/components/ui/switch.js +2 -2
  105. package/dist/components/ui/switch.js.map +1 -1
  106. package/dist/components/ui/table.d.ts +16 -0
  107. package/dist/components/ui/table.js +1 -1
  108. package/dist/components/ui/table.js.map +1 -1
  109. package/dist/components/ui/tabs.js +1 -1
  110. package/dist/components/ui/tabs.js.map +1 -1
  111. package/dist/components/ui/textarea.js +1 -1
  112. package/dist/components/ui/textarea.js.map +1 -1
  113. package/dist/components/ui/toggle.js +1 -1
  114. package/dist/components/ui/toggle.js.map +1 -1
  115. package/dist/index.d.ts +6 -5
  116. package/dist/index.js +17 -2
  117. package/dist/index.js.map +1 -1
  118. package/dist/lib/avatar-tone.d.ts +126 -0
  119. package/dist/lib/avatar-tone.js +68 -0
  120. package/dist/lib/avatar-tone.js.map +1 -0
  121. package/dist/shell/app-header.js +12 -2
  122. package/dist/shell/app-header.js.map +1 -1
  123. package/dist/shell/app-launcher.js +2 -2
  124. package/dist/shell/app-launcher.js.map +1 -1
  125. package/dist/shell/index.d.ts +3 -3
  126. package/dist/shell/shell-context.d.ts +1 -7
  127. package/dist/shell/shell-context.js +1 -19
  128. package/dist/shell/shell-context.js.map +1 -1
  129. package/dist/shell/sidebar-group.js +5 -3
  130. package/dist/shell/sidebar-group.js.map +1 -1
  131. package/dist/shell/sidebar-menu-item.js +12 -4
  132. package/dist/shell/sidebar-menu-item.js.map +1 -1
  133. package/dist/shell/types.d.ts +17 -1
  134. package/dist/shell/types.js.map +1 -1
  135. package/dist/shell/user-menu.js +3 -2
  136. package/dist/shell/user-menu.js.map +1 -1
  137. package/dist/shell/utils.d.ts +10 -22
  138. package/dist/shell/utils.js +3 -11
  139. package/dist/shell/utils.js.map +1 -1
  140. package/dist/shell/workspace-switcher.js +23 -8
  141. package/dist/shell/workspace-switcher.js.map +1 -1
  142. package/dist/tokens.d.ts +165 -17
  143. package/dist/tokens.js +122 -25
  144. package/dist/tokens.js.map +1 -1
  145. package/package.json +4 -2
  146. 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 phụ. Bản `dark` được nâng sáng để đạt tương phản AA khi làm CHỮ trên nền #0E1116 —
44
- * bản `light` quá tối cho việc đó.
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
- readonly mutedForeground: "#7E8290";
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: "#D64545";
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
- readonly input: "#EDEEF1";
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
- readonly mutedForeground: "#7E8290";
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: "#D64545";
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
- readonly input: "#1E2128";
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
- /** Alias trạng thái — success/warning/info trỏ vào màu phụ, destructive giữ nguyên 2 chế độ. */
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: "#FFFFFF";
255
+ readonly successForeground: "#0E1116";
122
256
  readonly warning: "#F2C94C";
123
257
  readonly warningForeground: "#0E1116";
124
258
  readonly info: "#1F8A8B";
125
- readonly infoForeground: "#FFFFFF";
126
- readonly destructive: "#D64545";
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: "#D64545";
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
- /** 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. */
146
- declare const avatarPalette: readonly ["#B88A00", "#5C4006", "#1F8A8B", "#0F1E3D", "#2EA56A", "#D64545", "#7E8290", "#3A3F4A"];
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, ash, avatarPalette, brandNeutral, chart, gold, layout, semantic, status, typography };
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
- mutedForeground: "#7E8290",
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: "#D64545",
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
- input: "#EDEEF1",
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
- mutedForeground: "#7E8290",
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: "#D64545",
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
- input: "#1E2128",
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: "#FFFFFF",
173
+ successForeground: "#0E1116",
99
174
  warning: accent.light.yellow,
100
175
  warningForeground: "#0E1116",
101
176
  info: accent.light.teal,
102
- infoForeground: "#FFFFFF",
103
- destructive: "#D64545",
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: "#D64545",
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, "#7EA2E8", accent.dark.green, accent.dark.red]
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
- const avatarPalette = [
122
- gold[400],
123
- gold[600],
124
- accent.light.teal,
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,
@@ -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.1.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",