@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.
@@ -0,0 +1,364 @@
1
+ /* Hiệu ứng kính (liquid glass) — TRỤC RIÊNG, không phải một bảng màu.
2
+ Bật bằng `data-surface="glass"` trên <html>, chồng lên `data-palette` nên
3
+ Solar + glass hay Nebula + glass đều dùng được.
4
+
5
+ Hai thứ tạo nên hiệu ứng:
6
+ 1. Token bề mặt chuyển sang trong mờ (--card, --popover, --border…).
7
+ 2. --surface-filter bật backdrop-filter cho class .surface.
8
+
9
+ Ngoài glass thì --surface-filter là `none` -> không tạo containing context,
10
+ không tốn GPU, và `position: fixed` bên trong vẫn neo theo viewport. */
11
+
12
+ /* Phải khoá theo chế độ sáng/tối giống palettes.css. Viết trần
13
+ `:root[data-surface=glass]` là (0,2,0), THUA `:root:not(.dark)[data-palette=…]`
14
+ (0,3,0) -> token --card của bảng màu đè lại và kính trở về đục hoàn toàn.
15
+ Viết đủ hai vế thì bằng điểm, và surfaces.css nạp sau nên thắng. */
16
+ :root:not(.dark)[data-surface="glass"],
17
+ .light [data-surface="glass"],
18
+ :root:not(.dark) [data-surface="glass"] {
19
+ /* Nút soft phải ĐỤC ở theme kính. Để trong suốt thì nó ăn theo gradient sáng
20
+ phía sau card và tương phản chữ tụt còn 3.94:1. Giá trị này chính là
21
+ --background của theme thường, nên nút soft trông y hệt hai theme. */
22
+ --control-base: var(--background);
23
+ --surface-filter: blur(16px) saturate(1.6);
24
+ --header-surface: color-mix(in oklab, var(--background) 46%, transparent);
25
+ --surface-backdrop:
26
+ radial-gradient(60rem 40rem at 12% -8%, color-mix(in oklab, var(--primary) 30%, transparent), transparent 60%),
27
+ radial-gradient(50rem 36rem at 92% 4%, color-mix(in oklab, var(--info) 26%, transparent), transparent 62%),
28
+ radial-gradient(46rem 34rem at 68% 96%, color-mix(in oklab, var(--success) 22%, transparent), transparent 60%);
29
+
30
+ --card: color-mix(in oklab, var(--color-white) 62%, transparent);
31
+ --popover: color-mix(in oklab, var(--color-white) 76%, transparent);
32
+ --sidebar: color-mix(in oklab, var(--color-white) 46%, transparent);
33
+
34
+ /* Chữ mờ phải TỐI HƠN ở nhánh kính sáng — đây là món nợ AA mà bản trước ghi
35
+ "còn nợ, không sửa trong đợt này", nay trả.
36
+
37
+ Đo trên nền tổng hợp (card trắng 62% chồng lên đỉnh gradient chồng lên nền
38
+ trang), `slate-500` nguyên bản chỉ đạt 3,66 / 4,13 / 4,23 ở ba đỉnh
39
+ primary / info / success — dưới ngưỡng AA 4,5 cho chữ 14px.
40
+
41
+ Hai đường chữa, đã đo cả hai:
42
+ - LÀM NHẠT GRADIENT: không cứu được. Hạ đậm độ xuống 0,4 lần vẫn chỉ lên
43
+ 4,29, vì phần lớn độ sáng đến từ chính tấm card trắng chứ không phải
44
+ gradient — bỏ hẳn gradient cũng mới 4,74.
45
+ - PHA TỐI CHỮ: hiệu quả tuyến tính. 20% → 4,30 · 30% → 4,63 · 40% → 5,00.
46
+
47
+ Chọn 40%: 30% tuy đã qua ngưỡng nhưng chỉ dư 0,13, mỏng tới mức chênh lệch
48
+ làm tròn giữa các engine cũng đủ kéo tụt. Ở 5,00 chữ mờ vẫn nhạt hơn chữ
49
+ chính rất xa (chữ chính đo được 15,12–17,83) nên nó vẫn đọc ra là "mờ".
50
+
51
+ Nhánh kính TỐI không cần: đo được 4,98–6,78, đã qua từ trước. */
52
+ --muted-foreground: color-mix(
53
+ in oklab,
54
+ oklch(from var(--color-slate-700) l var(--tint-c-700) var(--tint-hue)) 40%,
55
+ oklch(from var(--color-slate-500) l var(--tint-c-500) var(--tint-hue))
56
+ );
57
+
58
+ /* Khối code KHÔNG trong mờ: nó là nội dung để đọc, không phải khung kính.
59
+ Trong mờ thì chữ nằm trên gradient nền, vừa khó đọc vừa làm dải phủ mép
60
+ phải chồng màu. Đục thì thanh cuộn và nút copy cũng hiện rõ. */
61
+ --code-bg: oklch(from var(--color-slate-100) l var(--tint-c-100) var(--tint-hue));
62
+
63
+ --secondary: color-mix(in oklab, oklch(from var(--color-slate-500) l var(--tint-c-500) var(--tint-hue)) 10%, transparent);
64
+ --muted: color-mix(in oklab, oklch(from var(--color-slate-500) l var(--tint-c-500) var(--tint-hue)) 10%, transparent);
65
+ --accent: color-mix(in oklab, oklch(from var(--color-slate-500) l var(--tint-c-500) var(--tint-hue)) 14%, transparent);
66
+ --sidebar-accent: color-mix(in oklab, oklch(from var(--color-slate-500) l var(--tint-c-500) var(--tint-hue)) 14%, transparent);
67
+
68
+ --border: color-mix(in oklab, oklch(from var(--color-slate-700) l var(--tint-c-700) var(--tint-hue)) 14%, transparent);
69
+ --input: color-mix(in oklab, oklch(from var(--color-slate-700) l var(--tint-c-700) var(--tint-hue)) 22%, transparent);
70
+
71
+ /* Thanh trượt: rãnh và phần đã chọn TRONG MỜ, riêng núm thì ĐỤC — lý do đo
72
+ được nằm ở rule .ui-slider-thumb cuối file. Phần đã chọn giữ 72% chứ không
73
+ 68% như nút: dải này chỉ cao 6px, loãng thêm là mất ranh giới với rãnh. */
74
+ --slider-track: color-mix(in oklab, oklch(from var(--color-slate-700) l var(--tint-c-700) var(--tint-hue)) 12%, transparent);
75
+ --slider-range: color-mix(in oklab, var(--primary) 72%, transparent);
76
+ --slider-thumb: var(--background);
77
+ --slider-thumb-border: color-mix(in oklab, var(--primary) 70%, transparent);
78
+ }
79
+
80
+ :root.dark[data-surface="glass"],
81
+ .dark [data-surface="glass"],
82
+ :root.dark [data-surface="glass"] {
83
+ /* Nút soft phải ĐỤC ở theme kính. Để trong suốt thì nó ăn theo gradient sáng
84
+ phía sau card và tương phản chữ tụt còn 3.94:1. Giá trị này chính là
85
+ --background của theme thường, nên nút soft trông y hệt hai theme. */
86
+ --control-base: var(--background);
87
+ --surface-filter: blur(16px) saturate(1.6);
88
+ --header-surface: color-mix(in oklab, var(--background) 46%, transparent);
89
+ --surface-backdrop:
90
+ radial-gradient(60rem 40rem at 12% -8%, color-mix(in oklab, var(--primary) 40%, transparent), transparent 60%),
91
+ radial-gradient(50rem 36rem at 92% 4%, color-mix(in oklab, var(--info) 34%, transparent), transparent 62%),
92
+ radial-gradient(46rem 34rem at 68% 96%, color-mix(in oklab, var(--success) 30%, transparent), transparent 60%);
93
+
94
+ --card: color-mix(in oklab, oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue)) 52%, transparent);
95
+ --popover: color-mix(in oklab, oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue)) 76%, transparent);
96
+ --sidebar: color-mix(in oklab, oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue)) 42%, transparent);
97
+
98
+ --code-bg: oklch(from var(--color-slate-800) l var(--tint-c-800) var(--tint-hue));
99
+
100
+ --secondary: color-mix(in oklab, var(--color-white) 10%, transparent);
101
+ --muted: color-mix(in oklab, var(--color-white) 10%, transparent);
102
+ --accent: color-mix(in oklab, var(--color-white) 14%, transparent);
103
+ --sidebar-accent: color-mix(in oklab, var(--color-white) 14%, transparent);
104
+
105
+ --border: color-mix(in oklab, var(--color-white) 14%, transparent);
106
+ --input: color-mix(in oklab, var(--color-white) 20%, transparent);
107
+
108
+ /* Núm SÁNG hơn nền trang chứ không tối hơn: theme thường nó là `--background`
109
+ (slate-950), đặt lên một mặt đã trong mờ thì nó chìm mất. Pha 26% chữ vào
110
+ nền cho ra một thấu kính đang bắt sáng — và vì pha từ chính hai token đó
111
+ nên nó ám đúng họ trung tính của bảng màu đang chọn, không phải một nấc
112
+ slate chốt cứng (Solar ấm, Nebula tím… núm slate sẽ lệch tông).
113
+ Vẫn ĐỤC — lý do ở cuối file. */
114
+ --slider-track: color-mix(in oklab, var(--color-white) 12%, transparent);
115
+ --slider-range: color-mix(in oklab, var(--primary) 76%, transparent);
116
+ --slider-thumb: color-mix(in oklab, var(--foreground) 26%, var(--background));
117
+ --slider-thumb-border: color-mix(in oklab, var(--primary) 76%, transparent);
118
+ }
119
+
120
+ /* Bám theo TOKEN NỀN chứ không theo tên component: mọi thứ dùng bg-card /
121
+ bg-popover / bg-sidebar đều thành kính, kể cả div viết tay ngoài thư viện.
122
+ `[class~="bg-card"]` khớp đúng token trong danh sách class -> không dính
123
+ `hover:bg-card` (đó là token khác). Class .surface là cửa sau cho những chỗ
124
+ dùng nền khác, ví dụ Dialog xài bg-background.
125
+ Rule này chỉ tồn tại dưới data-surface="glass", ngoài ra không tốn gì. */
126
+ :root[data-surface="glass"] :is(
127
+ [class~="bg-card"],
128
+ [class~="bg-popover"],
129
+ /* Tấm menu dùng `bg-menu-surface` chứ không `bg-popover` — thiếu dòng này là
130
+ bật theme kính mà mọi menu vẫn đục. */
131
+ [class~="bg-menu-surface"],
132
+ [class~="bg-sidebar"],
133
+ .surface
134
+ ) {
135
+ -webkit-backdrop-filter: var(--surface-filter);
136
+ backdrop-filter: var(--surface-filter);
137
+ }
138
+
139
+ /* Apple nói rõ trong "Adopting Liquid Glass": phải kiểm giao diện với các thiết
140
+ lập trợ năng, vì người dùng có thể tắt hiệu ứng trong suốt. Repo trước đây
141
+ không xử lý gì — kính vẫn mờ kể cả khi hệ điều hành bảo đừng.
142
+
143
+ Tắt trong suốt thì: bỏ backdrop-filter, mọi mặt nền thành ĐỤC, và nút kính
144
+ không còn là kính nữa mà là chip đặc.
145
+
146
+ Chỉ bắt trong phạm vi `[data-surface="glass"]`. Trước đây khối này còn kèm
147
+ `:root` trần, mà ngoài theme kính thì mặt nền vốn đã đục — nên nó không sửa
148
+ được gì, chỉ kịp ĐÈ `--card` / `--sidebar` của mọi bảng màu về một nấc slate:
149
+ bật tuỳ chọn trợ năng là Solar/Nebula/Aurora mất sạch sắc riêng. */
150
+ @media (prefers-reduced-transparency: reduce) {
151
+ :root:not(.dark)[data-surface="glass"],
152
+ .light [data-surface="glass"],
153
+ :root:not(.dark) [data-surface="glass"] {
154
+ --surface-filter: none;
155
+ --card: var(--color-white);
156
+ --popover: var(--color-white);
157
+ --sidebar: oklch(from var(--color-slate-50) l var(--tint-c-50) var(--tint-hue));
158
+ --header-surface: var(--color-white);
159
+ --slider-range: var(--primary);
160
+ }
161
+
162
+ :root.dark[data-surface="glass"],
163
+ .dark [data-surface="glass"],
164
+ :root.dark [data-surface="glass"] {
165
+ --surface-filter: none;
166
+ --card: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
167
+ --popover: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
168
+ --sidebar: oklch(from var(--color-slate-900) l var(--tint-c-900) var(--tint-hue));
169
+ --header-surface: oklch(from var(--color-slate-950) l var(--tint-c-950) var(--tint-hue));
170
+ --slider-range: var(--primary);
171
+ }
172
+ }
173
+
174
+ /* Cũng theo tài liệu đó: nếu tô màu cho control thì phải có bản light, bản dark
175
+ VÀ một bản tăng tương phản cho mỗi bản. Viền mảnh và chữ mờ là hai thứ chìm
176
+ sớm nhất, nên siết hai cái đó. */
177
+ @media (prefers-contrast: more) {
178
+ :root,
179
+ .light {
180
+ --border: oklch(from var(--color-slate-400) l var(--tint-c-400) var(--tint-hue));
181
+ --input: oklch(from var(--color-slate-500) l var(--tint-c-500) var(--tint-hue));
182
+ --muted-foreground: oklch(from var(--color-slate-700) l var(--tint-c-700) var(--tint-hue));
183
+ --glass-border: var(--color-white);
184
+ }
185
+
186
+ :root.dark {
187
+ --border: oklch(from var(--color-slate-500) l var(--tint-c-500) var(--tint-hue));
188
+ --input: oklch(from var(--color-slate-400) l var(--tint-c-400) var(--tint-hue));
189
+ --muted-foreground: oklch(from var(--color-slate-300) l var(--tint-c-300) var(--tint-hue));
190
+ --glass-border: var(--color-white);
191
+ }
192
+ }
193
+
194
+ /* MỌI nút đều thành kính khi bật theme này — glass là vật liệu của bề mặt, không
195
+ phải một kiểu nút. Đúng như tài liệu Liquid Glass của Apple: kính thuộc lớp
196
+ trên cùng của giao diện, và "dùng dè sẻn" nghĩa là không rải thành biến thể.
197
+
198
+ Nền nút hạ độ đục qua --x-fill (tách riêng nên text-primary không bị ảnh
199
+ hưởng), cộng ba lớp còn lại của kính: mờ nền, viền sáng, vệt sáng bên trong. */
200
+ :root[data-surface="glass"] .ui-button,
201
+ [data-surface="glass"] .ui-button {
202
+ --primary-fill: color-mix(in oklab, var(--primary) 68%, transparent);
203
+ --primary-fill-hover: color-mix(in oklab, var(--primary) 84%, transparent);
204
+ --secondary-fill: color-mix(in oklab, var(--secondary) 68%, transparent);
205
+ --secondary-fill-hover: color-mix(in oklab, var(--secondary) 84%, transparent);
206
+ --destructive-fill: color-mix(in oklab, var(--destructive) 68%, transparent);
207
+ --destructive-fill-hover: color-mix(in oklab, var(--destructive) 84%, transparent);
208
+ --success-fill: color-mix(in oklab, var(--success) 68%, transparent);
209
+ --success-fill-hover: color-mix(in oklab, var(--success) 84%, transparent);
210
+ --warning-fill: color-mix(in oklab, var(--warning) 68%, transparent);
211
+ --warning-fill-hover: color-mix(in oklab, var(--warning) 84%, transparent);
212
+ --info-fill: color-mix(in oklab, var(--info) 68%, transparent);
213
+ --info-fill-hover: color-mix(in oklab, var(--info) 84%, transparent);
214
+
215
+ /* PHẢI KHAI LẠI CẢ `--button-*`, không chỉ `--x-fill`.
216
+ `components/button.css` khai `--button-default-bg: var(--primary-fill)` ở
217
+ `:root`, mà custom property thay thế NGAY tại chỗ khai — nên nó đã chốt giá
218
+ trị `--primary-fill` của `:root` và mười ba dòng ngay trên không còn với
219
+ tới nó. Đo được lúc quên bước này: sáu nút trong khối trưng bày kính đi từ
220
+ `oklab(… / 0.68)` sang đục hoàn toàn.
221
+ Khai lại ở ĐÂY thì chúng giải theo `--x-fill` của chính phần tử này.
222
+ Đây là cái giá có thật của tầng token theo component: mọi chỗ ghi đè theo
223
+ PHẠM VI đều phải biết tới nó. */
224
+ --button-default-bg: var(--primary-fill);
225
+ --button-default-bg-hover: var(--primary-fill-hover);
226
+ --button-default-bg-active: color-mix(in oklab, var(--primary) 92%, transparent);
227
+ --button-secondary-bg: var(--secondary-fill);
228
+ --button-secondary-bg-hover: var(--secondary-fill-hover);
229
+ --button-secondary-bg-active: color-mix(in oklab, var(--secondary) 92%, transparent);
230
+ --button-destructive-bg: var(--destructive-fill);
231
+ --button-destructive-bg-hover: var(--destructive-fill-hover);
232
+ --button-destructive-bg-active: color-mix(in oklab, var(--destructive) 92%, transparent);
233
+ --button-success-bg: var(--success-fill);
234
+ --button-success-bg-hover: var(--success-fill-hover);
235
+ --button-success-bg-active: color-mix(in oklab, var(--success) 92%, transparent);
236
+ --button-warning-bg: var(--warning-fill);
237
+ --button-warning-bg-hover: var(--warning-fill-hover);
238
+ --button-warning-bg-active: color-mix(in oklab, var(--warning) 92%, transparent);
239
+ --button-info-bg: var(--info-fill);
240
+ --button-info-bg-hover: var(--info-fill-hover);
241
+ --button-info-bg-active: color-mix(in oklab, var(--info) 92%, transparent);
242
+
243
+ -webkit-backdrop-filter: var(--surface-filter);
244
+ backdrop-filter: var(--surface-filter);
245
+ border-color: var(--glass-border);
246
+
247
+ /* Tách ra biến để rule :focus-visible bên dưới ghép lại được. `box-shadow`
248
+ là một thuộc tính duy nhất chứ không cộng dồn, nên đặt thẳng ở đây sẽ
249
+ XOÁ SẠCH vòng focus — ở theme kính người dùng bàn phím không còn thấy
250
+ mình đang ở nút nào. Lỗi này có sẵn từ trước, hồi vòng focus còn là
251
+ `ring-2` (utility đó cũng ghi vào chính box-shadow). */
252
+ --glass-button-shadow:
253
+ inset 0 2px 4px var(--glass-highlight),
254
+ 0 10px 24px -10px color-mix(in oklab, var(--color-black) 40%, transparent);
255
+ box-shadow: var(--glass-button-shadow);
256
+ }
257
+
258
+ /* Vòng focus xếp TRƯỚC bóng kính: danh sách box-shadow vẽ từ trái sang nên
259
+ lớp đầu nằm trên cùng. */
260
+ :root[data-surface="glass"] .ui-button:focus-visible,
261
+ [data-surface="glass"] .ui-button:focus-visible {
262
+ box-shadow: var(--focus-ring), var(--glass-button-shadow);
263
+ }
264
+
265
+ /* Nút KHÔNG có nền (ghost, link): tắt luôn backdrop-filter.
266
+ `backdrop-filter` vẫn làm mờ + tăng bão hoà vùng phía sau kể cả khi element
267
+ trong suốt hoàn toàn, nên nó vẽ ra một mảng chữ nhật thấy rõ — nút link tự
268
+ nhiên mọc nền. Không nền thì không có gì để làm kính. */
269
+ :root[data-surface="glass"] .ui-button:where(.bg-transparent),
270
+ [data-surface="glass"] .ui-button:where(.bg-transparent) {
271
+ -webkit-backdrop-filter: none;
272
+ backdrop-filter: none;
273
+ box-shadow: none;
274
+ }
275
+
276
+ /* Nút không nền vẫn phải thấy được lúc focus — nó chỉ không có bóng kính. */
277
+ :root[data-surface="glass"] .ui-button:where(.bg-transparent):focus-visible,
278
+ [data-surface="glass"] .ui-button:where(.bg-transparent):focus-visible {
279
+ box-shadow: var(--focus-ring);
280
+ }
281
+
282
+ /* outline dùng `bg-background`, mà utility đó giải qua --color-background khai ở
283
+ :root — ghi đè --background ngay trên nút KHÔNG ăn. Phải đặt thẳng màu nền. */
284
+ /* Nút `outline` ở theme kính phải TRONG SUỐT, nếu không nó có nền đục và không
285
+ còn gì để backdrop-filter làm mờ.
286
+
287
+ Bản trước bắt theo TÊN CLASS (`:where(.bg-background)`) — cách đó chết ngay
288
+ khi bảng cva đổi sang `bg-(--button-outline-bg)`, vì Tailwind sinh ra một tên
289
+ class khác. Đặt thẳng token thì không phụ thuộc tên class nào. */
290
+ :root[data-surface="glass"] .ui-button,
291
+ [data-surface="glass"] .ui-button {
292
+ --button-outline-bg: transparent;
293
+ }
294
+
295
+ /* Thanh trượt: BA MẶT, ba độ sâu khác nhau. Hạ độ đục thôi là chưa đủ — kính
296
+ đọc ra được là nhờ chênh lệch độ sâu, nên rãnh phải LÕM (bóng inset + vệt
297
+ sáng ở mép dưới) còn núm phải NỔI (vệt sáng mép trên, mép dưới tối, bóng đổ).
298
+ Thiếu bước này thì thanh trượt chỉ là ba mảng phẳng mờ mờ chồng lên nhau —
299
+ đúng cái cảm giác "chưa giống kính" của bản trước.
300
+
301
+ KHÔNG có backdrop-filter ở đâu trong thanh trượt cả, hai lý do đều ĐO ĐƯỢC:
302
+
303
+ 1. Nó không làm gì. Phía sau một dải cao 6px luôn là một mặt phẳng (card,
304
+ hoặc gradient nền vốn đã rất mượt) — làm mờ một vùng phẳng thì ra đúng
305
+ vùng phẳng đó. Đo pixel có/không lọc: lệch 4/255, mà phần lệch đó là do
306
+ `saturate` chứ không phải `blur`.
307
+ 2. Với cái núm 16px nó còn phản tác dụng: blur lấy mẫu rộng gấp đôi chính
308
+ hình, và Chromium kẹp mép ở border-box nên mép dọc của phần đã chọn
309
+ không tan đi mà nhoè thành một khối chữ nhật nằm giữa hình tròn. */
310
+
311
+ /* Rãnh: một khe LÕM. Bóng inset nằm DƯỚI con, nên phần đã chọn che nó đi ở
312
+ nửa trái — đúng cảm giác chất lỏng đã lấp đầy khe. */
313
+ :root[data-surface="glass"] .ui-slider-track,
314
+ [data-surface="glass"] .ui-slider-track {
315
+ box-shadow:
316
+ inset 0 1px 2px color-mix(in oklab, var(--color-black) 22%, transparent),
317
+ 0 1px 0 var(--glass-highlight);
318
+ }
319
+
320
+ /* Phần đã chọn chỉ cần vệt sáng ở mép trên để đọc ra như chất lỏng có màu. */
321
+ :root[data-surface="glass"] .ui-slider-range,
322
+ [data-surface="glass"] .ui-slider-range {
323
+ box-shadow: inset 0 1px 0 var(--glass-highlight);
324
+ }
325
+
326
+ /* Núm phải ĐỤC, kể cả ở theme kính — cùng lý do với khối code.
327
+
328
+ Mép phải của phần đã chọn dừng đúng ở TÂM núm, nên núm trong mờ bao nhiêu
329
+ thì lộ ra bấy nhiêu một khối chữ nhật xanh nằm giữa hình tròn. Đo bước nhảy
330
+ màu giữa hai nửa núm (kênh mạnh nhất, trên 255):
331
+
332
+ đục 62% -> 38 80% -> 21 90% -> 11 95% -> 5 100% -> 0
333
+
334
+ Tức muốn sạch thì phải gần như đục hoàn toàn; "vừa trong vừa sạch" không
335
+ phải một lựa chọn có thật ở đây. Kính của thanh trượt vì vậy nằm ở RÃNH và
336
+ phần đã chọn, còn núm là thấu kính: viền sáng, vệt sáng bên trong, bóng đổ.
337
+ iOS cũng làm đúng vậy — núm của nó là một đĩa đặc có bóng. */
338
+ :root[data-surface="glass"] .ui-slider-thumb,
339
+ [data-surface="glass"] .ui-slider-thumb {
340
+ /* Tách ra biến, đúng lý do của --glass-button-shadow: `box-shadow` là MỘT
341
+ thuộc tính chứ không cộng dồn, đặt thẳng ở đây là xoá sạch `hover:ring-4`
342
+ và `focus-visible:shadow-focus` — hai utility đó cũng ghi vào chính
343
+ box-shadow. Hai rule dưới ghép lại thủ công. */
344
+ --glass-thumb-shadow:
345
+ inset 0 1px 1px var(--glass-highlight),
346
+ inset 0 -1px 1px color-mix(in oklab, var(--color-black) 12%, transparent),
347
+ 0 2px 6px -2px color-mix(in oklab, var(--color-black) 45%, transparent);
348
+ box-shadow: var(--glass-thumb-shadow);
349
+ }
350
+
351
+ /* Giữ nguyên quầng `hover:ring-4 hover:ring-ring/20` của núm. */
352
+ :root[data-surface="glass"] .ui-slider-thumb:hover,
353
+ [data-surface="glass"] .ui-slider-thumb:hover {
354
+ box-shadow:
355
+ 0 0 0 4px color-mix(in oklab, var(--ring) 20%, transparent),
356
+ var(--glass-thumb-shadow);
357
+ }
358
+
359
+ /* Vòng focus xếp TRƯỚC bóng kính: danh sách box-shadow vẽ từ trái sang nên lớp
360
+ đầu nằm trên cùng. */
361
+ :root[data-surface="glass"] .ui-slider-thumb:focus-visible,
362
+ [data-surface="glass"] .ui-slider-thumb:focus-visible {
363
+ box-shadow: var(--focus-ring), var(--glass-thumb-shadow);
364
+ }
@@ -0,0 +1,163 @@
1
+ /* =============================================================================
2
+ Cầu nối Tailwind v4 — nạp file này thay cho `index.css` khi app dùng Tailwind.
3
+
4
+ @import "tailwindcss" source(none);
5
+ @import "@vestow/theme/css/tailwind.css";
6
+
7
+ Nó thêm đúng hai thứ vào bộ token thuần ở `index.css`:
8
+ 1. `@custom-variant dark` — để variant `dark:` bám vào class `.dark`
9
+ (chứ không phải `prefers-color-scheme`, vì người dùng chọn được).
10
+ 2. `@theme inline` — ánh xạ CSS var sang utility (`bg-background`,
11
+ `rounded-lg`, `shadow-focus`…). `inline` = Tailwind đọc giá trị var tại
12
+ chỗ, nhờ vậy `dark:` và các variant khác hoạt động đúng.
13
+
14
+ KHÔNG cần `theme(static)` nữa: các nấc màu gốc do `primitives.css` cung cấp
15
+ chứ không còn đi vay bảng màu mặc định của Tailwind. Trước đây thiếu
16
+ `theme(static)` là Tailwind cây-rung sạch 273 biến `--color-*` và mọi token
17
+ trỏ vào chúng ra rỗng — nút thành trong suốt.
18
+ ========================================================================== */
19
+
20
+ @import "./index.css";
21
+
22
+ @custom-variant dark (&:is(.dark *));
23
+
24
+ /* Ánh xạ CSS var -> utility Tailwind. `inline` = Tailwind đọc giá trị var
25
+ tại chỗ, nhờ vậy `dark:` và các variant khác hoạt động đúng. */
26
+ @theme inline {
27
+ --color-background: var(--background);
28
+ --color-foreground: var(--foreground);
29
+
30
+ --color-card: var(--card);
31
+ --color-card-foreground: var(--card-foreground);
32
+
33
+ --color-popover: var(--popover);
34
+ --color-popover-foreground: var(--popover-foreground);
35
+
36
+ --color-primary: var(--primary);
37
+ --color-primary-foreground: var(--primary-foreground);
38
+
39
+ --color-secondary: var(--secondary);
40
+ --color-secondary-foreground: var(--secondary-foreground);
41
+
42
+ --color-muted: var(--muted);
43
+ --color-muted-foreground: var(--muted-foreground);
44
+
45
+ --color-accent: var(--accent);
46
+ --color-accent-foreground: var(--accent-foreground);
47
+ --color-menu-surface: var(--menu-surface);
48
+ --color-menu-accent: var(--menu-accent);
49
+ --color-menu-accent-foreground: var(--menu-accent-foreground);
50
+
51
+ --color-destructive: var(--destructive);
52
+ --color-destructive-foreground: var(--destructive-foreground);
53
+
54
+ --color-success: var(--success);
55
+ --color-success-foreground: var(--success-foreground);
56
+
57
+ --color-warning: var(--warning);
58
+ --color-warning-foreground: var(--warning-foreground);
59
+
60
+ --color-info: var(--info);
61
+ --color-info-foreground: var(--info-foreground);
62
+
63
+ --color-primary-soft: var(--primary-soft);
64
+ --color-primary-soft-border: var(--primary-soft-border);
65
+ --color-primary-soft-foreground: var(--primary-soft-foreground);
66
+ --color-primary-soft-hover: var(--primary-soft-hover);
67
+
68
+ --color-destructive-soft: var(--destructive-soft);
69
+ --color-destructive-soft-border: var(--destructive-soft-border);
70
+ --color-destructive-soft-foreground: var(--destructive-soft-foreground);
71
+ --color-destructive-soft-hover: var(--destructive-soft-hover);
72
+
73
+ --color-success-soft: var(--success-soft);
74
+ --color-success-soft-border: var(--success-soft-border);
75
+ --color-success-soft-foreground: var(--success-soft-foreground);
76
+ --color-success-soft-hover: var(--success-soft-hover);
77
+
78
+ --color-warning-soft: var(--warning-soft);
79
+ --color-warning-soft-border: var(--warning-soft-border);
80
+ --color-warning-soft-foreground: var(--warning-soft-foreground);
81
+ --color-warning-soft-hover: var(--warning-soft-hover);
82
+
83
+ --color-info-soft: var(--info-soft);
84
+ --color-info-soft-border: var(--info-soft-border);
85
+ --color-info-soft-foreground: var(--info-soft-foreground);
86
+ --color-info-soft-hover: var(--info-soft-hover);
87
+
88
+ --color-primary-fill: var(--primary-fill);
89
+ --color-primary-fill-hover: var(--primary-fill-hover);
90
+ --color-secondary-fill: var(--secondary-fill);
91
+ --color-secondary-fill-hover: var(--secondary-fill-hover);
92
+ --color-destructive-fill: var(--destructive-fill);
93
+ --color-destructive-fill-hover: var(--destructive-fill-hover);
94
+ --color-success-fill: var(--success-fill);
95
+ --color-success-fill-hover: var(--success-fill-hover);
96
+ --color-warning-fill: var(--warning-fill);
97
+ --color-warning-fill-hover: var(--warning-fill-hover);
98
+ --color-info-fill: var(--info-fill);
99
+ --color-info-fill-hover: var(--info-fill-hover);
100
+ --color-glass-border: var(--glass-border);
101
+ --color-glass-highlight: var(--glass-highlight);
102
+
103
+ --color-slider-track: var(--slider-track);
104
+ --color-slider-range: var(--slider-range);
105
+ --color-slider-thumb: var(--slider-thumb);
106
+ --color-slider-thumb-border: var(--slider-thumb-border);
107
+
108
+ --color-border: var(--border);
109
+ --color-input: var(--input);
110
+ --color-ring: var(--ring);
111
+
112
+ --color-sidebar: var(--sidebar);
113
+ --color-sidebar-foreground: var(--sidebar-foreground);
114
+ --color-sidebar-primary: var(--sidebar-primary);
115
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
116
+ --color-sidebar-accent: var(--sidebar-accent);
117
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
118
+ --color-sidebar-border: var(--sidebar-border);
119
+ --color-sidebar-ring: var(--sidebar-ring);
120
+
121
+ --color-chart-1-foreground: var(--chart-1-foreground);
122
+ --color-chart-2-foreground: var(--chart-2-foreground);
123
+ --color-chart-3-foreground: var(--chart-3-foreground);
124
+ --color-chart-4-foreground: var(--chart-4-foreground);
125
+ --color-chart-5-foreground: var(--chart-5-foreground);
126
+ --color-chart-1: var(--chart-1);
127
+ --color-chart-2: var(--chart-2);
128
+ --color-chart-3: var(--chart-3);
129
+ --color-chart-4: var(--chart-4);
130
+ --color-chart-5: var(--chart-5);
131
+ --color-code-bg: var(--code-bg);
132
+ --color-code-fg: var(--code-fg);
133
+ --color-code-comment: var(--code-comment);
134
+ --color-code-keyword: var(--code-keyword);
135
+ --color-code-entity: var(--code-entity);
136
+ --color-code-tag: var(--code-tag);
137
+ --color-code-component: var(--code-component);
138
+ --color-code-string: var(--code-string);
139
+ --color-code-number: var(--code-number);
140
+
141
+ /* Sinh ra `shadow-focus` và `shadow-focus-invalid`. */
142
+ --shadow-focus: var(--focus-ring);
143
+ --shadow-focus-invalid: var(--focus-ring-invalid);
144
+
145
+ --radius-sm: var(--radius-sm);
146
+ --radius-md: var(--radius-md);
147
+ --radius-lg: var(--radius-lg);
148
+ --radius-xl: var(--radius-xl);
149
+ --radius-2xl: var(--radius-2xl);
150
+
151
+ /* Tailwind v4 lấy --font-sans làm --default-font-family và --font-mono làm
152
+ --default-mono-font-family, nên body và <code> tự đổi, không cần rắc
153
+ `font-sans` thủ công ở đâu cả. Giá trị thật khai ở tokens.css. */
154
+ --font-heading: var(--font-heading);
155
+ --font-sans: var(--font-sans);
156
+ --font-mono: var(--font-mono);
157
+
158
+ /* Sinh ra `animate-accordion-down`… — keyframes đã khai ở tokens.css. */
159
+ --animate-collapsible-down: var(--animate-collapsible-down);
160
+ --animate-collapsible-up: var(--animate-collapsible-up);
161
+ --animate-accordion-down: var(--animate-accordion-down);
162
+ --animate-accordion-up: var(--animate-accordion-up);
163
+ }