@vestow/theme 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -0
- package/css/README.md +90 -0
- package/css/app.css +100 -0
- package/css/bases.css +86 -0
- package/css/charts.css +152 -0
- package/css/components/button.css +143 -0
- package/css/index.css +63 -0
- package/css/menus.css +165 -0
- package/css/palettes.css +292 -0
- package/css/primitives.css +224 -0
- package/css/styles.css +203 -0
- package/css/surfaces.css +364 -0
- package/css/tailwind.css +163 -0
- package/css/tokens.css +627 -0
- package/css/typeset.css +274 -0
- package/dist/controller.d.ts +56 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +256 -0
- package/dist/controller.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/preset.d.ts +90 -0
- package/dist/preset.d.ts.map +1 -0
- package/dist/preset.js +86 -0
- package/dist/preset.js.map +1 -0
- package/dist/registry.d.ts +252 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +238 -0
- package/dist/registry.js.map +1 -0
- package/dist/token-names.d.ts +512 -0
- package/dist/token-names.d.ts.map +1 -0
- package/dist/token-names.js +7 -0
- package/dist/token-names.js.map +1 -0
- package/package.json +40 -0
package/css/tokens.css
ADDED
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
/* =============================================================================
|
|
2
|
+
Design tokens — nguồn duy nhất của màu sắc / bo tròn / spacing ngữ nghĩa.
|
|
3
|
+
KHÔNG hardcode màu trong component. Mọi component chỉ dùng utility Tailwind
|
|
4
|
+
trỏ tới các token dưới đây (vd: bg-background, text-muted-foreground).
|
|
5
|
+
|
|
6
|
+
Hệ màu dùng oklch: nội suy đều hơn hsl, chỉnh độ sáng không lệch tone.
|
|
7
|
+
|
|
8
|
+
File này là CSS THUẦN — không `@theme`, không `@custom-variant`, không
|
|
9
|
+
`@apply`. Trình duyệt nào cũng đọc được, framework nào cũng nạp được.
|
|
10
|
+
Phần cầu nối sang Tailwind v4 nằm riêng ở `tailwind.css`; đừng thêm at-rule
|
|
11
|
+
của Tailwind vào đây, nếu không woodblock-angular (không có Tailwind) sẽ
|
|
12
|
+
không dùng được bộ token này nữa.
|
|
13
|
+
|
|
14
|
+
Các nấc màu gốc (`--color-slate-900`…) đến từ `primitives.css` — file tự
|
|
15
|
+
sinh, chép đúng giá trị của bảng màu Tailwind. Xem `scripts/gen-primitives.mjs`.
|
|
16
|
+
========================================================================== */
|
|
17
|
+
/* `.light` đi kèm `:root` để có thể ép một VÙNG về theme sáng — trang docs cần
|
|
18
|
+
hiện màu của cả hai theme cạnh nhau. `.dark` vốn đã là class nên dùng được
|
|
19
|
+
ngay; light thì trước đây chỉ có ở :root, không scope được. */
|
|
20
|
+
:root,
|
|
21
|
+
.light {
|
|
22
|
+
/* --- radius --- */
|
|
23
|
+
--radius: 0.625rem;
|
|
24
|
+
|
|
25
|
+
/* --- ám màu mặt nền ---
|
|
26
|
+
Nấc màu gốc chỉ còn góp ĐỘ SÁNG; chroma và hue của cả nhóm trung tính do
|
|
27
|
+
hai token dưới đây quyết định, và mỗi bảng màu khai đè `--tint-hue` của
|
|
28
|
+
riêng nó.
|
|
29
|
+
|
|
30
|
+
Vì sao không chỉ chọn một họ trung tính có sẵn: Tailwind chỉ cho HAI họ có
|
|
31
|
+
sắc thật — slate 0,042 và gray 0,034 — và cả hai đều ở hue xanh lam ~265.
|
|
32
|
+
Không có bản cam, tím hay teal tương đương, nên Solar/Nebula/Aurora dùng họ
|
|
33
|
+
nào cũng chỉ tới 0,006–0,011, tức nhạt gấp bốn tới bảy lần bảng mặc định.
|
|
34
|
+
|
|
35
|
+
Và KHÔNG lấy hue từ chính nấc gốc (`oklch(from … l var(…) h)`): ở chroma
|
|
36
|
+
0,001 thì hue là số vô nghĩa — `stone-50` khai hue 106, tức OLIVE. Nâng
|
|
37
|
+
chroma lên mà giữ hue đó là mặt nền Solar ra xanh lá. Hue phải khai tay.
|
|
38
|
+
|
|
39
|
+
Đường cong chroma dưới đây chép đúng của thang slate, nên Hyperion giữ
|
|
40
|
+
nguyên mọi giá trị (chỉ hue phẳng lại: thang slate vốn trôi 248 -> 266 dọc
|
|
41
|
+
ramp, ở chroma này lệch đó không nhìn ra). Bảng nào không với tới trần sRGB
|
|
42
|
+
thì kẹp ở 85% trần và khai đè tại chỗ — chỉ Solar (950) và Aurora (900,
|
|
43
|
+
950) rơi vào diện đó, xem palettes.css. */
|
|
44
|
+
--tint-hue: 265.755;
|
|
45
|
+
/* Hệ số cho cả thang, để một bảng tắt hẳn trục này mà không phải khai đè
|
|
46
|
+
chín con số. Eclipse là ca duy nhất: chroma 0 chính là định nghĩa của nó,
|
|
47
|
+
mà nó KHÔNG tự khai hết mọi mặt nền — `surfaces.css` ở theme kính khai đè
|
|
48
|
+
--card/--popover/--sidebar/--code-bg, nên thiếu hệ số này thì bật kính lên
|
|
49
|
+
là Eclipse ám xanh lam theo slate. */
|
|
50
|
+
--tint-scale: 1;
|
|
51
|
+
--tint-c-50: calc(0.003 * var(--tint-scale));
|
|
52
|
+
--tint-c-100: calc(0.007 * var(--tint-scale));
|
|
53
|
+
--tint-c-200: calc(0.013 * var(--tint-scale));
|
|
54
|
+
--tint-c-300: calc(0.022 * var(--tint-scale));
|
|
55
|
+
--tint-c-400: calc(0.04 * var(--tint-scale));
|
|
56
|
+
--tint-c-500: calc(0.046 * var(--tint-scale));
|
|
57
|
+
--tint-c-700: calc(0.044 * var(--tint-scale));
|
|
58
|
+
--tint-c-800: calc(0.041 * var(--tint-scale));
|
|
59
|
+
--tint-c-900: calc(0.042 * var(--tint-scale));
|
|
60
|
+
--tint-c-950: calc(0.042 * var(--tint-scale));
|
|
61
|
+
|
|
62
|
+
/* --- surface --- */
|
|
63
|
+
--background: var(--color-white);
|
|
64
|
+
--foreground: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
|
|
65
|
+
|
|
66
|
+
--card: var(--color-white);
|
|
67
|
+
--card-foreground: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
|
|
68
|
+
|
|
69
|
+
--popover: var(--color-white);
|
|
70
|
+
--popover-foreground: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
|
|
71
|
+
|
|
72
|
+
/* --- brand --- */
|
|
73
|
+
--primary: var(--color-blue-600);
|
|
74
|
+
--primary-foreground: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
|
|
75
|
+
|
|
76
|
+
--secondary: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
77
|
+
--secondary-foreground: oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue));
|
|
78
|
+
|
|
79
|
+
--muted: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
80
|
+
--muted-foreground: oklch(from var(--color-slate-500) l var(--tint-c-500) var(--tint-hue));
|
|
81
|
+
|
|
82
|
+
--accent: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
83
|
+
/* Highlight của MỤC MENU đang trỏ tới. Tách khỏi `--accent` vì trục
|
|
84
|
+
`data-menu-accent` phải đổi được riêng phần menu: `--accent` còn được
|
|
85
|
+
Sidebar, Calendar và vài chỗ hover khác đọc, đổi thẳng nó là đổi cả những
|
|
86
|
+
chỗ không ai xin. Mặc định trỏ vào `--accent` nên không đổi gì sẵn có. */
|
|
87
|
+
--menu-accent: var(--accent);
|
|
88
|
+
--menu-accent-foreground: var(--accent-foreground);
|
|
89
|
+
/* Mặt nền của tấm menu. Tách khỏi `--popover` vì trục `data-menu-appearance`
|
|
90
|
+
phải hạ được độ đục của RIÊNG menu — Popover và Tooltip cũng đọc
|
|
91
|
+
`--popover` thì không được đi theo. Mặc định trỏ thẳng vào nó nên không đổi
|
|
92
|
+
gì sẵn có.
|
|
93
|
+
|
|
94
|
+
Không tự trộn được từ chính `--popover` trong khối `translucent` nếu dùng
|
|
95
|
+
chung một tên: custom property tham chiếu chính nó trên CÙNG phần tử là một
|
|
96
|
+
vòng lặp, CSS coi cả hai là không hợp lệ. Hai tên thì không. */
|
|
97
|
+
--menu-surface: var(--popover);
|
|
98
|
+
--accent-foreground: oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue));
|
|
99
|
+
|
|
100
|
+
/* --- status: admin cần đủ 4 trạng thái, không chỉ destructive ---
|
|
101
|
+
Các cặp nền/chữ ở đây đều đã ĐO đạt WCAG AA (>= 4.5:1). Đổi một trong hai
|
|
102
|
+
thì đo lại, đừng chỉnh theo cảm giác: --success và --info từng là 0.6/0.62
|
|
103
|
+
và chỉ đạt 3.57 / 3.36 với chữ trắng. */
|
|
104
|
+
--destructive: var(--color-red-600);
|
|
105
|
+
--destructive-foreground: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
|
|
106
|
+
|
|
107
|
+
--success: var(--color-green-700);
|
|
108
|
+
--success-foreground: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
|
|
109
|
+
|
|
110
|
+
/* NGOẠI LỆ về màu CHỮ, không phải về việc neo: nó vẫn trỏ vào một nấc như
|
|
111
|
+
mọi token khác (luật 3 của check-tokens cũng không cho phép literal).
|
|
112
|
+
Chỗ khác biệt là bốn sắc kia đặt chữ TRẮNG lên nền, còn vàng thì không
|
|
113
|
+
nấc amber/yellow nào vừa đủ tối cho chữ trắng vừa còn ra màu vàng — sớm
|
|
114
|
+
nhất là amber-700, đã ngả nâu. Vàng vốn là màu sáng nên nó dùng chữ TỐI. */
|
|
115
|
+
--warning: var(--color-amber-500);
|
|
116
|
+
--warning-foreground: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
|
|
117
|
+
|
|
118
|
+
--info: var(--color-cyan-700);
|
|
119
|
+
--info-foreground: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
|
|
120
|
+
|
|
121
|
+
/* --- soft: nền pha loãng + chữ giữ nguyên sắc, kiểu "soft-danger" của Bootstrap.
|
|
122
|
+
Để trong suốt chứ không trộn với màu nền cụ thể: chip soft nằm trên card, trên
|
|
123
|
+
nền trang, và cả trong Alert đã có tint — trong suốt thì tự hoà theo mặt dưới.
|
|
124
|
+
Khai riêng cho sáng/tối vì modifier kiểu `bg-x/10` chỉ được một con số cho cả
|
|
125
|
+
hai theme, mà nền tối nuốt tint nên cần đậm gần gấp đôi. */
|
|
126
|
+
/* Chữ soft: hạ độ sáng và ĐẨY chroma lên 1.2×. Pha với đen như trước vừa
|
|
127
|
+
tối vừa làm mất độ tươi (chroma 0.245 -> 0.17) nên nhìn đục.
|
|
128
|
+
Vàng phải hạ sâu hơn hẳn (0.24 so với 0.08) vì sắc vàng vốn sáng. */
|
|
129
|
+
|
|
130
|
+
/* Nền của nút, tách khỏi --x để theme kính hạ được độ đục mà không kéo theo
|
|
131
|
+
`text-primary`, `border-primary`, `ring-primary`. Mặc định là màu đặc. */
|
|
132
|
+
--primary-fill: var(--primary);
|
|
133
|
+
--primary-fill-hover: color-mix(in oklab, var(--primary) 90%, transparent);
|
|
134
|
+
--secondary-fill: var(--secondary);
|
|
135
|
+
--secondary-fill-hover: color-mix(in oklab, var(--secondary) 90%, transparent);
|
|
136
|
+
--destructive-fill: var(--destructive);
|
|
137
|
+
--destructive-fill-hover: color-mix(in oklab, var(--destructive) 90%, transparent);
|
|
138
|
+
--success-fill: var(--success);
|
|
139
|
+
--success-fill-hover: color-mix(in oklab, var(--success) 90%, transparent);
|
|
140
|
+
--warning-fill: var(--warning);
|
|
141
|
+
--warning-fill-hover: color-mix(in oklab, var(--warning) 90%, transparent);
|
|
142
|
+
--info-fill: var(--info);
|
|
143
|
+
--info-fill-hover: color-mix(in oklab, var(--info) 90%, transparent);
|
|
144
|
+
|
|
145
|
+
/* Ba mặt của thanh trượt, tách khỏi --muted / --primary / --background vì
|
|
146
|
+
theme kính phải hạ độ đục của RIÊNG chúng: `bg-muted` thì đã trong mờ sẵn,
|
|
147
|
+
nhưng `bg-primary` và `bg-background` vẫn đục kín, nên phần đã chọn và cái
|
|
148
|
+
núm là hai mảng đặc nằm giữa một giao diện kính. Mặc định là màu đặc,
|
|
149
|
+
surfaces.css khai đè khi bật data-surface="glass". */
|
|
150
|
+
--slider-track: var(--muted);
|
|
151
|
+
--slider-range: var(--primary);
|
|
152
|
+
--slider-thumb: var(--background);
|
|
153
|
+
--slider-thumb-border: var(--primary);
|
|
154
|
+
|
|
155
|
+
--primary-soft: color-mix(in oklab, var(--primary) 14%, var(--control-base));
|
|
156
|
+
--primary-soft-border: color-mix(in oklab, var(--primary) 26%, var(--control-base));
|
|
157
|
+
--primary-soft-foreground: oklch(from var(--primary) calc(l - 0.08) calc(c * 1.2) h);
|
|
158
|
+
--primary-soft-hover: color-mix(in oklab, var(--primary) 20%, var(--control-base));
|
|
159
|
+
|
|
160
|
+
--destructive-soft: color-mix(in oklab, var(--destructive) 14%, var(--control-base));
|
|
161
|
+
--destructive-soft-border: color-mix(in oklab, var(--destructive) 26%, var(--control-base));
|
|
162
|
+
--destructive-soft-foreground: oklch(from var(--destructive) calc(l - 0.14) calc(c * 1.2) h);
|
|
163
|
+
--destructive-soft-hover: color-mix(in oklab, var(--destructive) 20%, var(--control-base));
|
|
164
|
+
|
|
165
|
+
--success-soft: color-mix(in oklab, var(--success) 14%, var(--control-base));
|
|
166
|
+
--success-soft-border: color-mix(in oklab, var(--success) 26%, var(--control-base));
|
|
167
|
+
--success-soft-foreground: oklch(from var(--success) calc(l - 0.08) calc(c * 1.2) h);
|
|
168
|
+
--success-soft-hover: color-mix(in oklab, var(--success) 20%, var(--control-base));
|
|
169
|
+
|
|
170
|
+
/* Chữ soft ở chế độ sáng phải tối hơn màu gốc — ĐO mới biết: để nguyên
|
|
171
|
+
var(--x) thì primary 3.4:1, success 3.2:1, destructive 4.4:1, trượt hết AA.
|
|
172
|
+
Pha với đen theo tỉ lệ nên nó vẫn đổi đúng theo bảng màu đang chọn. */
|
|
173
|
+
--warning-soft: color-mix(in oklab, var(--warning) 14%, var(--control-base));
|
|
174
|
+
--warning-soft-border: color-mix(in oklab, var(--warning) 30%, var(--control-base));
|
|
175
|
+
--warning-soft-foreground: oklch(from var(--warning) calc(l - 0.24) calc(c * 1.2) h);
|
|
176
|
+
--warning-soft-hover: color-mix(in oklab, var(--warning) 22%, var(--control-base));
|
|
177
|
+
|
|
178
|
+
--info-soft: color-mix(in oklab, var(--info) 14%, var(--control-base));
|
|
179
|
+
--info-soft-border: color-mix(in oklab, var(--info) 26%, var(--control-base));
|
|
180
|
+
--info-soft-foreground: oklch(from var(--info) calc(l - 0.08) calc(c * 1.2) h);
|
|
181
|
+
--info-soft-hover: color-mix(in oklab, var(--info) 20%, var(--control-base));
|
|
182
|
+
|
|
183
|
+
/* Bề mặt: mặc định không lọc gì, không nền gì.
|
|
184
|
+
surfaces.css bật cả hai lên khi chọn glass. */
|
|
185
|
+
/* Nền mà các tint soft tô đè lên. `transparent` nghĩa là soft ăn theo mặt
|
|
186
|
+
nền phía sau — đúng ở theme thường, nhưng ở glass thì phía sau là gradient
|
|
187
|
+
sáng nên chữ soft mất tương phản. surfaces.css đổi nó thành màu đục. */
|
|
188
|
+
--control-base: transparent;
|
|
189
|
+
|
|
190
|
+
/* Hai mặt của vật liệu kính, dùng bởi surfaces.css khi bật
|
|
191
|
+
data-surface="glass": viền sáng ở mép, và vệt sáng bên trong tạo cảm giác
|
|
192
|
+
tấm kính có ĐỘ DÀY. Thiếu cái thứ hai thì bề mặt chỉ là một ô mờ phẳng.
|
|
193
|
+
|
|
194
|
+
Luôn trắng, cố tình KHÔNG đổi theo sáng/tối: đây là mặt phản sáng của vật
|
|
195
|
+
liệu, không phải màu chữ.
|
|
196
|
+
|
|
197
|
+
Từng có cả một BỘ nút kính đi kèm ở đây — nền, nền hover, chữ, và một style
|
|
198
|
+
"shade" nền tối, nhân thêm cho năm sắc màu. Đã gỡ: kính là vật liệu của BỀ
|
|
199
|
+
MẶT (`data-surface`), không phải một biến thể của nút, nên mọi nút tự thành
|
|
200
|
+
kính mà không nút nào phải khai gì.
|
|
201
|
+
|
|
202
|
+
Con số đáng giữ lại từ đợt đó, vì nó giải thích vì sao KHÔNG nên dựng lại:
|
|
203
|
+
chữ trắng trên kính trong suốt đo được 10,7:1 ở chỗ ảnh tối nhưng chỉ
|
|
204
|
+
**1,35:1** ở chỗ sáng, và blur không cứu được (pixel xấu nhất
|
|
205
|
+
187,224,224 → 185,224,224). Trách nhiệm tương phản thuộc về MẶT NỀN chứ
|
|
206
|
+
không phải cái nút: media phải đủ tối, hoặc phủ scrim lên media. Nút không
|
|
207
|
+
tự biết phía sau nó là gì. */
|
|
208
|
+
--glass-border: color-mix(in oklab, var(--color-white) 30%, transparent);
|
|
209
|
+
--glass-highlight: color-mix(in oklab, var(--color-white) 22%, transparent);
|
|
210
|
+
|
|
211
|
+
--surface-filter: none;
|
|
212
|
+
--surface-backdrop: none;
|
|
213
|
+
/* Nền header dính trên cùng. Là token để theme glass hạ độ đục xuống được —
|
|
214
|
+
88% đục thì kính ở dưới trông rất lệch so với phần thân trang. */
|
|
215
|
+
--header-surface: color-mix(in oklab, var(--background) 88%, transparent);
|
|
216
|
+
|
|
217
|
+
/* --- line / focus --- */
|
|
218
|
+
--border: oklch(from var(--color-slate-200) l var(--tint-c-200) var(--tint-hue));
|
|
219
|
+
--input: oklch(from var(--color-slate-200) l var(--tint-c-200) var(--tint-hue));
|
|
220
|
+
--ring: var(--color-blue-600);
|
|
221
|
+
|
|
222
|
+
/* Vòng focus HAI LỚP, dùng chung cho mọi control nhập liệu.
|
|
223
|
+
|
|
224
|
+
Lớp trong ĐẶC và lớp ngoài loãng, không phải hai lớp loãng: viền 1px đổi
|
|
225
|
+
sang `--ring` cộng lớp trong 1px là chỉ báo focus dày 2px liền mạch —
|
|
226
|
+
đúng mức WCAG 2.4.13 đòi. Lớp ngoài chỉ là quầng trang trí, hạ nó xuống
|
|
227
|
+
dưới ngưỡng tương phản cũng không mất chỉ báo.
|
|
228
|
+
|
|
229
|
+
Bản không hợp lệ đổi sắc chứ KHÔNG bỏ quầng: bỏ đi thì field sai lại là
|
|
230
|
+
field khó thấy đang focus nhất. */
|
|
231
|
+
--focus-ring: 0 0 0 1px var(--ring), 0 0 0 4px color-mix(in oklab, var(--ring) 18%, transparent);
|
|
232
|
+
--focus-ring-invalid: 0 0 0 1px var(--destructive), 0 0 0 4px color-mix(in oklab, var(--destructive) 18%, transparent);
|
|
233
|
+
|
|
234
|
+
/* --- sidebar: tách riêng để đổi theme sidebar không đụng tới app --- */
|
|
235
|
+
--sidebar: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
|
|
236
|
+
--sidebar-foreground: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
|
|
237
|
+
--sidebar-primary: var(--color-blue-600);
|
|
238
|
+
--sidebar-primary-foreground: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
|
|
239
|
+
--sidebar-accent: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
240
|
+
--sidebar-accent-foreground: oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue));
|
|
241
|
+
--sidebar-border: oklch(from var(--color-slate-200) l var(--tint-c-200) var(--tint-hue));
|
|
242
|
+
--sidebar-ring: var(--color-blue-600);
|
|
243
|
+
|
|
244
|
+
/* --- chart: năm màu chuỗi, neo theo sắc của --primary ---
|
|
245
|
+
Trải theo CẢ sắc lẫn ĐỘ SÁNG. Trải theo sắc thôi là chưa đủ: đo bằng
|
|
246
|
+
luminance thì bản cũ có hai màu chỉ chênh 0,007 — in đen trắng hay với
|
|
247
|
+
người khiếm sắc là trùng nhau. Bản này khoảng cách nhỏ nhất 0,045, và mọi
|
|
248
|
+
màu đạt tối thiểu 3,13:1 trên mặt card (WCAG 1.4.11).
|
|
249
|
+
Bốn bảng màu còn lại khai đè bộ này trong palettes.css. */
|
|
250
|
+
--chart-1: var(--color-blue-600);
|
|
251
|
+
--chart-2: var(--color-cyan-600);
|
|
252
|
+
--chart-3: var(--color-violet-900);
|
|
253
|
+
--chart-4: var(--color-sky-800);
|
|
254
|
+
--chart-5: var(--color-indigo-400);
|
|
255
|
+
|
|
256
|
+
/* Chữ/icon đặt TRỰC TIẾP trên một màu chuỗi (nhãn trong cột, chữ cái trong
|
|
257
|
+
avatar). Không thể dùng chung MỘT màu chữ cho cả năm: thang màu chuỗi cố ý
|
|
258
|
+
trải rộng độ sáng, nên cùng một màu chữ sẽ đọc được ở đầu này và biến mất ở
|
|
259
|
+
đầu kia — bản cũ chỉ có --chart-foreground trắng và đã hỏng sẵn (chữ trắng
|
|
260
|
+
trên chart-3 sáng chỉ đạt 3,0:1, trên blue-400 tối chỉ 2,64:1).
|
|
261
|
+
Cú pháp màu tương đối tự chọn đen hay trắng theo độ sáng của chính nền:
|
|
262
|
+
L < 0,58 thì chữ trắng, ngược lại chữ đen. Ngưỡng dò bằng cách quét 0,46–0,68
|
|
263
|
+
trên cả 50 màu chuỗi: 0,58 cho tương phản thấp nhất cao nhất (4,74:1).
|
|
264
|
+
HỆ SỐ 1000 KHÔNG PHẢI SỐ TUỲ Ý. Nó quyết định độ dốc của bậc thang: với
|
|
265
|
+
hệ số 100, một màu nằm sát ngưỡng cho ra giá trị NẰM GIỮA 0 và 1, tức chữ
|
|
266
|
+
màu xám. Đã dính thật — red-600 có L = 0,577, lệch ngưỡng 0,003, ra
|
|
267
|
+
clamp(0; 0,3; 1) = 0,3 và tương phản tụt còn 2,85:1. Hệ số 1000 thu dải mập
|
|
268
|
+
mờ xuống 0,001, mà màu gần ngưỡng nhất trong cả 50 màu vẫn cách 0,003. Khai một lần là đủ cho cả năm
|
|
269
|
+
bảng màu, vì var(--chart-N) resolve theo bảng đang bật. */
|
|
270
|
+
--chart-1-foreground: oklch(from var(--chart-1) clamp(0, (0.58 - l) * 1000, 1) 0 h);
|
|
271
|
+
--chart-2-foreground: oklch(from var(--chart-2) clamp(0, (0.58 - l) * 1000, 1) 0 h);
|
|
272
|
+
--chart-3-foreground: oklch(from var(--chart-3) clamp(0, (0.58 - l) * 1000, 1) 0 h);
|
|
273
|
+
--chart-4-foreground: oklch(from var(--chart-4) clamp(0, (0.58 - l) * 1000, 1) 0 h);
|
|
274
|
+
--chart-5-foreground: oklch(from var(--chart-5) clamp(0, (0.58 - l) * 1000, 1) 0 h);
|
|
275
|
+
/* --- code: bảng màu tô cú pháp, neo theo GitHub Light ---
|
|
276
|
+
Đây là theme shiki mà shadcn dùng cho khối code trong tài liệu. Mỗi dòng
|
|
277
|
+
là nấc Tailwind gần nhất với màu tương ứng của nó (ΔE ≤ 0.08). Nền dùng
|
|
278
|
+
luôn --secondary nên tự đổi theo bảng màu đang chọn. */
|
|
279
|
+
--code-bg: var(--secondary);
|
|
280
|
+
--code-fg: var(--foreground);
|
|
281
|
+
--code-comment: var(--color-gray-600);
|
|
282
|
+
--code-keyword: var(--color-red-700);
|
|
283
|
+
--code-entity: var(--color-purple-700);
|
|
284
|
+
--code-tag: var(--color-green-800);
|
|
285
|
+
--code-component: var(--color-blue-700);
|
|
286
|
+
--code-string: var(--color-blue-950);
|
|
287
|
+
--code-number: var(--color-blue-700);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.dark {
|
|
291
|
+
--background: oklch(from var(--color-slate-950) l var(--tint-c-950) var(--tint-hue));
|
|
292
|
+
--foreground: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
293
|
+
|
|
294
|
+
--card: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
|
|
295
|
+
--card-foreground: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
296
|
+
|
|
297
|
+
--popover: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
|
|
298
|
+
--popover-foreground: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
299
|
+
|
|
300
|
+
--primary: var(--color-blue-600);
|
|
301
|
+
--primary-foreground: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
|
|
302
|
+
|
|
303
|
+
--secondary: oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue));
|
|
304
|
+
--secondary-foreground: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
305
|
+
|
|
306
|
+
--muted: oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue));
|
|
307
|
+
--muted-foreground: oklch(from var(--color-slate-400) l var(--tint-c-400) var(--tint-hue));
|
|
308
|
+
|
|
309
|
+
--accent: oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue));
|
|
310
|
+
--accent-foreground: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
311
|
+
|
|
312
|
+
/* Bản tối dùng CÙNG nấc với bản sáng. Trần độ sáng của nền nút do chữ trắng
|
|
313
|
+
quyết định (>= 4.5:1), không liên quan tới nền trang — nên nấc sáng nhất
|
|
314
|
+
còn đạt là một, cho cả hai theme. Nấc tối hơn thì chữ dễ đọc hơn nhưng nút
|
|
315
|
+
chìm vào nền tối: blue-700 tách còn 2.76, blue-800 còn 2.13.
|
|
316
|
+
Vì vậy chữ ở đây là màu SÁNG, khác bản trước. */
|
|
317
|
+
--destructive: var(--color-red-600);
|
|
318
|
+
--destructive-foreground: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
|
|
319
|
+
|
|
320
|
+
--success: var(--color-green-700);
|
|
321
|
+
--success-foreground: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
|
|
322
|
+
|
|
323
|
+
--warning: var(--color-yellow-500);
|
|
324
|
+
--warning-foreground: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
|
|
325
|
+
|
|
326
|
+
--info: var(--color-cyan-700);
|
|
327
|
+
--info-foreground: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
|
|
328
|
+
|
|
329
|
+
/* Nền tối cần tint đậm hơn. Chữ phải pha thêm trắng: dùng thẳng màu gốc thì
|
|
330
|
+
destructive chỉ đạt 4.0:1 trên nền soft — đo được, không phải phỏng đoán. */
|
|
331
|
+
--primary-soft: color-mix(in oklab, var(--primary) 22%, var(--control-base));
|
|
332
|
+
--primary-soft-border: color-mix(in oklab, var(--primary) 34%, var(--control-base));
|
|
333
|
+
--primary-soft-hover: color-mix(in oklab, var(--primary) 32%, var(--control-base));
|
|
334
|
+
|
|
335
|
+
--destructive-soft: color-mix(in oklab, var(--destructive) 22%, var(--control-base));
|
|
336
|
+
--destructive-soft-border: color-mix(in oklab, var(--destructive) 34%, var(--control-base));
|
|
337
|
+
--destructive-soft-hover: color-mix(in oklab, var(--destructive) 32%, var(--control-base));
|
|
338
|
+
|
|
339
|
+
--success-soft: color-mix(in oklab, var(--success) 22%, var(--control-base));
|
|
340
|
+
--success-soft-border: color-mix(in oklab, var(--success) 34%, var(--control-base));
|
|
341
|
+
--success-soft-hover: color-mix(in oklab, var(--success) 32%, var(--control-base));
|
|
342
|
+
|
|
343
|
+
--warning-soft: color-mix(in oklab, var(--warning) 22%, var(--control-base));
|
|
344
|
+
--warning-soft-border: color-mix(in oklab, var(--warning) 34%, var(--control-base));
|
|
345
|
+
--warning-soft-hover: color-mix(in oklab, var(--warning) 32%, var(--control-base));
|
|
346
|
+
|
|
347
|
+
--info-soft: color-mix(in oklab, var(--info) 22%, var(--control-base));
|
|
348
|
+
--info-soft-border: color-mix(in oklab, var(--info) 34%, var(--control-base));
|
|
349
|
+
--info-soft-hover: color-mix(in oklab, var(--info) 32%, var(--control-base));
|
|
350
|
+
|
|
351
|
+
--primary-soft-foreground: oklch(from var(--primary) calc(l + 0.18) calc(c * 1.1) h);
|
|
352
|
+
/* Đỏ cộng 0.21 chứ không 0.18 như bốn sắc kia: chip soft đặt trên `--card`
|
|
353
|
+
(chứ không trên nền trang) ở Solar tối đo được 4.49 — hụt AA đúng 0.008.
|
|
354
|
+
Solar có --card sáng nhất trong năm bảng nên lớp tint đỏ 22% nằm trên đó
|
|
355
|
+
bị nâng lên cao nhất. Xem mục đo lại trong CLAUDE.md. */
|
|
356
|
+
--destructive-soft-foreground: oklch(from var(--destructive) calc(l + 0.21) calc(c * 1.1) h);
|
|
357
|
+
--success-soft-foreground: oklch(from var(--success) calc(l + 0.18) calc(c * 1.1) h);
|
|
358
|
+
--warning-soft-foreground: oklch(from var(--warning) calc(l + 0.06) calc(c * 1.1) h);
|
|
359
|
+
--info-soft-foreground: oklch(from var(--info) calc(l + 0.18) calc(c * 1.1) h);
|
|
360
|
+
|
|
361
|
+
--border: color-mix(in oklab, var(--color-white) 12%, transparent);
|
|
362
|
+
--input: color-mix(in oklab, var(--color-white) 16%, transparent);
|
|
363
|
+
--ring: var(--color-blue-400);
|
|
364
|
+
|
|
365
|
+
/* Quầng đậm hơn bản sáng (18% -> 30%): nền tối nuốt tint, để nguyên 18%
|
|
366
|
+
thì lớp ngoài gần như không thấy. Cùng cách xử lý như nhóm token soft. */
|
|
367
|
+
--focus-ring: 0 0 0 1px var(--ring), 0 0 0 4px color-mix(in oklab, var(--ring) 30%, transparent);
|
|
368
|
+
--focus-ring-invalid: 0 0 0 1px var(--destructive), 0 0 0 4px color-mix(in oklab, var(--destructive) 30%, transparent);
|
|
369
|
+
|
|
370
|
+
--sidebar: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
|
|
371
|
+
--sidebar-foreground: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
372
|
+
--sidebar-primary: var(--color-blue-400);
|
|
373
|
+
--sidebar-primary-foreground: oklch(from var(--color-slate-950) l var(--tint-c-950) var(--tint-hue));
|
|
374
|
+
--sidebar-accent: oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue));
|
|
375
|
+
--sidebar-accent-foreground: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
|
|
376
|
+
--sidebar-border: color-mix(in oklab, var(--color-white) 12%, transparent);
|
|
377
|
+
--sidebar-ring: var(--color-blue-400);
|
|
378
|
+
|
|
379
|
+
--chart-1: var(--color-blue-400);
|
|
380
|
+
--chart-2: var(--color-cyan-400);
|
|
381
|
+
--chart-3: var(--color-violet-500);
|
|
382
|
+
--chart-4: var(--color-sky-400);
|
|
383
|
+
--chart-5: var(--color-indigo-400);
|
|
384
|
+
--code-bg: var(--secondary);
|
|
385
|
+
--code-fg: var(--foreground);
|
|
386
|
+
--code-comment: var(--color-gray-400);
|
|
387
|
+
--code-keyword: var(--color-red-400);
|
|
388
|
+
--code-entity: var(--color-violet-400);
|
|
389
|
+
--code-tag: var(--color-green-300);
|
|
390
|
+
--code-component: var(--color-blue-300);
|
|
391
|
+
--code-string: var(--color-sky-300);
|
|
392
|
+
--code-number: var(--color-blue-300);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Bo tròn suy ra từ `--radius`. Khai ở đây (chứ không chỉ trong `@theme` của
|
|
396
|
+
Tailwind) để consumer không dùng Tailwind vẫn có `var(--radius-md)`.
|
|
397
|
+
|
|
398
|
+
Thang NHÂN TỈ LỆ, không cộng trừ px. Trước đây là -4 / -2 / 0 / +4 / +8, và
|
|
399
|
+
ở mỏ neo mặc định 0.625rem hai cách cho KẾT QUẢ TRÙNG KHÍT (6 / 8 / 10 / 14 /
|
|
400
|
+
18px) — các hệ số dưới đây chính là tỉ lệ đo được từ thang cũ, nên đổi sang
|
|
401
|
+
nhân không dịch một pixel nào của bảng gốc lẫn bốn bảng màu hiện có.
|
|
402
|
+
|
|
403
|
+
Cộng trừ px SỤP ở mỏ neo nhỏ, và đó là lý do phải đổi. Ở `--radius: 3px`
|
|
404
|
+
thang cũ ra -1 / 1 / 3 / 7 / 11: nấc nhỏ bị kẹp về 0, nấc lớn vọt lên gần
|
|
405
|
+
gấp bốn lần mỏ neo, tức hai đầu thang giãn ra đúng lúc thiết kế muốn nó co
|
|
406
|
+
lại. Nhân tỉ lệ ra 1,8 / 2,4 / 3 / 4,2 / 5,4 — cả thang nằm gọn trong 2-5px,
|
|
407
|
+
giữ nguyên thứ tự và giữ nguyên tương quan.
|
|
408
|
+
|
|
409
|
+
Hệ quả là `--radius` mới thật sự là MỘT mặc định cho cả hệ: đổi nó thì mọi
|
|
410
|
+
component đi theo cùng tỉ lệ, còn chỗ nào cần khác thì tự khai class tại chỗ.
|
|
411
|
+
Trước khi đổi, một mỏ neo nhỏ làm thang vỡ ra chứ không co lại.
|
|
412
|
+
`[data-style]` PHẢI có trong danh sách này, và đó không phải chỗ thừa.
|
|
413
|
+
`calc(var(--radius) …)` giải ngay tại phần tử mang khai báo, rồi chỉ có KẾT
|
|
414
|
+
QUẢ mới thừa kế xuống. Nên đặt `--radius` trên một phần tử CON của `.light`
|
|
415
|
+
thì năm nấc đã tính xong ở `.light` bằng giá trị CŨ — ghi đè im lặng không
|
|
416
|
+
có tác dụng. Đã đo trên `/create`: `--radius` chạy 0rem → 1.4rem mà
|
|
417
|
+
`rounded-lg` đứng nguyên 10px, tức cả ô chọn bo góc lẫn `data-style="nova"`
|
|
418
|
+
đều vô hiệu khi trục hình khối nằm ở một khối lồng thay vì ở `<html>`.
|
|
419
|
+
|
|
420
|
+
Cùng lớp lỗi với "bảng màu đè token kính" và với ba lần lặp attribute ở
|
|
421
|
+
`styles.css`: quy tắc đúng ở `:root` không tự đúng ở khối lồng. */
|
|
422
|
+
:root,
|
|
423
|
+
.light,
|
|
424
|
+
.dark,
|
|
425
|
+
[data-style] {
|
|
426
|
+
--radius-sm: calc(var(--radius) * 0.6);
|
|
427
|
+
--radius-md: calc(var(--radius) * 0.8);
|
|
428
|
+
--radius-lg: var(--radius);
|
|
429
|
+
--radius-xl: calc(var(--radius) * 1.4);
|
|
430
|
+
--radius-2xl: calc(var(--radius) * 1.8);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* Geist cho chữ, Geist Mono cho code — đúng bộ font bản thiết kế dùng.
|
|
434
|
+
Webfont nạp từ Google Fonts trong index.html của app (có subset vietnamese),
|
|
435
|
+
không thêm package nào.
|
|
436
|
+
`tailwind.css` ánh xạ hai biến này vào `@theme`, nhờ đó Tailwind lấy chúng
|
|
437
|
+
làm --default-font-family / --default-mono-font-family. */
|
|
438
|
+
:root {
|
|
439
|
+
--font-sans: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
440
|
+
/* Họ chữ cho TIÊU ĐỀ. Mặc định trỏ vào `--font-sans` nên không đổi gì cho tới
|
|
441
|
+
khi có người đặt lại nó — nhưng nó là một token RIÊNG, không phải bí danh:
|
|
442
|
+
đổi nó không kéo theo chữ thân bài, và ngược lại.
|
|
443
|
+
|
|
444
|
+
Người đọc nó là rule `h1..h6` trong `@layer base` của `src/variants`,
|
|
445
|
+
và `--typeset-font-heading` của lớp văn xuôi rơi lui về đây. */
|
|
446
|
+
--font-heading: var(--font-sans);
|
|
447
|
+
--font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* Số đo của CONTROL — tầng component, và nó phải TỰ KIẾM chỗ đứng.
|
|
451
|
+
|
|
452
|
+
TIÊU CHÍ KẾT NẠP: một số đo chỉ thành token khi có thứ BÊN NGOÀI component
|
|
453
|
+
phải với vào trong nó — một trục theme, một ràng buộc CSS, hoặc một component
|
|
454
|
+
khác đang chép lại nó. Muốn đổi số đo cho vui thì ghi đè bằng `class`; `cn()`
|
|
455
|
+
lo việc đó và tailwind-merge đã đo là thắng.
|
|
456
|
+
|
|
457
|
+
Đây đúng là tiêu chí mà 30 token component sẵn có (--sidebar-*, --code-*,
|
|
458
|
+
--slider-*, --header-surface, --chart-active-bar) vốn đã tuân theo: cả 30 đều
|
|
459
|
+
sinh ra vì theme kính hoặc bảng màu phải với vào trong một component, không
|
|
460
|
+
cái nào sinh ra vì ai đó muốn tuỳ biến. Khác biệt duy nhất là chúng đều là
|
|
461
|
+
MÀU, còn nhóm dưới đây là SỐ ĐO.
|
|
462
|
+
|
|
463
|
+
ĐỪNG làm bản cạn kiệt kiểu PrimeVue. Đo rồi: 44 cỡ trên 12 component, mỗi cỡ
|
|
464
|
+
3–5 số đo là ra 130–220 token — nhiều hơn cả bộ token ngữ nghĩa hiện có, và
|
|
465
|
+
mỗi token là một lời hứa tương thích ngược không rút lại được.
|
|
466
|
+
|
|
467
|
+
Neo vào `--spacing` chứ không ghi rem cứng, để bên rescale thang spacing thì
|
|
468
|
+
control đi theo. */
|
|
469
|
+
:root {
|
|
470
|
+
/* Ô nhập. `h-[30px]` từng bị chép nguyên văn vào 4 component, và cỡ `sm` đã
|
|
471
|
+
TRÔI thành ba số khác nhau — xem chú thích ở cuối khối. 30px cố ý nằm giữa
|
|
472
|
+
h-7 (28) và h-8 (32), nên hệ số là 7.5. */
|
|
473
|
+
--ui-field-height-sm: calc(var(--spacing) * 7.5);
|
|
474
|
+
--ui-field-height-md: calc(var(--spacing) * 9);
|
|
475
|
+
--ui-field-padding-x-sm: calc(var(--spacing) * 2.5);
|
|
476
|
+
--ui-field-padding-x-md: calc(var(--spacing) * 3);
|
|
477
|
+
--ui-field-text-sm: 12px;
|
|
478
|
+
|
|
479
|
+
/* Nút và Toggle. `Toggle` là một nút giữ trạng thái nên nó CỐ Ý soi theo
|
|
480
|
+
thang cỡ của `Button` — trước đây bằng cách chép nguyên văn cả chuỗi class.
|
|
481
|
+
Chỉ có xs lệch padding, ba cỡ còn lại dùng chung một giá trị, nên khai một
|
|
482
|
+
token thay vì ba. */
|
|
483
|
+
--ui-control-height-xs: calc(var(--spacing) * 6);
|
|
484
|
+
--ui-control-height-sm: calc(var(--spacing) * 7);
|
|
485
|
+
--ui-control-height-md: calc(var(--spacing) * 8);
|
|
486
|
+
--ui-control-height-lg: calc(var(--spacing) * 9);
|
|
487
|
+
--ui-control-padding-x-xs: calc(var(--spacing) * 2);
|
|
488
|
+
--ui-control-padding-x: calc(var(--spacing) * 2.5);
|
|
489
|
+
|
|
490
|
+
/* Nhịp phản hồi khi bấm. `140ms` bị chép ở 4 component control (Button,
|
|
491
|
+
Toggle, Checkbox, RadioGroup) và `100ms` ở 2 — cùng lớp lỗi với h-[30px].
|
|
492
|
+
Không nằm trong thang `--duration-*` chung vì đây là nhịp riêng của control:
|
|
493
|
+
nó phải nhanh hơn 150ms để cú bấm thấy "dính tay". */
|
|
494
|
+
--ui-control-duration: 140ms;
|
|
495
|
+
--ui-control-duration-press: 100ms;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/* Tầng chồng.
|
|
499
|
+
|
|
500
|
+
Tailwind KHÔNG có thang z-index (utility `z-10` là số tĩnh, không đọc token),
|
|
501
|
+
nên repo tự khai. Bảy nấc dưới đây ĐO trên toàn repo chứ không bịa cho tròn —
|
|
502
|
+
mỗi nấc đều đang có chỗ dùng thật:
|
|
503
|
+
|
|
504
|
+
1 NavigationMenuIndicator, cột sticky của DataTable
|
|
505
|
+
10 nút copy trong CodeBlock, header dính của bảng, tay cầm Splitter (14 chỗ)
|
|
506
|
+
20 nội dung nổi trên phần trang trí của cùng khối — SidebarRail
|
|
507
|
+
30 thanh dính của layout — AdminLayout
|
|
508
|
+
40 đảo nổi — KitTocIsland
|
|
509
|
+
50 overlay: Dialog, Popover, Tooltip, Select, Sheet, HoverCard (9 chỗ)
|
|
510
|
+
100 SkipLink — phải trên tất cả, kể cả overlay
|
|
511
|
+
|
|
512
|
+
Đọc bằng `z-(--z-overlay)`; không cần khai trong `@theme inline` vì Tailwind
|
|
513
|
+
không có namespace z-index để mà ánh xạ vào.
|
|
514
|
+
|
|
515
|
+
Thang này chỉ nói về tầng TOÀN CỤC. Thứ tự chồng CỤC BỘ giữa các phần tử anh
|
|
516
|
+
em trong cùng một component thì không thuộc về đây và cứ viết thẳng — lớp phủ
|
|
517
|
+
của SpeedDial dùng `-z-10` để nằm dưới nút và các mục của chính nó, đặt tên
|
|
518
|
+
cho nó trong thang toàn cục là nói sai về phạm vi của nó. */
|
|
519
|
+
:root {
|
|
520
|
+
--z-base: 1;
|
|
521
|
+
--z-raised: 10;
|
|
522
|
+
--z-elevated: 20;
|
|
523
|
+
--z-sticky: 30;
|
|
524
|
+
--z-floating: 40;
|
|
525
|
+
--z-overlay: 50;
|
|
526
|
+
--z-skip-link: 100;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/* Thời lượng chuyển động. Tailwind cũng không có thang này (`duration-200` là
|
|
530
|
+
số tĩnh). Ba giá trị, đúng bằng ba giá trị repo đang dùng: 150ms ở 5 chỗ,
|
|
531
|
+
200ms ở 22 chỗ, 300ms ở 2 chỗ.
|
|
532
|
+
|
|
533
|
+
CẨN THẬN khi đụng tới easing: `--ease-out` mà `primitives.css` chép từ
|
|
534
|
+
Tailwind là `cubic-bezier(0, 0, 0.2, 1)`, còn TỪ KHOÁ `ease-out` của CSS là
|
|
535
|
+
`cubic-bezier(0, 0, 0.58, 1)`. Hai đường cong khác nhau — đổi từ khoá sang
|
|
536
|
+
token là đổi cả cảm giác chuyển động, không phải dọn dẹp. */
|
|
537
|
+
:root {
|
|
538
|
+
--duration-fast: 150ms;
|
|
539
|
+
--duration-base: 200ms;
|
|
540
|
+
--duration-slow: 300ms;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/* Animation mở/đóng theo chiều cao.
|
|
544
|
+
|
|
545
|
+
Khai riêng cho Collapsible và Accordion cho khớp tên biến của reka. ĐÃ ĐO:
|
|
546
|
+
AccordionContent thực ra đặt CẢ HAI biến cùng lúc
|
|
547
|
+
(--reka-accordion-content-height và --reka-collapsible-content-height đều ra
|
|
548
|
+
59.5px), nên dùng chung cũng chạy. Tách ra là để đọc code khỏi phải nhớ chi
|
|
549
|
+
tiết đó, không phải vì bắt buộc.
|
|
550
|
+
|
|
551
|
+
ĐỌC BA TÊN BIẾN, vì mỗi bên đặt một tiền tố: reka ghi
|
|
552
|
+
`--reka-*-content-height`, Radix ghi `--radix-*-content-height`, và cây
|
|
553
|
+
vanilla — không có primitive nào để mà mượn tên — ghi `--hy-*-content-height`.
|
|
554
|
+
Một phần tử chỉ do MỘT bên dựng nên không bao giờ có hai cái cùng lúc, và
|
|
555
|
+
`var()` lồng rơi lui đúng sang tên còn lại. Đây là chỗ duy nhất trong bộ token
|
|
556
|
+
phải biết tên riêng của primitive — thà nhận nó ở một chỗ còn hơn để mỗi cây
|
|
557
|
+
khai một bản `@keyframes` rồi bốn bản trôi khỏi nhau.
|
|
558
|
+
(Radix cũng đặt cả hai: nó gán
|
|
559
|
+
`--radix-accordion-content-height: var(--radix-collapsible-content-height)`.)
|
|
560
|
+
|
|
561
|
+
`@keyframes` để ở tầng ngoài cùng chứ không lồng trong `@theme`: đây là CSS
|
|
562
|
+
thuần nên consumer không dùng Tailwind vẫn viết được
|
|
563
|
+
`animation: var(--animate-accordion-down)`. */
|
|
564
|
+
:root {
|
|
565
|
+
--animate-collapsible-down: collapsible-down var(--duration-base) ease-out;
|
|
566
|
+
--animate-collapsible-up: collapsible-up var(--duration-base) ease-out;
|
|
567
|
+
--animate-accordion-down: accordion-down var(--duration-base) ease-out;
|
|
568
|
+
--animate-accordion-up: accordion-up var(--duration-base) ease-out;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
@keyframes collapsible-down {
|
|
572
|
+
from { height: 0; }
|
|
573
|
+
to { height: var(--reka-collapsible-content-height, var(--radix-collapsible-content-height, var(--hy-collapsible-content-height))); }
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
@keyframes collapsible-up {
|
|
577
|
+
from { height: var(--reka-collapsible-content-height, var(--radix-collapsible-content-height, var(--hy-collapsible-content-height))); }
|
|
578
|
+
to { height: 0; }
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
@keyframes accordion-down {
|
|
582
|
+
from { height: 0; }
|
|
583
|
+
to { height: var(--reka-accordion-content-height, var(--radix-accordion-content-height, var(--hy-accordion-content-height))); }
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
@keyframes accordion-up {
|
|
587
|
+
from { height: var(--reka-accordion-content-height, var(--radix-accordion-content-height, var(--hy-accordion-content-height))); }
|
|
588
|
+
to { height: 0; }
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/* Người bị rối loạn tiền đình có thể chóng mặt, buồn nôn vì chuyển động trên
|
|
592
|
+
màn hình. Hệ điều hành cho họ bật cờ này và CSS phải nghe theo.
|
|
593
|
+
|
|
594
|
+
Trước đó repo chỉ rắc `motion-reduce:` ở 8 component — nghĩa là phần lớn
|
|
595
|
+
transition và toàn bộ animation vào/ra của Dialog, Sheet, Accordion vẫn chạy.
|
|
596
|
+
Đặt ở đây thì không component nào phải nhớ nữa.
|
|
597
|
+
|
|
598
|
+
BA điều cố ý KHÔNG làm:
|
|
599
|
+
|
|
600
|
+
1. Không đụng `transform`. Nghe thì hợp lý vì slide/scale là thứ cần bỏ,
|
|
601
|
+
nhưng SpeedDial dùng `translate()` để ĐẶT VỊ TRÍ chứ không phải để tạo
|
|
602
|
+
hiệu ứng — vô hiệu hoá nó là dồn cả tám mục chồng lên nút chính.
|
|
603
|
+
2. Không hạ `animation-iteration-count` xuống 1. Spinner quay vô hạn, cắt đi
|
|
604
|
+
là chỉ báo tải đứng im — hỏng chứ không phải trợ năng. Chuyển động lặp
|
|
605
|
+
liên tục, không có điểm đầu cuối, vốn không phải thứ cờ này nhắm tới.
|
|
606
|
+
3. Không xoá hẳn transition, chỉ rút còn 1ms. Tài liệu nói rõ: giảm chuyển
|
|
607
|
+
động KHÔNG phải bỏ phản hồi — trạng thái vẫn phải đổi, chỉ là đổi ngay
|
|
608
|
+
thay vì trượt tới. */
|
|
609
|
+
@media (prefers-reduced-motion: reduce) {
|
|
610
|
+
*,
|
|
611
|
+
*::before,
|
|
612
|
+
*::after {
|
|
613
|
+
transition-duration: 1ms !important;
|
|
614
|
+
transition-delay: 0ms !important;
|
|
615
|
+
animation-duration: 1ms !important;
|
|
616
|
+
animation-delay: 0ms !important;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/* Spinner là ngoại lệ — xem lý do 2 ở trên. */
|
|
620
|
+
.animate-spin {
|
|
621
|
+
animation-duration: 1s !important;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
html {
|
|
625
|
+
scroll-behavior: auto !important;
|
|
626
|
+
}
|
|
627
|
+
}
|