glasscn-core 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.
@@ -0,0 +1,589 @@
1
+ /* ============================================================================
2
+ * glasscn-core — 零侵入自动层 (Auto Layer)
3
+ * ----------------------------------------------------------------------------
4
+ * shadcn 每个组件都会输出 data-slot 属性(<div data-slot="card">)。
5
+ * 挂在这些属性上,就能把已有项目里的 shadcn 组件就地变成玻璃:
6
+ * 一行组件源码不用改,也不用重新 shadcn add。
7
+ *
8
+ * 规则:
9
+ * · 全部写在 @layer components 里 → Tailwind 工具类永远赢,用户可覆写
10
+ * · 只补 shadcn 不设的属性:backdrop-filter、微光、药丸圆角
11
+ * · 颜色 / 阴影 / 描边交给 tokens/bridge.css 的变量接管
12
+ * · 主按钮排除在玻璃之外 —— 它是实心强调色
13
+ * · 任意元素加 data-glass="off" 即可局部退出
14
+ * ==========================================================================*/
15
+
16
+ @layer components {
17
+ /* --- 通用玻璃面 -------------------------------------------------------- */
18
+ /* 名单手工挑选,不用 [data-slot] 通配 —— 通配会让长列表里每个 cell
19
+ 都起一个合成层。规范另一条硬约束:模糊层最多叠三层。 */
20
+
21
+ [data-slot="card"],
22
+ [data-slot="alert"],
23
+ [data-slot="dialog-content"],
24
+ [data-slot="sheet-content"],
25
+ [data-slot="drawer-content"],
26
+ [data-slot="popover-content"],
27
+ [data-slot="hover-card-content"],
28
+ [data-slot="tooltip-content"],
29
+ [data-slot="dropdown-menu-content"],
30
+ [data-slot="dropdown-menu-sub-content"],
31
+ [data-slot="context-menu-content"],
32
+ [data-slot="menubar-content"],
33
+ [data-slot="select-content"],
34
+ [data-slot="select-trigger"],
35
+ [data-slot="command"],
36
+ [data-slot="navigation-menu-content"],
37
+ [data-slot="navigation-menu-viewport"],
38
+ [data-slot="sidebar"],
39
+ [data-slot="tabs-list"],
40
+ [data-slot="toggle-group"],
41
+ [data-slot="input"],
42
+ [data-slot="textarea"],
43
+ [data-slot="badge"],
44
+ [data-slot="toggle"],
45
+ [data-slot="accordion-item"],
46
+ [data-slot="table-container"],
47
+ [data-slot="pagination-link"],
48
+ [data-slot="avatar"] {
49
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
50
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
51
+ transition-property: transform, box-shadow, background-color, border-color;
52
+ transition-duration:
53
+ var(--glass-duration),
54
+ var(--glass-duration),
55
+ var(--glass-duration-color),
56
+ var(--glass-duration-color);
57
+ transition-timing-function:
58
+ var(--glass-ease),
59
+ var(--glass-ease),
60
+ var(--glass-ease-out),
61
+ var(--glass-ease-out);
62
+ }
63
+
64
+ /* 按钮单独一条:只有非实心变体才是玻璃。
65
+ 主按钮和危险按钮是实心色块,加 backdrop-filter 会让色块透出背景,
66
+ 直接毁掉可供性。 */
67
+ [data-slot="button"]:not([data-variant="default"], [data-variant="destructive"]) {
68
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
69
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
70
+ }
71
+
72
+ [data-slot="button"] {
73
+ transition-property: transform, box-shadow, background-color, border-color, filter;
74
+ transition-duration:
75
+ var(--glass-duration),
76
+ var(--glass-duration),
77
+ var(--glass-duration-color),
78
+ var(--glass-duration-color),
79
+ var(--glass-duration-color);
80
+ transition-timing-function:
81
+ var(--glass-ease),
82
+ var(--glass-ease),
83
+ var(--glass-ease-out),
84
+ var(--glass-ease-out),
85
+ var(--glass-ease-out);
86
+ }
87
+
88
+ /* --- 形状:接管 --radius,不写 border-radius 属性 ------------------------ */
89
+ /*
90
+ * 两条都要绕开:
91
+ *
92
+ * 1. 组件写的是 rounded-md / rounded-lg / rounded-xl,属于 @layer utilities。
93
+ * 在 @layer components 里写 border-radius 属性一定输 —— 这也正是这套
94
+ * 架构自己的规则:接管变量,不覆写属性。
95
+ *
96
+ * 2. shadcn 的 globals.css 用 @theme inline,Tailwind 会把量表**内联**掉:
97
+ * .rounded-sm → calc(var(--radius) - 4px)
98
+ * .rounded-md → calc(var(--radius) - 2px)
99
+ * .rounded-lg → var(--radius)
100
+ * .rounded-xl → calc(var(--radius) + 4px)
101
+ * 所以改 --radius-md 是没用的,要改的是 --radius 本身,
102
+ * 并且按该元素用的是哪一级把偏移量反解回去。
103
+ *
104
+ * 两个名字都设:非 inline 的 @theme 走 --radius-*,inline 的走 --radius。
105
+ */
106
+
107
+ /* 药丸 —— 用 rounded-md,所以 --radius 要比目标大 2px */
108
+ [data-slot="button"],
109
+ [data-slot="badge"],
110
+ [data-slot="input"],
111
+ [data-slot="select-trigger"],
112
+ [data-slot="toggle"],
113
+ [data-slot="pagination-link"],
114
+ [data-slot="tabs-trigger"] {
115
+ --radius: calc(var(--glass-radius-pill) + 2px);
116
+ --radius-md: var(--glass-radius-pill);
117
+ }
118
+
119
+ /* 药丸容器 —— 用 rounded-lg,偏移为 0 */
120
+ [data-slot="tabs-list"],
121
+ [data-slot="toggle-group"] {
122
+ --radius: var(--glass-radius-pill);
123
+ --radius-lg: var(--glass-radius-pill);
124
+ }
125
+
126
+ /* 多行输入不做药丸 —— 文本会贴着圆弧。用 rounded-md,给卡片圆角。 */
127
+ [data-slot="textarea"] {
128
+ --radius: calc(var(--glass-radius-card) + 2px);
129
+ --radius-md: var(--glass-radius-card);
130
+ }
131
+
132
+ /* 浮层与菜单 —— 用 rounded-md,给卡片圆角 */
133
+ [data-slot="popover-content"],
134
+ [data-slot="hover-card-content"],
135
+ [data-slot="dropdown-menu-content"],
136
+ [data-slot="dropdown-menu-sub-content"],
137
+ [data-slot="context-menu-content"],
138
+ [data-slot="menubar-content"],
139
+ [data-slot="select-content"],
140
+ [data-slot="tooltip-content"] {
141
+ --radius: calc(var(--glass-radius-card) + 2px);
142
+ --radius-md: var(--glass-radius-card);
143
+ }
144
+
145
+ /* 大面板 —— 用 rounded-lg,给 28px */
146
+ [data-slot="dialog-content"],
147
+ [data-slot="sheet-content"],
148
+ [data-slot="drawer-content"],
149
+ [data-slot="command"] {
150
+ --radius: var(--glass-radius-panel);
151
+ --radius-lg: var(--glass-radius-panel);
152
+ }
153
+
154
+ /* 容器类 —— 用 rounded-lg,给卡片圆角 */
155
+ [data-slot="alert"],
156
+ [data-slot="accordion-item"],
157
+ [data-slot="table-container"] {
158
+ --radius: var(--glass-radius-card);
159
+ --radius-lg: var(--glass-radius-card);
160
+ }
161
+
162
+ /* 骨架屏 —— 用 rounded-md */
163
+ [data-slot="skeleton"] {
164
+ --radius: calc(var(--glass-radius-md) + 2px);
165
+ --radius-md: var(--glass-radius-md);
166
+ }
167
+
168
+ /* 列表项 —— 用 rounded-sm,偏移 -4px */
169
+ [data-slot="dropdown-menu-item"],
170
+ [data-slot="dropdown-menu-checkbox-item"],
171
+ [data-slot="dropdown-menu-radio-item"],
172
+ [data-slot="context-menu-item"],
173
+ [data-slot="menubar-item"],
174
+ [data-slot="select-item"],
175
+ [data-slot="command-item"] {
176
+ --radius: calc(var(--glass-radius-sm) + 4px);
177
+ --radius-sm: var(--glass-radius-sm);
178
+ }
179
+
180
+ /* --- 高度分档 ---------------------------------------------------------- */
181
+
182
+ /* flat —— 贴在背景上 */
183
+ [data-slot="input"],
184
+ [data-slot="textarea"],
185
+ [data-slot="select-trigger"],
186
+ [data-slot="badge"],
187
+ [data-slot="avatar"],
188
+ [data-slot="table-container"],
189
+ [data-slot="accordion-item"] {
190
+ --glass-blur: var(--glass-blur-flat);
191
+ --glass-fill-current: var(--glass-fill-flat);
192
+ --glass-shadow: var(--glass-shadow-flat);
193
+ }
194
+
195
+ /* raised —— 微微抬起 */
196
+ [data-slot="card"],
197
+ [data-slot="alert"],
198
+ [data-slot="button"],
199
+ [data-slot="toggle"],
200
+ [data-slot="toggle-group"],
201
+ [data-slot="tabs-list"],
202
+ [data-slot="pagination-link"] {
203
+ --glass-blur: var(--glass-blur-raised);
204
+ --glass-fill-current: var(--glass-fill-raised);
205
+ --glass-shadow: var(--glass-shadow-raised);
206
+ }
207
+
208
+ /* overlay —— 浮在内容之上 */
209
+ [data-slot="popover-content"],
210
+ [data-slot="hover-card-content"],
211
+ [data-slot="tooltip-content"],
212
+ [data-slot="navigation-menu-content"],
213
+ [data-slot="navigation-menu-viewport"],
214
+ [data-slot="sidebar"] {
215
+ --glass-blur: var(--glass-blur-overlay);
216
+ --glass-fill-current: var(--glass-fill-overlay);
217
+ --glass-shadow: var(--glass-shadow-overlay);
218
+ }
219
+
220
+ /* 菜单类单独一档:blur 28 saturate 200%,填充 .10、描边 .14。
221
+ 比 modal 稍薄但饱和更高 —— 菜单浮在内容正上方很近的距离,
222
+ 太厚会让下面的内容彻底消失,失去空间关系。 */
223
+ [data-slot="dropdown-menu-content"],
224
+ [data-slot="dropdown-menu-sub-content"],
225
+ [data-slot="context-menu-content"],
226
+ [data-slot="menubar-content"],
227
+ [data-slot="select-content"] {
228
+ --glass-blur: 28px;
229
+ --glass-saturate: 200%;
230
+ --glass-fill-current: 0.1;
231
+ --glass-shadow: 0 20px 60px rgb(0 0 0 / 0.4);
232
+ --popover: rgb(var(--glass-white) / 0.1);
233
+ border-color: rgb(var(--glass-white) / 0.14);
234
+ box-shadow:
235
+ 0 1px 0 0 rgb(var(--glass-white) / 0.18) inset,
236
+ var(--glass-shadow);
237
+ overflow: hidden;
238
+ }
239
+
240
+ /* modal —— 盖住全屏 */
241
+ [data-slot="dialog-content"],
242
+ [data-slot="sheet-content"],
243
+ [data-slot="drawer-content"],
244
+ [data-slot="command"] {
245
+ --glass-blur: var(--glass-blur-modal);
246
+ --glass-saturate: var(--glass-saturate-modal);
247
+ --glass-fill-current: var(--glass-fill-modal);
248
+ --glass-shadow: var(--glass-shadow-modal);
249
+
250
+ /* shadcn 的 DialogContent 写的是 bg-background,而 --background 必须
251
+ 保持不透明(它是页面底)。在这一个元素上局部改写变量本身:
252
+ bg-background 就地解析成玻璃填充,用户写 className="bg-…" 依然生效。
253
+ 两个名字都写:@theme inline 编成 var(--background),
254
+ 非 inline 的 @theme 编成 var(--color-background)。 */
255
+ --background: rgb(var(--glass-white) / var(--glass-fill-modal));
256
+ --color-background: var(--background);
257
+ }
258
+
259
+ /* --- 遮罩层:景深,不是色块 -------------------------------------------- */
260
+
261
+ [data-slot="dialog-overlay"],
262
+ [data-slot="sheet-overlay"],
263
+ [data-slot="drawer-overlay"],
264
+ [data-slot="alert-dialog-overlay"] {
265
+ background-color: var(--glass-scrim);
266
+ -webkit-backdrop-filter: blur(var(--glass-scrim-blur)) saturate(140%);
267
+ backdrop-filter: blur(var(--glass-scrim-blur)) saturate(140%);
268
+ }
269
+
270
+ /* --- 浮层物化(materialize)-------------------------------------------- */
271
+ /*
272
+ * 面板不能"啪"地出现 -- 那是直接操作感的反义词。三层动效:
273
+ *
274
+ * 遮罩 opacity 淡入 200ms,淡出更快 -- 「调暗以聚焦」
275
+ * 面板 scale 0.96->1 + 从触发方向 4px 位移,240ms,阻尼 1.0。
276
+ * transform-origin 用 Radix 给的 --radix-*-transform-origin,
277
+ * 面板从触发它的按钮那一侧长出来 -- 空间关系一目了然
278
+ * 退场 沿入场同一路径反向走,160ms(快于入场 --
279
+ * 离开不需要仪式感),这是「对称路径」原则
280
+ *
281
+ * 为什么用 @keyframes 而不是 transition:Radix 的 Presence
282
+ * 靠 animationName 判断"退出动画是否在途"来延迟卸载 --
283
+ * transition 不触发 animationend,退出动画会被瞬间卸载吞掉。
284
+ * keyframes 也天然可中断:重新打开时新动画从头播,没有终态残留。
285
+ */
286
+
287
+ [data-slot="dialog-overlay"],
288
+ [data-slot="sheet-overlay"],
289
+ [data-slot="drawer-overlay"],
290
+ [data-slot="alert-dialog-overlay"] {
291
+ animation: glass-fade-in var(--glass-duration-enter) var(--glass-ease-out);
292
+ }
293
+ [data-slot="dialog-overlay"][data-state="closed"],
294
+ [data-slot="sheet-overlay"][data-state="closed"],
295
+ [data-slot="drawer-overlay"][data-state="closed"],
296
+ [data-slot="alert-dialog-overlay"][data-state="closed"] {
297
+ animation: glass-fade-out var(--glass-duration-exit) var(--glass-ease-out);
298
+ }
299
+
300
+ /* modal:居中物化。scale 起点比浮层更低 -- 更"厚"的东西动得更重。
301
+ 基准 translate(-50%,-50%) 是 shadcn Dialog 的居中手段,keyframes
302
+ 里必须带上,否则第一帧跳位。 */
303
+ [data-slot="dialog-content"],
304
+ [data-slot="command"] {
305
+ animation: glass-modal-in var(--glass-duration-enter) var(--glass-ease);
306
+ }
307
+ [data-slot="dialog-content"][data-state="closed"],
308
+ [data-slot="command"][data-state="closed"] {
309
+ animation: glass-modal-out var(--glass-duration-exit) var(--glass-ease);
310
+ }
311
+
312
+ /* Sheet / Drawer:从边缘滑入,data-side 决定方向 --
313
+ 从哪来回哪去,对称路径。 */
314
+ [data-slot="sheet-content"],
315
+ [data-slot="drawer-content"] {
316
+ animation: glass-sheet-in-bottom var(--glass-duration-enter) var(--glass-ease);
317
+ }
318
+ [data-slot="sheet-content"][data-state="closed"],
319
+ [data-slot="drawer-content"][data-state="closed"] {
320
+ animation: glass-sheet-out-bottom var(--glass-duration-exit) var(--glass-ease);
321
+ }
322
+ [data-slot="sheet-content"][data-side="top"],
323
+ [data-slot="drawer-content"][data-side="top"] {
324
+ animation-name: glass-sheet-in-top;
325
+ }
326
+ [data-slot="sheet-content"][data-side="top"][data-state="closed"],
327
+ [data-slot="drawer-content"][data-side="top"][data-state="closed"] {
328
+ animation-name: glass-sheet-out-top;
329
+ }
330
+ [data-slot="sheet-content"][data-side="left"],
331
+ [data-slot="drawer-content"][data-side="left"] {
332
+ animation-name: glass-sheet-in-left;
333
+ }
334
+ [data-slot="sheet-content"][data-side="left"][data-state="closed"],
335
+ [data-slot="drawer-content"][data-side="left"][data-state="closed"] {
336
+ animation-name: glass-sheet-out-left;
337
+ }
338
+ [data-slot="sheet-content"][data-side="right"],
339
+ [data-slot="drawer-content"][data-side="right"] {
340
+ animation-name: glass-sheet-in-right;
341
+ }
342
+ [data-slot="sheet-content"][data-side="right"][data-state="closed"],
343
+ [data-slot="drawer-content"][data-side="right"][data-state="closed"] {
344
+ animation-name: glass-sheet-out-right;
345
+ }
346
+
347
+ /* 锚定浮层(popover / 菜单 / select):从触发方向长出来。
348
+ 4px 朝 origin 的反方向收拢 -- origin 在顶(内容在按钮下方)时
349
+ 面板从下方 4px 处上浮到位。 */
350
+ [data-slot="popover-content"],
351
+ [data-slot="hover-card-content"],
352
+ [data-slot="dropdown-menu-content"],
353
+ [data-slot="dropdown-menu-sub-content"],
354
+ [data-slot="context-menu-content"],
355
+ [data-slot="menubar-content"],
356
+ [data-slot="select-content"] {
357
+ animation: glass-pop-in var(--glass-duration-enter) var(--glass-ease);
358
+ }
359
+ [data-slot="popover-content"][data-state="closed"],
360
+ [data-slot="hover-card-content"][data-state="closed"],
361
+ [data-slot="dropdown-menu-content"][data-state="closed"],
362
+ [data-slot="dropdown-menu-sub-content"][data-state="closed"],
363
+ [data-slot="context-menu-content"][data-state="closed"],
364
+ [data-slot="menubar-content"][data-state="closed"],
365
+ [data-slot="select-content"][data-state="closed"] {
366
+ animation: glass-pop-out var(--glass-duration-exit) var(--glass-ease);
367
+ }
368
+
369
+ /* tooltip:更快更轻,它是瞬时反馈不是面板 */
370
+ [data-slot="tooltip-content"] {
371
+ animation: glass-tip-in 120ms var(--glass-ease);
372
+ }
373
+ [data-slot="tooltip-content"][data-state="closed"] {
374
+ animation: glass-tip-out 100ms var(--glass-ease-out);
375
+ }
376
+
377
+ /* 手风琴:内容沿纵轴展开。grid-template-rows 0fr->1fr 过渡,
378
+ 比 max-height hack 干净(不需要猜上界)。
379
+ keyframes 里的 0fr/1fr 关键帧在支持 grid 过渡的浏览器上平滑,
380
+ 不支持的退成两帧切换 -- 仍然可见、仍然对称。 */
381
+ [data-slot="accordion-content"] {
382
+ overflow: hidden;
383
+ animation: none;
384
+ }
385
+ [data-slot="accordion-content"][data-state="open"] {
386
+ animation: glass-accordion-in var(--glass-duration) var(--glass-ease);
387
+ }
388
+ [data-slot="accordion-content"][data-state="closed"] {
389
+ animation: glass-accordion-out var(--glass-duration-exit) var(--glass-ease);
390
+ }
391
+
392
+ /* --- 交互态 ------------------------------------------------------------ */
393
+
394
+ [data-slot="card"]:hover,
395
+ [data-slot="alert"]:hover {
396
+ background-color: rgb(var(--glass-white) / var(--glass-fill-hover));
397
+ border-color: var(--glass-border-hover);
398
+ transform: translateY(calc(var(--glass-lift) * -1));
399
+ box-shadow:
400
+ var(--glass-specular-size) var(--glass-specular-color) inset,
401
+ var(--glass-underline),
402
+ var(--glass-shadow-hover);
403
+ }
404
+
405
+ /* 玻璃按钮:抬 2px(不是卡片的 4px —— 按钮更小,4px 会显得跳) */
406
+ [data-slot="button"]:not([data-variant="default"], [data-variant="destructive"]):hover:not(
407
+ :disabled,
408
+ [aria-disabled="true"]
409
+ ),
410
+ [data-slot="toggle"]:hover:not(:disabled, [aria-disabled="true"]),
411
+ [data-slot="pagination-link"]:hover,
412
+ [data-slot="select-trigger"]:hover:not([data-disabled]) {
413
+ background-color: rgb(var(--glass-white) / var(--glass-fill-hover));
414
+ border-color: var(--glass-border-hover);
415
+ transform: translateY(-2px);
416
+ }
417
+
418
+ /* 实心按钮:提亮而不是变透 */
419
+ [data-slot="button"][data-variant="default"]:hover:not(:disabled, [aria-disabled="true"]),
420
+ [data-slot="button"][data-variant="destructive"]:hover:not(:disabled, [aria-disabled="true"]) {
421
+ transform: translateY(-2px);
422
+ filter: brightness(1.1);
423
+ }
424
+
425
+ /* 禁用:实心按钮必须先褪成玻璃再降透明度。
426
+ 只降 opacity 的话,0.45 的琥珀还是一块醒目的暖色块,
427
+ 用户读作"可以点,只是颜色淡了些"—— 可供性没有真正撤销。 */
428
+ [data-slot="button"]:disabled,
429
+ [data-slot="button"][aria-disabled="true"] {
430
+ background-color: rgb(var(--glass-white) / 0.05) !important;
431
+ color: var(--glass-text-muted) !important;
432
+ border-color: rgb(var(--glass-white) / 0.08) !important;
433
+ box-shadow: none !important;
434
+ filter: none !important;
435
+ transform: none !important;
436
+ opacity: 1;
437
+ cursor: not-allowed;
438
+ }
439
+
440
+ /* 按下:三态里的第三态,80ms 内完成 —— 要"啪"地一下,不能拖 */
441
+ [data-slot="button"]:active:not(:disabled, [aria-disabled="true"]),
442
+ [data-slot="toggle"]:active:not(:disabled, [aria-disabled="true"]) {
443
+ transform: translateY(var(--glass-press)) scale(var(--glass-press-scale));
444
+ transition-duration: var(--glass-duration-fast);
445
+ }
446
+
447
+ /* 输入聚焦:描边提亮 + 外环 + 高光 */
448
+ [data-slot="input"]:focus-visible,
449
+ [data-slot="textarea"]:focus-visible,
450
+ [data-slot="select-trigger"]:focus-visible {
451
+ background-color: rgb(var(--glass-white) / 0.1);
452
+ border-color: rgb(var(--glass-white) / 0.3);
453
+ box-shadow:
454
+ var(--glass-specular-size) var(--glass-specular-color) inset,
455
+ 0 0 0 3px color-mix(in oklab, var(--glass-accent) 22%, transparent);
456
+ }
457
+
458
+ [data-slot="input"]::placeholder,
459
+ [data-slot="textarea"]::placeholder {
460
+ color: var(--glass-text-muted);
461
+ }
462
+
463
+ /* --- 菜单 / 列表项 ------------------------------------------------------ */
464
+ /* 不做位移也不做模糊:一个列表几十项,每项起合成层会直接掉帧。 */
465
+
466
+ [data-slot="dropdown-menu-item"],
467
+ [data-slot="dropdown-menu-checkbox-item"],
468
+ [data-slot="dropdown-menu-radio-item"],
469
+ [data-slot="context-menu-item"],
470
+ [data-slot="menubar-item"],
471
+ [data-slot="select-item"],
472
+ [data-slot="command-item"] {
473
+ transition: background-color var(--glass-duration-fast) var(--glass-ease-out);
474
+
475
+ &[data-highlighted],
476
+ &[data-state="checked"] {
477
+ background-color: rgb(var(--glass-white) / 0.1);
478
+ }
479
+ }
480
+
481
+ [data-slot="dropdown-menu-separator"],
482
+ [data-slot="context-menu-separator"],
483
+ [data-slot="menubar-separator"],
484
+ [data-slot="command-separator"],
485
+ [data-slot="separator"] {
486
+ background-color: rgb(var(--glass-white) / 0.1);
487
+ }
488
+
489
+ /* Tabs:选中项是浮在 list 玻璃之上的更亮的一块 */
490
+ [data-slot="tabs-trigger"] {
491
+ transition: background-color var(--glass-duration-color) var(--glass-ease-out),
492
+ box-shadow var(--glass-duration) var(--glass-ease);
493
+
494
+ &[data-state="active"] {
495
+ background-color: rgb(var(--glass-white) / 0.14);
496
+ box-shadow: 0 1px 0 0 var(--glass-specular-color) inset;
497
+ }
498
+ }
499
+
500
+ /* 开关 / 滑块:轨道是玻璃槽,滑块保持实心,靠对比建立可供性 */
501
+ [data-slot="switch"] {
502
+ -webkit-backdrop-filter: blur(var(--glass-blur-flat));
503
+ backdrop-filter: blur(var(--glass-blur-flat));
504
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.3) inset;
505
+ }
506
+
507
+ /* 拨杆是这套体系里唯一的动量交互 -- 手指把它拨出去,它会带着
508
+ 惯性冲过头再回来。所以唯独这里用过冲曲线(阻尼 ~0.8),
509
+ hover/入场都不许用,只有"被甩出去的东西"才配弹。 */
510
+ [data-slot="switch-thumb"] {
511
+ box-shadow:
512
+ 0 1px 3px 0 rgb(0 0 0 / 0.4),
513
+ 0 1px 0 0 rgb(var(--glass-white) / 0.5) inset;
514
+ transition:
515
+ transform var(--glass-duration) var(--glass-ease-spring),
516
+ background-color var(--glass-duration-color) var(--glass-ease-out);
517
+ }
518
+
519
+ [data-slot="slider-track"] {
520
+ -webkit-backdrop-filter: blur(var(--glass-blur-flat));
521
+ backdrop-filter: blur(var(--glass-blur-flat));
522
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.3) inset;
523
+ }
524
+
525
+ [data-slot="slider-thumb"] {
526
+ box-shadow:
527
+ 0 1px 4px 0 rgb(0 0 0 / 0.4),
528
+ 0 1px 0 0 rgb(var(--glass-white) / 0.5) inset;
529
+ /* hover 的放大是跟随意图,阻尼 1.0;按压要"啪",80ms */
530
+ transition: transform var(--glass-duration) var(--glass-ease);
531
+
532
+ &:hover { transform: scale(1.12); }
533
+ &:active {
534
+ transform: scale(1.04);
535
+ transition-duration: var(--glass-duration-fast);
536
+ }
537
+ }
538
+
539
+ /* --- 骨架屏 ------------------------------------------------------------ */
540
+
541
+ [data-slot="skeleton"] {
542
+ animation: none;
543
+ background-color: rgb(var(--glass-white) / var(--glass-fill-flat));
544
+ background-image: linear-gradient(
545
+ 100deg,
546
+ transparent 20%,
547
+ rgb(var(--glass-white) / 0.08) 50%,
548
+ transparent 80%
549
+ );
550
+ background-size: 200% 100%;
551
+ animation: glass-skeleton-sweep 1.8s linear infinite;
552
+ }
553
+
554
+ /* --- 表格 -------------------------------------------------------------- */
555
+ /* 行不加 backdrop-filter:一千行就是一千个合成层。 */
556
+
557
+ [data-slot="table-header"] {
558
+ background-color: rgb(var(--glass-white) / 0.05);
559
+ }
560
+
561
+ [data-slot="table-row"] {
562
+ border-bottom-color: rgb(var(--glass-white) / 0.08);
563
+
564
+ &:hover {
565
+ background-color: rgb(var(--glass-white) / 0.05);
566
+ }
567
+ }
568
+
569
+ /* --- 嵌套保护:最多三层模糊 -------------------------------------------- */
570
+
571
+ [data-slot="card"] [data-slot="card"],
572
+ [data-slot="dialog-content"] [data-slot="card"],
573
+ [data-slot="sheet-content"] [data-slot="card"],
574
+ [data-slot="popover-content"] [data-slot="card"],
575
+ [data-slot="card"] [data-slot="alert"] {
576
+ -webkit-backdrop-filter: none;
577
+ backdrop-filter: none;
578
+ --glass-fill-current: 0.05;
579
+ --glass-shadow: none;
580
+ }
581
+
582
+ /* --- 局部退出 ---------------------------------------------------------- */
583
+
584
+ [data-glass="off"],
585
+ [data-glass="off"] * {
586
+ -webkit-backdrop-filter: none !important;
587
+ backdrop-filter: none !important;
588
+ }
589
+ }
@@ -0,0 +1,17 @@
1
+ /* ============================================================================
2
+ * glasscn-core/headless — 只要工具类,不自动接管 shadcn
3
+ * ----------------------------------------------------------------------------
4
+ * 适用于:不想让 shadcn 组件被自动改写,只手动用 glass-* 工具类;
5
+ * 或者你的组件不是 shadcn(没有 data-slot)。
6
+ * ==========================================================================*/
7
+
8
+ @import "./tokens/primitives.css";
9
+ @import "./tokens/palette.css";
10
+ @import "./tokens/elevation.css";
11
+ @import "./tokens/typography.css";
12
+
13
+ @import "./primitives/surface.css";
14
+ @import "./primitives/interaction.css";
15
+ @import "./primitives/backdrop.css";
16
+ @import "./primitives/reveal.css";
17
+ @import "./primitives/fallback.css";
package/src/index.css ADDED
@@ -0,0 +1,28 @@
1
+ /* ============================================================================
2
+ * glasscn-core — 完整入口
3
+ * ----------------------------------------------------------------------------
4
+ * 在你项目的 CSS 里,紧跟 shadcn 的变量之后引入:
5
+ *
6
+ * @import "tailwindcss";
7
+ * ... shadcn 的 :root / .dark 变量 ...
8
+ * @import "glasscn-core"; ← 这一行
9
+ * @import "glasscn-core/palettes/switchable.css"; ← 想运行时切调色再加
10
+ *
11
+ * 顺序很重要:glasscn 要在 shadcn 变量之后,才能接管 --card / --popover 等。
12
+ *
13
+ * 字体不在这里加载,只定义 token —— 见 tokens/typography.css 顶部说明。
14
+ * ==========================================================================*/
15
+
16
+ @import "./tokens/primitives.css";
17
+ @import "./tokens/palette.css";
18
+ @import "./tokens/elevation.css";
19
+ @import "./tokens/typography.css";
20
+ @import "./tokens/bridge.css";
21
+
22
+ @import "./primitives/surface.css";
23
+ @import "./primitives/interaction.css";
24
+ @import "./primitives/backdrop.css";
25
+ @import "./primitives/reveal.css";
26
+ @import "./primitives/fallback.css";
27
+
28
+ @import "./bridge/auto.css";
@@ -0,0 +1,35 @@
1
+ /* ============================================================================
2
+ * glasscn-core/palettes/ember-slate — Ember & Slate —— 暖调近黑 + 琥珀赭石。默认,适合产品后台与工具型界面
3
+ * ----------------------------------------------------------------------------
4
+ * 两种用法:
5
+ * 1. 静态套用 —— @import 本文件,全站生效
6
+ * 2. 运行时切换 —— <html data-glass-palette="ember-slate">
7
+ *
8
+ * 玻璃填充在所有调色里都是白色中性,这里只换背景光斑、强调色和文字色调。
9
+ * ==========================================================================*/
10
+
11
+ :root {
12
+ --glass-bg-base: #100d0a;
13
+ --glass-bg-mid: #1c1510;
14
+ --glass-blob-1: #c2410c;
15
+ --glass-blob-2: #78350f;
16
+ --glass-blob-3: #f59e0b;
17
+ --glass-accent: #f59e0b;
18
+ --glass-accent-hover: #fbbf24;
19
+ --glass-accent-fg: #1a1206;
20
+ --glass-text-rgb: 255 248 240;
21
+ color-scheme: dark;
22
+ }
23
+
24
+ :root[data-glass-palette="ember-slate"] {
25
+ --glass-bg-base: #100d0a;
26
+ --glass-bg-mid: #1c1510;
27
+ --glass-blob-1: #c2410c;
28
+ --glass-blob-2: #78350f;
29
+ --glass-blob-3: #f59e0b;
30
+ --glass-accent: #f59e0b;
31
+ --glass-accent-hover: #fbbf24;
32
+ --glass-accent-fg: #1a1206;
33
+ --glass-text-rgb: 255 248 240;
34
+ color-scheme: dark;
35
+ }