@xihan-ui/styles 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE +21 -0
  3. package/README.md +27 -0
  4. package/css/accordion.css +108 -0
  5. package/css/affix.css +27 -0
  6. package/css/alert.css +108 -0
  7. package/css/anchor.css +137 -0
  8. package/css/avatar-group.css +82 -0
  9. package/css/avatar.css +55 -0
  10. package/css/back-top.css +61 -0
  11. package/css/badge.css +50 -0
  12. package/css/breadcrumb.css +127 -0
  13. package/css/button-group.css +131 -0
  14. package/css/button.css +98 -0
  15. package/css/calendar.css +219 -0
  16. package/css/card.css +116 -0
  17. package/css/carousel.css +193 -0
  18. package/css/cascader.css +397 -0
  19. package/css/checkbox-group.css +185 -0
  20. package/css/checkbox.css +91 -0
  21. package/css/clipboard.css +133 -0
  22. package/css/code-block.css +81 -0
  23. package/css/collapsible.css +63 -0
  24. package/css/color-picker.css +424 -0
  25. package/css/combobox.css +433 -0
  26. package/css/composer.css +127 -0
  27. package/css/context-menu.css +263 -0
  28. package/css/countdown.css +21 -0
  29. package/css/date-field.css +200 -0
  30. package/css/date-picker.css +231 -0
  31. package/css/descriptions.css +200 -0
  32. package/css/dialog.css +127 -0
  33. package/css/drawer.css +372 -0
  34. package/css/dynamic-input.css +157 -0
  35. package/css/editable.css +249 -0
  36. package/css/ellipsis.css +41 -0
  37. package/css/empty-state.css +78 -0
  38. package/css/field.css +65 -0
  39. package/css/file-upload.css +298 -0
  40. package/css/flex.css +95 -0
  41. package/css/float-button.css +121 -0
  42. package/css/form.css +185 -0
  43. package/css/gradient-text.css +62 -0
  44. package/css/grid.css +428 -0
  45. package/css/highlight.css +21 -0
  46. package/css/hover-card.css +146 -0
  47. package/css/icon-wrapper.css +64 -0
  48. package/css/icon.css +54 -0
  49. package/css/image.css +45 -0
  50. package/css/infinite-scroll.css +22 -0
  51. package/css/layers.css +2 -0
  52. package/css/layout.css +169 -0
  53. package/css/list.css +117 -0
  54. package/css/listbox.css +174 -0
  55. package/css/loading-bar.css +66 -0
  56. package/css/log.css +66 -0
  57. package/css/marquee.css +182 -0
  58. package/css/mention.css +222 -0
  59. package/css/menu.css +144 -0
  60. package/css/menubar.css +271 -0
  61. package/css/motion.css +58 -0
  62. package/css/navigation-menu.css +270 -0
  63. package/css/number-animation.css +32 -0
  64. package/css/number-field.css +219 -0
  65. package/css/page-header.css +85 -0
  66. package/css/pagination.css +148 -0
  67. package/css/pin-input.css +157 -0
  68. package/css/popconfirm.css +150 -0
  69. package/css/popover.css +150 -0
  70. package/css/popselect.css +207 -0
  71. package/css/progress.css +37 -0
  72. package/css/qr-code.css +50 -0
  73. package/css/radio-group.css +101 -0
  74. package/css/rating.css +141 -0
  75. package/css/reset.css +22 -0
  76. package/css/result.css +104 -0
  77. package/css/scroll-area.css +176 -0
  78. package/css/select.css +264 -0
  79. package/css/separator.css +17 -0
  80. package/css/skeleton.css +78 -0
  81. package/css/slider.css +197 -0
  82. package/css/spinner.css +74 -0
  83. package/css/splitter.css +127 -0
  84. package/css/statistic.css +67 -0
  85. package/css/steps.css +252 -0
  86. package/css/switch.css +61 -0
  87. package/css/table.css +404 -0
  88. package/css/tabs.css +157 -0
  89. package/css/tags-input.css +360 -0
  90. package/css/text-field.css +195 -0
  91. package/css/thread.css +107 -0
  92. package/css/time-field.css +200 -0
  93. package/css/time-picker.css +347 -0
  94. package/css/time.css +20 -0
  95. package/css/timeline.css +284 -0
  96. package/css/toast.css +195 -0
  97. package/css/toaster.css +69 -0
  98. package/css/toggle-group.css +160 -0
  99. package/css/toggle.css +103 -0
  100. package/css/tone.css +85 -0
  101. package/css/toolbar.css +114 -0
  102. package/css/tooltip.css +109 -0
  103. package/css/tour.css +305 -0
  104. package/css/transfer.css +318 -0
  105. package/css/tree-select.css +441 -0
  106. package/css/tree.css +204 -0
  107. package/css/typography.css +151 -0
  108. package/css/undefined.css +38 -0
  109. package/css/virtualizer.css +104 -0
  110. package/css/visually-hidden.css +13 -0
  111. package/css/watermark.css +48 -0
  112. package/index.css +113 -0
  113. package/index.unlayered.css +16164 -0
  114. package/package.json +163 -0
package/css/badge.css ADDED
@@ -0,0 +1,50 @@
1
+ @layer xihan.components {
2
+ [data-scope='badge'][data-part='root'] {
3
+ display: inline-flex;
4
+ align-items: center;
5
+ gap: var(--xh-space-1);
6
+ padding-inline: var(--xh-space-2);
7
+ padding-block: var(--xh-space-0_5);
8
+ border: var(--xh-stroke-thin) solid transparent;
9
+ border-radius: var(--xh-shape-pill);
10
+ background: var(--xh-badge-bg, var(--xh-bg-subtle));
11
+ color: var(--xh-badge-fg, var(--xh-fg-default));
12
+ font-size: var(--xh-badge-font-size, var(--xh-font-size-xs));
13
+ font-weight: var(--xh-badge-font-weight, var(--xh-font-weight-medium));
14
+ line-height: var(--xh-leading-none);
15
+ white-space: nowrap;
16
+ }
17
+
18
+ /* —— 变体 —— */
19
+ /* 形态只决定颜色怎么用;用哪族颜色由 data-tone 决定,没写 tone 就沿用原来的品牌/中性配色 */
20
+ [data-scope='badge'][data-part='root'][data-variant='solid'] {
21
+ background: var(--xh-_tone, var(--xh-bg-brand));
22
+ color: var(--xh-_tone-on, var(--xh-fg-on-brand));
23
+ }
24
+
25
+ [data-scope='badge'][data-part='root'][data-variant='subtle'] {
26
+ background: var(--xh-_tone-subtle, var(--xh-bg-subtle));
27
+ color: var(--xh-_tone-fg, var(--xh-fg-default));
28
+ }
29
+
30
+ [data-scope='badge'][data-part='root'][data-variant='outline'] {
31
+ border-color: var(--xh-_tone-border, var(--xh-border-default));
32
+ background: transparent;
33
+ color: var(--xh-_tone-fg, var(--xh-fg-default));
34
+ }
35
+
36
+ /* —— 尺寸 —— */
37
+ /* 徽标是随文排的药丸,不占控件行高,档位走间距与字号而不是 control-h */
38
+ [data-scope='badge'][data-part='root'][data-size='sm'] {
39
+ gap: var(--xh-space-0_5);
40
+ padding-inline: var(--xh-space-1_5);
41
+ padding-block: var(--xh-space-0);
42
+ }
43
+
44
+ [data-scope='badge'][data-part='root'][data-size='lg'] {
45
+ gap: var(--xh-space-1_5);
46
+ padding-inline: var(--xh-space-3);
47
+ padding-block: var(--xh-space-1);
48
+ font-size: var(--xh-font-size-sm);
49
+ }
50
+ }
@@ -0,0 +1,127 @@
1
+ @layer xihan.components {
2
+ /* 根节点是 nav 地标,不参与排版:整条路径的行为全在 list 上,
3
+ 这里不给 display,UA 的 [hidden]{display:none} 就仍然压得住 */
4
+ [data-scope='breadcrumb'][data-part='root'] {
5
+ /* 尺寸档只换这两个私有槽,列表与链接都从 root 继承它们,换档不必给每个部件各写一条选择器 */
6
+ --xh-_breadcrumb-font-size: var(--xh-text-label-size);
7
+ --xh-_breadcrumb-gap: var(--xh-space-2);
8
+ /* 当前项与链接悬停的强调色随语气走。没写 data-tone 时 --xh-_tone-fg 没被声明,
9
+ 退回原来的前景色。聚焦环不在其列,它恒是 --xh-ring-focus */
10
+ --xh-_breadcrumb-accent-text: var(--xh-_tone-fg, var(--xh-fg-default));
11
+
12
+ color: var(--xh-breadcrumb-fg, var(--xh-fg-muted));
13
+ font-size: var(--xh-breadcrumb-font-size, var(--xh-_breadcrumb-font-size));
14
+ line-height: var(--xh-breadcrumb-leading, var(--xh-leading-tight));
15
+ }
16
+
17
+ /* —— 尺寸 —— */
18
+ /* 缺省档写在上面的基础规则里。间距量的是路径里文字与分隔符之间的距离,
19
+ 不在控件间距那把尺上(那把尺的大档与中档同值),三档一律取空间尺 */
20
+ [data-scope='breadcrumb'][data-part='root'][data-size='sm'] {
21
+ --xh-_breadcrumb-font-size: var(--xh-font-size-sm);
22
+ --xh-_breadcrumb-gap: var(--xh-space-1_5);
23
+ }
24
+
25
+ [data-scope='breadcrumb'][data-part='root'][data-size='lg'] {
26
+ --xh-_breadcrumb-font-size: var(--xh-font-size-lg);
27
+ --xh-_breadcrumb-gap: var(--xh-space-3);
28
+ }
29
+
30
+ /* ol 的项目符号与内外边距是 UA 给的,库里没有全局 preflight,只能在这儿自己抹平 */
31
+ [data-scope='breadcrumb'][data-part='list'] {
32
+ display: flex;
33
+ align-items: center;
34
+ /* 层级多到一行放不下就换行:宁可折行,也不要把路径压扁或撑出横向滚动 */
35
+ flex-wrap: wrap;
36
+ gap: var(--xh-breadcrumb-gap, var(--xh-_breadcrumb-gap));
37
+ margin: 0;
38
+ padding: 0;
39
+ list-style: none;
40
+ }
41
+
42
+ [data-scope='breadcrumb'][data-part='list'][hidden] {
43
+ display: none;
44
+ }
45
+
46
+ [data-scope='breadcrumb'][data-part='item'] {
47
+ display: inline-flex;
48
+ align-items: center;
49
+ /* 库里没有全局 preflight,列表项的外边距在这里自己抹平 */
50
+ margin: 0;
51
+ /* 允许被压缩,链接内部的省略号才有生效的余地 */
52
+ min-inline-size: 0;
53
+ }
54
+
55
+ [data-scope='breadcrumb'][data-part='item'][hidden] {
56
+ display: none;
57
+ }
58
+
59
+ [data-scope='breadcrumb'][data-part='link'] {
60
+ display: inline-flex;
61
+ align-items: center;
62
+ gap: var(--xh-breadcrumb-link-gap, var(--xh-space-1));
63
+ /* 一层的名字可能很长(文档标题、订单号),压不住会把整条路径撑开 */
64
+ max-inline-size: var(--xh-breadcrumb-link-max-w, 12rem);
65
+ padding-inline: var(--xh-breadcrumb-link-px, var(--xh-space-1));
66
+ border-radius: var(--xh-breadcrumb-link-radius, var(--xh-shape-control));
67
+ color: inherit;
68
+ text-decoration: none;
69
+ white-space: nowrap;
70
+ overflow: hidden;
71
+ text-overflow: ellipsis;
72
+ transition:
73
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
74
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
75
+ }
76
+
77
+ [data-scope='breadcrumb'][data-part='link'][hidden] {
78
+ display: none;
79
+ }
80
+
81
+ /* 当前页那条不再是"可去的地方":不给悬停反馈,指针也不摆出可点的样子。
82
+ 连接层同时拦下了它的点击并把它移出了 Tab 序列,这里只负责说清楚这件事 */
83
+ [data-scope='breadcrumb'][data-part='link']:not([data-current]):hover {
84
+ background: var(--xh-breadcrumb-link-bg-hover, var(--xh-bg-subtle-hover));
85
+ color: var(--xh-breadcrumb-link-fg-hover, var(--xh-_breadcrumb-accent-text));
86
+ }
87
+
88
+ [data-scope='breadcrumb'][data-part='link']:focus-visible {
89
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
90
+ outline-offset: var(--xh-ring-offset);
91
+ }
92
+
93
+ /* 当前页用字重与前景色区分,不加下划线:下划线是"可点"的信号,而它点不动 */
94
+ [data-scope='breadcrumb'][data-part='link'][data-current] {
95
+ color: var(--xh-breadcrumb-current-fg, var(--xh-_breadcrumb-accent-text));
96
+ font-weight: var(--xh-breadcrumb-current-font-weight, var(--xh-font-weight-medium));
97
+ cursor: default;
98
+ }
99
+
100
+ /* 分隔符与省略号都是纯视觉占位,读屏那边已被 aria-hidden 摘掉 */
101
+ [data-scope='breadcrumb'][data-part='separator'],
102
+ [data-scope='breadcrumb'][data-part='ellipsis'] {
103
+ display: inline-flex;
104
+ flex: none;
105
+ /* 两者也是列表项,宿主给 li 的外边距会把它们顶离同一行 */
106
+ margin: 0;
107
+ align-items: center;
108
+ justify-content: center;
109
+ color: var(--xh-breadcrumb-separator-fg, var(--xh-fg-subtle));
110
+ user-select: none;
111
+ }
112
+
113
+ [data-scope='breadcrumb'][data-part='separator'][hidden],
114
+ [data-scope='breadcrumb'][data-part='ellipsis'][hidden] {
115
+ display: none;
116
+ }
117
+
118
+ /* 分隔符常常是个图标:给一个跟着字号走的方框,斜杠与图标混用时基线才不跳 */
119
+ [data-scope='breadcrumb'][data-part='separator'] {
120
+ inline-size: var(--xh-breadcrumb-separator-size, 1em);
121
+ }
122
+
123
+ [data-scope='breadcrumb'][data-part='ellipsis'] {
124
+ inline-size: var(--xh-breadcrumb-ellipsis-size, var(--xh-space-5));
125
+ cursor: default;
126
+ }
127
+ }
@@ -0,0 +1,131 @@
1
+ @layer xihan.components {
2
+ /* 一组按钮连成一条:段与段之间不留缝,靠共边接上,所以不设 gap。
3
+ 排布只能读 data-orientation——role=group 不收 aria-orientation,连接层根本不输出它。
4
+ 圆角槽位在容器上先收成 0,组内每段都取得到,两端的圆角再逐段补回来 */
5
+ [data-scope='button-group'][data-part='root'] {
6
+ --xh-button-radius: 0;
7
+
8
+ display: inline-flex;
9
+ align-items: stretch;
10
+ }
11
+
12
+ [data-scope='button-group'][data-part='root'][data-orientation='horizontal'] {
13
+ flex-direction: row;
14
+ }
15
+
16
+ [data-scope='button-group'][data-part='root'][data-orientation='vertical'] {
17
+ flex-direction: column;
18
+ }
19
+
20
+ /* 给了 display 的角色节点必须补这条:display 声明会盖掉 UA 的 [hidden]{display:none},
21
+ 不补的话作者给节点加 hidden 后它照样显示 */
22
+ [data-scope='button-group'][data-part='root'][hidden] {
23
+ display: none;
24
+ }
25
+
26
+ /* —— 尺寸 —— */
27
+ /* 高度、内边距、间距、字号在容器上写一次,沿继承流给组内每一段。
28
+ 三档都写出来:自定义属性会一路继承,组里再套一组时内层不写就会沿用外层的档 */
29
+ [data-scope='button-group'][data-part='root'][data-size='sm'] {
30
+ --xh-button-h: var(--xh-control-h-sm);
31
+ --xh-button-px: var(--xh-control-px-sm);
32
+ --xh-button-gap: var(--xh-control-gap-sm);
33
+ --xh-button-font-size: var(--xh-font-size-sm);
34
+ }
35
+
36
+ [data-scope='button-group'][data-part='root'][data-size='md'] {
37
+ --xh-button-h: var(--xh-control-h-md);
38
+ --xh-button-px: var(--xh-control-px-md);
39
+ --xh-button-gap: var(--xh-control-gap-md);
40
+ --xh-button-font-size: var(--xh-font-size-md);
41
+ }
42
+
43
+ [data-scope='button-group'][data-part='root'][data-size='lg'] {
44
+ --xh-button-h: var(--xh-control-h-lg);
45
+ --xh-button-px: var(--xh-control-px-lg);
46
+ --xh-button-gap: var(--xh-control-gap-lg);
47
+ --xh-button-font-size: var(--xh-font-size-lg);
48
+ }
49
+
50
+ /* —— 形态 —— */
51
+ /* 只写按钮暴露出来的颜色槽位:段自己写了形态时走的是它自己那条规则,这里的槽位不参与。
52
+ 用哪族颜色由根上的 data-tone 决定,语气槽沿继承流下来,没写语气就沿用品牌/中性配色 */
53
+ [data-scope='button-group'][data-part='root'][data-variant='solid'] {
54
+ --xh-button-bg: var(--xh-_tone, var(--xh-bg-brand));
55
+ --xh-button-bg-hover: var(--xh-_tone-hover, var(--xh-bg-brand-hover));
56
+ --xh-button-bg-active: var(--xh-_tone-active, var(--xh-bg-brand-active));
57
+ --xh-button-fg: var(--xh-_tone-on, var(--xh-fg-on-brand));
58
+ }
59
+
60
+ [data-scope='button-group'][data-part='root'][data-variant='subtle'] {
61
+ --xh-button-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
62
+ --xh-button-bg-hover: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
63
+ --xh-button-bg-active: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
64
+ --xh-button-fg: var(--xh-_tone-fg, var(--xh-fg-default));
65
+ }
66
+
67
+ [data-scope='button-group'][data-part='root'][data-variant='outline'],
68
+ [data-scope='button-group'][data-part='root'][data-variant='ghost'] {
69
+ --xh-button-bg: transparent;
70
+ --xh-button-bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle));
71
+ --xh-button-bg-active: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-active));
72
+ --xh-button-fg: var(--xh-_tone-fg, var(--xh-fg-default));
73
+ }
74
+
75
+ /* 段的描边归组画(两个适配器各一条选择器:Vue 的段就是直接子节点,WC 的段在行为宿主里面):合边本来就是组的事,按钮那边没有描边色的槽位可以流 */
76
+ [data-scope='button-group'][data-part='root'][data-variant='outline'] > *,
77
+ [data-scope='button-group'][data-part='root'][data-variant='outline'] > * [data-scope='button'][data-part='root'] {
78
+ border-color: var(--xh-_tone-border, var(--xh-border-default));
79
+ }
80
+
81
+ /* —— 合边 —— */
82
+ /* 相邻两段的边重合在同一像素上,谁的描边浮在上面必须能指定,
83
+ 因此每一段都是定位元素——静态元素的 z-index 不生效 */
84
+ [data-scope='button-group'][data-part='root'] > * {
85
+ position: relative;
86
+ }
87
+
88
+ /* 后一段往回挪一个描边宽度,相邻的两条边重合成一条。
89
+ 方向跟着 data-orientation 走,横排收 inline 轴、竖排收 block 轴 */
90
+ [data-scope='button-group'][data-part='root'][data-orientation='horizontal'] > * + * {
91
+ margin-inline-start: calc(-1 * var(--xh-stroke-thin));
92
+ }
93
+
94
+ [data-scope='button-group'][data-part='root'][data-orientation='vertical'] > * + * {
95
+ margin-block-start: calc(-1 * var(--xh-stroke-thin));
96
+ }
97
+
98
+ /* 悬停或拿到焦点的那一段抬到最上层:焦点环画在元素外侧,
99
+ 不抬层就会被后一段的底色与描边盖掉一半 */
100
+ [data-scope='button-group'][data-part='root'] > *:hover,
101
+ [data-scope='button-group'][data-part='root'] > *:focus-within {
102
+ z-index: 1;
103
+ }
104
+
105
+ /* —— 两端圆角 —— */
106
+ /* 逻辑角属性写,rtl 与竖排都跟着换边,不必另写一套。
107
+ 只认直接子节点:段之间插了别的节点时宁可退化成直角,也不要圆错位置 */
108
+ [data-scope='button-group'][data-part='root'][data-orientation='horizontal'] > *:first-child,
109
+ [data-scope='button-group'][data-part='root'][data-orientation='horizontal'] > *:first-child [data-scope='button'][data-part='root'] {
110
+ border-start-start-radius: var(--xh-button-group-radius, var(--xh-shape-control));
111
+ border-end-start-radius: var(--xh-button-group-radius, var(--xh-shape-control));
112
+ }
113
+
114
+ [data-scope='button-group'][data-part='root'][data-orientation='horizontal'] > *:last-child,
115
+ [data-scope='button-group'][data-part='root'][data-orientation='horizontal'] > *:last-child [data-scope='button'][data-part='root'] {
116
+ border-start-end-radius: var(--xh-button-group-radius, var(--xh-shape-control));
117
+ border-end-end-radius: var(--xh-button-group-radius, var(--xh-shape-control));
118
+ }
119
+
120
+ [data-scope='button-group'][data-part='root'][data-orientation='vertical'] > *:first-child,
121
+ [data-scope='button-group'][data-part='root'][data-orientation='vertical'] > *:first-child [data-scope='button'][data-part='root'] {
122
+ border-start-start-radius: var(--xh-button-group-radius, var(--xh-shape-control));
123
+ border-start-end-radius: var(--xh-button-group-radius, var(--xh-shape-control));
124
+ }
125
+
126
+ [data-scope='button-group'][data-part='root'][data-orientation='vertical'] > *:last-child,
127
+ [data-scope='button-group'][data-part='root'][data-orientation='vertical'] > *:last-child [data-scope='button'][data-part='root'] {
128
+ border-end-start-radius: var(--xh-button-group-radius, var(--xh-shape-control));
129
+ border-end-end-radius: var(--xh-button-group-radius, var(--xh-shape-control));
130
+ }
131
+ }
package/css/button.css ADDED
@@ -0,0 +1,98 @@
1
+ @layer xihan.components {
2
+ [data-scope='button'][data-part='root'] {
3
+ /* 私有槽位:组件令牌 → 语义令牌两级回退,变体只改这几个槽位 */
4
+ --xh-_bg: var(--xh-button-bg, var(--xh-bg-subtle));
5
+ --xh-_bg-hover: var(--xh-button-bg-hover, var(--xh-bg-subtle-hover));
6
+ --xh-_bg-active: var(--xh-button-bg-active, var(--xh-bg-subtle-active));
7
+ --xh-_fg: var(--xh-button-fg, var(--xh-fg-default));
8
+
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ gap: var(--xh-button-gap, var(--xh-control-gap-md));
13
+ block-size: var(--xh-button-h, var(--xh-control-h-md));
14
+ padding-inline: var(--xh-button-px, var(--xh-control-px-md));
15
+ border: var(--xh-stroke-thin) solid transparent;
16
+ border-radius: var(--xh-button-radius, var(--xh-shape-control));
17
+ background: var(--xh-_bg);
18
+ color: var(--xh-_fg);
19
+ font-size: var(--xh-button-font-size, var(--xh-text-label-size));
20
+ font-weight: var(--xh-button-font-weight, var(--xh-text-label-weight));
21
+ line-height: var(--xh-leading-none);
22
+ white-space: nowrap;
23
+ cursor: pointer;
24
+ user-select: none;
25
+ transition:
26
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
27
+ border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
28
+ }
29
+
30
+ [data-scope='button'][data-part='root']:hover {
31
+ background: var(--xh-_bg-hover);
32
+ }
33
+
34
+ [data-scope='button'][data-part='root']:active {
35
+ background: var(--xh-_bg-active);
36
+ }
37
+
38
+ [data-scope='button'][data-part='root']:focus-visible {
39
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
40
+ outline-offset: var(--xh-ring-offset);
41
+ }
42
+
43
+ [data-scope='button'][data-part='root'][data-disabled],
44
+ [data-scope='button'][data-part='root'][data-loading] {
45
+ cursor: not-allowed;
46
+ opacity: var(--xh-state-disabled-opacity);
47
+ }
48
+
49
+ /* —— 变体 —— */
50
+ /* 形态只决定颜色怎么用;用哪族颜色由 data-tone 决定,没写 tone 就沿用原来的品牌/中性配色 */
51
+ [data-scope='button'][data-part='root'][data-variant='solid'] {
52
+ --xh-_bg: var(--xh-_tone, var(--xh-bg-brand));
53
+ --xh-_bg-hover: var(--xh-_tone-hover, var(--xh-bg-brand-hover));
54
+ --xh-_bg-active: var(--xh-_tone-active, var(--xh-bg-brand-active));
55
+ --xh-_fg: var(--xh-_tone-on, var(--xh-fg-on-brand));
56
+ }
57
+
58
+ [data-scope='button'][data-part='root'][data-variant='subtle'] {
59
+ --xh-_bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
60
+ --xh-_bg-hover: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
61
+ --xh-_bg-active: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
62
+ --xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
63
+ }
64
+
65
+ [data-scope='button'][data-part='root'][data-variant='outline'] {
66
+ --xh-_bg: transparent;
67
+ --xh-_bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle));
68
+ --xh-_bg-active: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-active));
69
+ --xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
70
+
71
+ border-color: var(--xh-_tone-border, var(--xh-border-default));
72
+ }
73
+
74
+ [data-scope='button'][data-part='root'][data-variant='ghost'] {
75
+ --xh-_bg: transparent;
76
+ --xh-_bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle));
77
+ --xh-_bg-active: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-active));
78
+ --xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
79
+ }
80
+
81
+ /* —— 尺寸 —— */
82
+ [data-scope='button'][data-part='root'][data-size='sm'] {
83
+ block-size: var(--xh-control-h-sm);
84
+ padding-inline: var(--xh-control-px-sm);
85
+ font-size: var(--xh-font-size-sm);
86
+ }
87
+
88
+ [data-scope='button'][data-part='root'][data-size='lg'] {
89
+ block-size: var(--xh-control-h-lg);
90
+ padding-inline: var(--xh-control-px-lg);
91
+ font-size: var(--xh-font-size-lg);
92
+ }
93
+
94
+ [data-scope='button'][data-part='indicator'] {
95
+ display: inline-flex;
96
+ animation: xh-spin 0.6s linear infinite;
97
+ }
98
+ }
@@ -0,0 +1,219 @@
1
+ @layer xihan.components {
2
+ [data-scope='calendar'][data-part='root'] {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: var(--xh-calendar-gap, var(--xh-space-3));
6
+ /* 数字等宽:1 与 7 的字形宽度在比例字体里并不相同,不定住会让整张网格随月份抖动 */
7
+ font-variant-numeric: tabular-nums;
8
+ }
9
+
10
+ [data-scope='calendar'][data-part='root'][hidden] {
11
+ display: none;
12
+ }
13
+
14
+ [data-scope='calendar'][data-part='header'] {
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: space-between;
18
+ gap: var(--xh-calendar-header-gap, var(--xh-space-2));
19
+ }
20
+
21
+ [data-scope='calendar'][data-part='header'][hidden] {
22
+ display: none;
23
+ }
24
+
25
+ [data-scope='calendar'][data-part='heading'] {
26
+ flex: 1;
27
+ text-align: center;
28
+ color: var(--xh-calendar-heading-fg, var(--xh-fg-default));
29
+ font-size: var(--xh-calendar-heading-font-size, var(--xh-text-label-size));
30
+ font-weight: var(--xh-calendar-heading-font-weight, var(--xh-font-weight-semibold));
31
+ line-height: var(--xh-leading-none);
32
+ user-select: none;
33
+ }
34
+
35
+ [data-scope='calendar'][data-part='prev-trigger'],
36
+ [data-scope='calendar'][data-part='next-trigger'] {
37
+ display: inline-flex;
38
+ flex: none;
39
+ align-items: center;
40
+ justify-content: center;
41
+ inline-size: var(--xh-calendar-nav-size, var(--xh-control-h-sm));
42
+ block-size: var(--xh-calendar-nav-size, var(--xh-control-h-sm));
43
+ border: var(--xh-stroke-thin) solid transparent;
44
+ border-radius: var(--xh-calendar-nav-radius, var(--xh-shape-control));
45
+ background: var(--xh-calendar-nav-bg, transparent);
46
+ color: var(--xh-calendar-nav-fg, var(--xh-fg-muted));
47
+ cursor: pointer;
48
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
49
+ }
50
+
51
+ [data-scope='calendar'][data-part='prev-trigger'][hidden],
52
+ [data-scope='calendar'][data-part='next-trigger'][hidden] {
53
+ display: none;
54
+ }
55
+
56
+ [data-scope='calendar'][data-part='prev-trigger']:hover:not(:disabled),
57
+ [data-scope='calendar'][data-part='next-trigger']:hover:not(:disabled) {
58
+ background: var(--xh-calendar-nav-bg-hover, var(--xh-bg-subtle-hover));
59
+ color: var(--xh-fg-default);
60
+ }
61
+
62
+ /* 翻月是单体控件,禁用走原生 :disabled(连接层输出的正是原生 disabled) */
63
+ [data-scope='calendar'][data-part='prev-trigger']:disabled,
64
+ [data-scope='calendar'][data-part='next-trigger']:disabled {
65
+ color: var(--xh-fg-disabled);
66
+ cursor: not-allowed;
67
+ }
68
+
69
+ [data-scope='calendar'][data-part='prev-trigger']:focus-visible,
70
+ [data-scope='calendar'][data-part='next-trigger']:focus-visible {
71
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
72
+ outline-offset: var(--xh-ring-offset);
73
+ }
74
+
75
+ [data-scope='calendar'][data-part='grid'] {
76
+ display: flex;
77
+ flex-direction: column;
78
+ gap: var(--xh-calendar-grid-gap, var(--xh-space-1));
79
+ }
80
+
81
+ [data-scope='calendar'][data-part='grid'][hidden] {
82
+ display: none;
83
+ }
84
+
85
+ /* 网格自己不落焦(焦点永远在某一格上),这里不画环 */
86
+ [data-scope='calendar'][data-part='grid']:focus-visible {
87
+ outline: none;
88
+ }
89
+
90
+ [data-scope='calendar'][data-part='grid-head'],
91
+ [data-scope='calendar'][data-part='grid-body'] {
92
+ display: flex;
93
+ flex-direction: column;
94
+ gap: var(--xh-calendar-row-gap, var(--xh-space-0));
95
+ }
96
+
97
+ [data-scope='calendar'][data-part='grid-head'][hidden],
98
+ [data-scope='calendar'][data-part='grid-body'][hidden] {
99
+ display: none;
100
+ }
101
+
102
+ /* 七等分:表头行与日期行共用同一条轨道定义,列才对得齐 */
103
+ [data-scope='calendar'][data-part='week-row'] {
104
+ display: grid;
105
+ grid-template-columns: repeat(7, minmax(0, 1fr));
106
+ }
107
+
108
+ [data-scope='calendar'][data-part='week-row'][hidden] {
109
+ display: none;
110
+ }
111
+
112
+ [data-scope='calendar'][data-part='week-day'] {
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: center;
116
+ block-size: var(--xh-calendar-week-day-h, var(--xh-control-h-sm));
117
+ color: var(--xh-calendar-week-day-fg, var(--xh-fg-subtle));
118
+ font-size: var(--xh-calendar-week-day-font-size, var(--xh-font-size-xs));
119
+ font-weight: var(--xh-calendar-week-day-font-weight, var(--xh-font-weight-medium));
120
+ line-height: var(--xh-leading-none);
121
+ user-select: none;
122
+ }
123
+
124
+ [data-scope='calendar'][data-part='week-day'][hidden] {
125
+ display: none;
126
+ }
127
+
128
+ /* 格子是区间底色的画布:连续背景铺在这一层,圆角的选中片留给 trigger。
129
+ 铺在 trigger 上会被格间空隙切断,区间看起来就是一串断开的圆点 */
130
+ [data-scope='calendar'][data-part='cell'] {
131
+ display: flex;
132
+ align-items: center;
133
+ justify-content: center;
134
+ padding: var(--xh-calendar-cell-gap, 2px);
135
+ }
136
+
137
+ [data-scope='calendar'][data-part='cell'][hidden] {
138
+ display: none;
139
+ }
140
+
141
+ [data-scope='calendar'][data-part='cell'][data-in-range] {
142
+ background: var(--xh-calendar-range-bg, var(--xh-bg-brand-subtle));
143
+ }
144
+
145
+ /* 区间两端各自收圆:中间那些格子保持直角,底色才连成一条 */
146
+ [data-scope='calendar'][data-part='cell'][data-range-start] {
147
+ border-start-start-radius: var(--xh-calendar-cell-radius, var(--xh-shape-pill));
148
+ border-end-start-radius: var(--xh-calendar-cell-radius, var(--xh-shape-pill));
149
+ }
150
+
151
+ [data-scope='calendar'][data-part='cell'][data-range-end] {
152
+ border-start-end-radius: var(--xh-calendar-cell-radius, var(--xh-shape-pill));
153
+ border-end-end-radius: var(--xh-calendar-cell-radius, var(--xh-shape-pill));
154
+ }
155
+
156
+ [data-scope='calendar'][data-part='cell-trigger'] {
157
+ display: flex;
158
+ flex: 1;
159
+ align-items: center;
160
+ justify-content: center;
161
+ /* 正方格:宽度由 week-row 的等分轨道给,高度跟着走,日历才不会被长月份拉扁 */
162
+ aspect-ratio: 1;
163
+ min-inline-size: var(--xh-calendar-cell-size, var(--xh-control-h-sm));
164
+ border: var(--xh-stroke-thin) solid transparent;
165
+ border-radius: var(--xh-calendar-cell-radius, var(--xh-shape-pill));
166
+ color: var(--xh-calendar-cell-fg, var(--xh-fg-default));
167
+ font-size: var(--xh-calendar-cell-font-size, var(--xh-text-body-size));
168
+ line-height: var(--xh-leading-none);
169
+ cursor: pointer;
170
+ user-select: none;
171
+ transition:
172
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
173
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
174
+ }
175
+
176
+ [data-scope='calendar'][data-part='cell-trigger'][hidden] {
177
+ display: none;
178
+ }
179
+
180
+ /* 邻月的日子照样可点,只是压暗——它们不是占位符 */
181
+ [data-scope='calendar'][data-part='cell-trigger'][data-outside-month] {
182
+ color: var(--xh-calendar-cell-fg-outside, var(--xh-fg-subtle));
183
+ }
184
+
185
+ [data-scope='calendar'][data-part='cell-trigger']:not([data-disabled]):not([data-selected]):hover {
186
+ background: var(--xh-calendar-cell-bg-hover, var(--xh-bg-subtle-hover));
187
+ }
188
+
189
+ /* 今天:只描一圈边,不占用选中色——今天与被选中是两件事,得能同时看得出来 */
190
+ [data-scope='calendar'][data-part='cell-trigger'][data-today] {
191
+ border-color: var(--xh-calendar-today-border, var(--xh-border-strong));
192
+ }
193
+
194
+ [data-scope='calendar'][data-part='cell-trigger'][data-selected] {
195
+ border-color: transparent;
196
+ background: var(--xh-calendar-cell-bg-selected, var(--xh-bg-brand));
197
+ color: var(--xh-calendar-cell-fg-selected, var(--xh-fg-on-brand));
198
+ font-weight: var(--xh-font-weight-medium);
199
+ }
200
+
201
+ /* 不可用走 aria-disabled 而非原生 disabled:格子仍要可聚焦(它还得当方向键的起点),
202
+ 所以只能靠 data-disabled 表达,写 :disabled 永远不命中 */
203
+ [data-scope='calendar'][data-part='cell-trigger'][data-disabled] {
204
+ color: var(--xh-fg-disabled);
205
+ cursor: not-allowed;
206
+ }
207
+
208
+ [data-scope='calendar'][data-part='cell-trigger'][data-disabled][data-selected] {
209
+ background: var(--xh-bg-muted);
210
+ color: var(--xh-fg-disabled);
211
+ }
212
+
213
+ /* 焦点环画在格子上:整张网格只有一格在 Tab 序列里,落焦点必须一眼看得见。
214
+ 往内收一像素,免得被相邻格子的底色盖住 */
215
+ [data-scope='calendar'][data-part='cell-trigger']:focus-visible {
216
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
217
+ outline-offset: calc(-1 * var(--xh-ring-width));
218
+ }
219
+ }