@xihan-ui/styles 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +1 -1
  3. package/component-tokens.d.ts +29 -0
  4. package/components.tokens.json +683 -27
  5. package/css/alert.css +2 -2
  6. package/css/approval.css +4 -4
  7. package/css/button.css +9 -5
  8. package/css/calendar-picker.css +3 -3
  9. package/css/calendar-range-picker.css +7 -7
  10. package/css/carousel.css +45 -13
  11. package/css/cartesian-chart.css +11 -11
  12. package/css/cascader.css +10 -10
  13. package/css/citation.css +2 -2
  14. package/css/code-view.css +6 -6
  15. package/css/color-picker.css +81 -0
  16. package/css/color-swatch-picker.css +9 -4
  17. package/css/combobox.css +12 -4
  18. package/css/command.css +1 -1
  19. package/css/date-field.css +39 -0
  20. package/css/date-picker.css +86 -9
  21. package/css/date-range-picker.css +47 -7
  22. package/css/diff-view.css +8 -8
  23. package/css/field-array.css +1 -1
  24. package/css/field.css +4 -0
  25. package/css/file-upload.css +1 -1
  26. package/css/float-button.css +140 -27
  27. package/css/form.css +14 -6
  28. package/css/graph-chart.css +2 -2
  29. package/css/highlight.css +1 -1
  30. package/css/image.css +1 -1
  31. package/css/mention.css +1 -1
  32. package/css/message-feed.css +1 -1
  33. package/css/notification.css +26 -20
  34. package/css/number-field.css +18 -18
  35. package/css/page-header.css +2 -2
  36. package/css/password-input.css +2 -2
  37. package/css/popconfirm.css +3 -3
  38. package/css/question-flow.css +8 -8
  39. package/css/radar-chart.css +6 -6
  40. package/css/rating.css +1 -1
  41. package/css/select.css +2 -2
  42. package/css/separator.css +2 -2
  43. package/css/side-nav.css +13 -13
  44. package/css/skeleton.css +5 -5
  45. package/css/slider.css +6 -6
  46. package/css/sortable.css +2 -2
  47. package/css/table.css +19 -2
  48. package/css/tags-input.css +1 -1
  49. package/css/time-picker.css +59 -2
  50. package/css/tool-call.css +7 -7
  51. package/css/tooltip.css +1 -1
  52. package/css/tree-select.css +2 -2
  53. package/css/tree.css +81 -11
  54. package/dist/css/accordion.css +269 -0
  55. package/dist/css/affix.css +47 -0
  56. package/dist/css/alert.css +247 -0
  57. package/dist/css/anchor.css +252 -0
  58. package/dist/css/approval.css +501 -0
  59. package/dist/css/avatar-group.css +99 -0
  60. package/dist/css/avatar.css +98 -0
  61. package/dist/css/back-top.css +161 -0
  62. package/dist/css/badge.css +205 -0
  63. package/dist/css/bar-code.css +62 -0
  64. package/dist/css/breadcrumb.css +230 -0
  65. package/dist/css/button-group.css +263 -0
  66. package/dist/css/button.css +219 -0
  67. package/dist/css/calendar-picker.css +467 -0
  68. package/dist/css/calendar-range-picker.css +554 -0
  69. package/dist/css/card.css +221 -0
  70. package/dist/css/carousel.css +710 -0
  71. package/dist/css/cartesian-chart.css +830 -0
  72. package/dist/css/cascader.css +961 -0
  73. package/dist/css/checkbox-group.css +358 -0
  74. package/dist/css/checkbox.css +257 -0
  75. package/dist/css/citation.css +470 -0
  76. package/dist/css/clipboard.css +315 -0
  77. package/dist/css/code-view.css +374 -0
  78. package/dist/css/collapsible.css +185 -0
  79. package/dist/css/color-field.css +176 -0
  80. package/dist/css/color-picker.css +595 -0
  81. package/dist/css/color-slider.css +281 -0
  82. package/dist/css/color-swatch-picker.css +250 -0
  83. package/dist/css/color-swatch.css +35 -0
  84. package/dist/css/combobox.css +574 -0
  85. package/dist/css/command.css +399 -0
  86. package/dist/css/context-menu.css +420 -0
  87. package/dist/css/date-field.css +317 -0
  88. package/dist/css/date-picker.css +868 -0
  89. package/dist/css/date-range-picker.css +886 -0
  90. package/dist/css/description.css +67 -0
  91. package/dist/css/descriptions.css +299 -0
  92. package/dist/css/dialog.css +455 -0
  93. package/dist/css/diff-view.css +513 -0
  94. package/dist/css/download-trigger.css +151 -0
  95. package/dist/css/drawer.css +552 -0
  96. package/dist/css/editable.css +295 -0
  97. package/dist/css/empty-state.css +163 -0
  98. package/dist/css/field-array.css +226 -0
  99. package/dist/css/field.css +166 -0
  100. package/dist/css/fieldset.css +102 -0
  101. package/dist/css/file-upload.css +429 -0
  102. package/dist/css/flex.css +441 -0
  103. package/dist/css/float-button.css +569 -0
  104. package/dist/css/floating-panel.css +429 -0
  105. package/dist/css/focus.css +46 -0
  106. package/dist/css/forced-colors.css +93 -0
  107. package/dist/css/form.css +393 -0
  108. package/dist/css/funnel-chart.css +106 -0
  109. package/dist/css/graph-chart.css +205 -0
  110. package/dist/css/grid-list.css +235 -0
  111. package/dist/css/grid.css +1201 -0
  112. package/dist/css/heatmap.css +552 -0
  113. package/dist/css/hierarchy-chart.css +298 -0
  114. package/dist/css/highlight.css +41 -0
  115. package/dist/css/hover-card.css +226 -0
  116. package/dist/css/icon.css +225 -0
  117. package/dist/css/image-cropper.css +373 -0
  118. package/dist/css/image-viewer.css +538 -0
  119. package/dist/css/image.css +98 -0
  120. package/dist/css/infinite-scroll.css +77 -0
  121. package/dist/css/input-group.css +275 -0
  122. package/dist/css/json-viewer.css +379 -0
  123. package/dist/css/kbd.css +72 -0
  124. package/dist/css/label.css +78 -0
  125. package/dist/css/layers.css +7 -0
  126. package/dist/css/layout.css +372 -0
  127. package/dist/css/liquid.css +106 -0
  128. package/dist/css/list.css +152 -0
  129. package/dist/css/listbox.css +390 -0
  130. package/dist/css/loading-bar.css +141 -0
  131. package/dist/css/log.css +306 -0
  132. package/dist/css/markdown-stream.css +149 -0
  133. package/dist/css/marquee.css +378 -0
  134. package/dist/css/masonry.css +70 -0
  135. package/dist/css/matrix-code.css +67 -0
  136. package/dist/css/mention.css +393 -0
  137. package/dist/css/menu.css +364 -0
  138. package/dist/css/menubar.css +456 -0
  139. package/dist/css/message-feed.css +318 -0
  140. package/dist/css/navigation-menu.css +513 -0
  141. package/dist/css/notification.css +782 -0
  142. package/dist/css/number-animation.css +44 -0
  143. package/dist/css/number-field.css +252 -0
  144. package/dist/css/overlay-arrow.css +56 -0
  145. package/dist/css/page-header.css +184 -0
  146. package/dist/css/pagination.css +450 -0
  147. package/dist/css/password-input.css +391 -0
  148. package/dist/css/pie-chart.css +247 -0
  149. package/dist/css/pin-input.css +199 -0
  150. package/dist/css/pointer.css +36 -0
  151. package/dist/css/popconfirm.css +320 -0
  152. package/dist/css/popover.css +279 -0
  153. package/dist/css/progress.css +501 -0
  154. package/dist/css/prompt-input.css +260 -0
  155. package/dist/css/question-flow.css +553 -0
  156. package/dist/css/radar-chart.css +233 -0
  157. package/dist/css/radio-group.css +598 -0
  158. package/dist/css/rating.css +294 -0
  159. package/dist/css/reasoning.css +343 -0
  160. package/dist/css/reset.css +117 -0
  161. package/dist/css/resizable.css +242 -0
  162. package/dist/css/sankey-chart.css +174 -0
  163. package/dist/css/scroll-area.css +240 -0
  164. package/dist/css/scrollbar.css +204 -0
  165. package/dist/css/select.css +612 -0
  166. package/dist/css/separator.css +122 -0
  167. package/dist/css/side-nav.css +476 -0
  168. package/dist/css/signature-pad.css +179 -0
  169. package/dist/css/skeleton.css +161 -0
  170. package/dist/css/slider.css +374 -0
  171. package/dist/css/sortable.css +228 -0
  172. package/dist/css/sparkline.css +164 -0
  173. package/dist/css/spinner.css +236 -0
  174. package/dist/css/splitter.css +159 -0
  175. package/dist/css/statistic.css +137 -0
  176. package/dist/css/steps.css +627 -0
  177. package/dist/css/switch.css +351 -0
  178. package/dist/css/table.css +1145 -0
  179. package/dist/css/tabs.css +944 -0
  180. package/dist/css/tag-group.css +247 -0
  181. package/dist/css/tag.css +310 -0
  182. package/dist/css/tags-input.css +322 -0
  183. package/dist/css/text-field.css +197 -0
  184. package/dist/css/time-field.css +277 -0
  185. package/dist/css/time-picker.css +707 -0
  186. package/dist/css/time-range-picker.css +746 -0
  187. package/dist/css/timeline.css +423 -0
  188. package/dist/css/timer.css +123 -0
  189. package/dist/css/timestamp.css +34 -0
  190. package/dist/css/toggle-group.css +424 -0
  191. package/dist/css/toggle.css +146 -0
  192. package/dist/css/tone.css +189 -0
  193. package/dist/css/tool-call.css +429 -0
  194. package/dist/css/toolbar.css +365 -0
  195. package/dist/css/tooltip.css +218 -0
  196. package/dist/css/tour.css +439 -0
  197. package/dist/css/transfer.css +638 -0
  198. package/dist/css/tree-select.css +878 -0
  199. package/dist/css/tree.css +733 -0
  200. package/dist/css/truncate.css +108 -0
  201. package/dist/css/typography.css +472 -0
  202. package/dist/css/undefined.css +58 -0
  203. package/dist/css/virtualizer.css +166 -0
  204. package/dist/css/visually-hidden.css +20 -0
  205. package/dist/css/watermark.css +82 -0
  206. package/dist/family/action-control.css +487 -0
  207. package/dist/family/chart.css +480 -0
  208. package/dist/family/check-mark.css +94 -0
  209. package/dist/family/choice-card.css +97 -0
  210. package/dist/family/collection-item.css +764 -0
  211. package/dist/family/field-chrome.css +287 -0
  212. package/dist/family/loading-ring.css +95 -0
  213. package/dist/family/material.css +72 -0
  214. package/dist/family/menu-choice.css +56 -0
  215. package/dist/family/motion.css +363 -0
  216. package/dist/family/swatch.css +59 -0
  217. package/dist/family/tag-list.css +59 -0
  218. package/family/action-control.css +3 -12
  219. package/index.css +7307 -6759
  220. package/index.unlayered.css +7309 -6761
  221. package/package.json +165 -164
@@ -0,0 +1,189 @@
1
+ /*
2
+ * Copyright (c) 2021-Present XiHanFun and contributors.
3
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
4
+ */
5
+
6
+ /*
7
+ * 语气层:与组件无关,全库共用一份。
8
+ *
9
+ * 三个视觉轴各管一件事,互不干涉:
10
+ * data-variant 形态 solid / subtle / outline / ghost —— 颜色怎么用
11
+ * data-tone 语气 brand / neutral / success / warning / danger / info —— 用哪族颜色
12
+ * data-size 尺寸 sm / md / lg
13
+ *
14
+ * 这里只声明私有槽,各组件的形态规则去消费。所以加一个语气是改这一处 6 条规则,
15
+ * 而不是逐个组件写 6 遍。
16
+ *
17
+ * --xh-_tone-on 是实心底上的前景色,--xh-_tone-shift 是交互态要往哪个方向挪
18
+ * (永远背离前景色走,悬停与按下才不会反过来压低对比度)。两者都由 --xh-_tone 现推:
19
+ * 按 WCAG 的权重算出语气色的相对亮度,低于 0.179 配白字、高于配深字,shift 取反面。
20
+ * 0.179 是白字与黑字对比度相等的那一点,解析解。
21
+ *
22
+ * --xh-_tone-on 还有第二个消费者:实心面上的聚焦环取的是这块面配对的前景色(见 focus.css),
23
+ * 部件没写 color 的那几档直接把这一支写进环色槽。所以它同时管着字与环两件事。
24
+ * 环本身不接语气:语气只决定面是哪一族色,不决定环是什么色。
25
+ *
26
+ * 现推是因为语气色可换肤:写死的分派只对本仓这套 600 档成立,使用者换一套调色板
27
+ * 配对就不再成立,而门禁只验本仓的色,换肤后的失配一路绿灯。
28
+ *
29
+ * 够不到相对颜色语法的引擎退回逐族写死的那一份,取值是本仓这套色的实测结果:
30
+ * 600 档上白字对 brand 5.08、neutral 7.80、danger 4.83 达标,而 success 3.04、
31
+ * warning 2.70、info 3.47 都不到 4.5,这三族配深字。兜底那一份用的是固定原语而不是
32
+ * --xh-fg-default:实心底本身是原语色、不随主题翻,配色若跟着主题翻,暗色下就会变成
33
+ * 白字压在浅黄底上(实测对比度 2.70)。
34
+ *
35
+ * brand 是例外:那一族的色相归使用者,所以它的底与前景走 --xh-bg-brand / --xh-fg-on-brand
36
+ * 这两个语义令牌。缺省主题下它们就解析成 brand-600 与 neutral-0,与其余五族同值;
37
+ * 改写过品牌色的使用者只改这两个令牌,实心底与它上面的字就一起跟着走。
38
+ * 底与前景既然随主题翻,brand 的 shift 也必须随主题翻——现推那一档自动跟着底色走,
39
+ * 文件末尾那条深色覆盖是给兜底那一档用的。
40
+ */
41
+ @layer xihan.components {
42
+ [data-tone] {
43
+ /* 先给一份中性缺省:写了 data-tone 却拼错取值时退成中性,
44
+ 而不是让 --xh-_tone 悬空——悬空会让下面每条 color-mix 变成计算期无效,
45
+ 用它的声明会整条失效,且 var() 兜底也救不回来(槽位是「已声明但无效」)。 */
46
+ --xh-_tone: var(--xh-color-neutral-600);
47
+ --xh-_tone-on: var(--xh-color-neutral-0);
48
+ --xh-_tone-shift: var(--xh-color-neutral-950);
49
+
50
+ /* 交互态与淡底都从主色兑出来,兑的对象是主题色,深浅两态自动跟随 */
51
+ --xh-_tone-hover: color-mix(in oklab, var(--xh-_tone) 88%, var(--xh-_tone-shift));
52
+ --xh-_tone-active: color-mix(in oklab, var(--xh-_tone) 78%, var(--xh-_tone-shift));
53
+ --xh-_tone-subtle: color-mix(in oklab, var(--xh-_tone) 12%, var(--xh-bg-surface));
54
+ --xh-_tone-subtle-hover: color-mix(in oklab, var(--xh-_tone) 20%, var(--xh-bg-surface));
55
+ --xh-_tone-subtle-active: color-mix(in oklab, var(--xh-_tone) 28%, var(--xh-bg-surface));
56
+ --xh-_tone-border: color-mix(in oklab, var(--xh-_tone) 40%, var(--xh-bg-surface));
57
+ /* 控件的可操作区边界要 3:1,兑了底色的 --xh-_tone-border 六族全在 1.44–2.18,够不着。
58
+ 这一支直接取主色本体;两族在各自的底上仍不够,在下面按主题单独兜 */
59
+ --xh-_tone-border-control: var(--xh-_tone);
60
+ /* 装饰性强调(色条、指示条、锚点高亮这类)是非文字图形,同样要 3:1,
61
+ 要求与可操作区边界一模一样,就直接跟着那一支走,连它按主题兜的两族一并继承。
62
+ 要读得清的状态文字不在此列,那些走 --xh-_tone-fg */
63
+ --xh-_tone-soft: var(--xh-_tone-border-control);
64
+ /* 在普通背景上表达该语气的文字色:往前景色兑,浅色态变深、深色态变浅。
65
+ 兑到 60% 是实测出来的:这一档要同时压得住淡底的静态/悬停/按下三态、面与抬起的面
66
+ 共五种底,72% 时六族里有三族够不到 4.5(最低 3.66),60% 全部达标且留有余量 */
67
+ --xh-_tone-fg: color-mix(in oklab, var(--xh-_tone) 60%, var(--xh-fg-default));
68
+ }
69
+
70
+ [data-tone='brand'] {
71
+ --xh-_tone: var(--xh-bg-brand);
72
+ --xh-_tone-on: var(--xh-fg-on-brand);
73
+ --xh-_tone-shift: var(--xh-color-neutral-950);
74
+ }
75
+
76
+ [data-tone='neutral'] {
77
+ --xh-_tone: var(--xh-color-neutral-600);
78
+ --xh-_tone-on: var(--xh-color-neutral-0);
79
+ --xh-_tone-shift: var(--xh-color-neutral-950);
80
+ }
81
+
82
+ [data-tone='danger'] {
83
+ --xh-_tone: var(--xh-color-danger-600);
84
+ --xh-_tone-on: var(--xh-color-neutral-0);
85
+ --xh-_tone-shift: var(--xh-color-neutral-950);
86
+ }
87
+
88
+ [data-tone='success'] {
89
+ --xh-_tone: var(--xh-color-success-600);
90
+ --xh-_tone-on: var(--xh-color-neutral-950);
91
+ --xh-_tone-shift: var(--xh-color-neutral-0);
92
+ }
93
+
94
+ [data-tone='warning'] {
95
+ --xh-_tone: var(--xh-color-warning-600);
96
+ --xh-_tone-on: var(--xh-color-neutral-950);
97
+ --xh-_tone-shift: var(--xh-color-neutral-0);
98
+ }
99
+
100
+ [data-tone='info'] {
101
+ --xh-_tone: var(--xh-color-info-600);
102
+ --xh-_tone-on: var(--xh-color-neutral-950);
103
+ --xh-_tone-shift: var(--xh-color-neutral-0);
104
+ }
105
+
106
+ /* 主色本体在各自的底上有两族够不到 3:1:黄在白底上只有 2.70,中性在深色底上只有 2.54。
107
+ 语气色是固定原语、不随主题翻,所以这两族只能按主题各兜一次。
108
+ 浅色是缺省态(不写 data-theme 也算浅色),深色靠属性选择器压过它。
109
+ data-theme 写在语气容器自己身上还是写在祖先上都算,两种用法都成立 */
110
+ [data-tone='warning'] {
111
+ --xh-_tone-border-control: var(--xh-color-warning-700);
112
+ }
113
+
114
+ :is([data-theme='dark'] *, [data-theme='dark'])[data-tone='warning'] {
115
+ --xh-_tone-border-control: var(--xh-color-warning-600);
116
+ }
117
+
118
+ [data-tone='neutral'] {
119
+ --xh-_tone-border-control: var(--xh-color-neutral-600);
120
+ }
121
+
122
+ :is([data-theme='dark'] *, [data-theme='dark'])[data-tone='neutral'] {
123
+ --xh-_tone-border-control: var(--xh-color-neutral-550);
124
+ }
125
+
126
+ /* 把语气色转成线性 sRGB 分量、按 WCAG 的权重算出相对亮度:低于 0.179 前景取白、
127
+ 高于取黑,挪动方向取前景的反面。三个通道写同一个式子,算出来的是纯白或纯黑;
128
+ alpha 显式写 1,不然会连语气色的透明度一起继承过来。
129
+ 探针按这里实际用到的整个形态测,只测得起 color(from …) 的引擎不会落进半截支持。
130
+ 上面各族写死的那一份是兜底:引擎不认这条语法时整块丢弃,退回那一份 */
131
+ @supports (color: color(from red srgb-linear clamp(0, (0.179 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * infinity, 1) 0 0 / 1)) {
132
+ [data-tone] {
133
+ --xh-_tone-on: color(from var(--xh-_tone) srgb-linear
134
+ clamp(0, (0.179 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * infinity, 1)
135
+ clamp(0, (0.179 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * infinity, 1)
136
+ clamp(0, (0.179 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * infinity, 1) / 1);
137
+ --xh-_tone-shift: color(from var(--xh-_tone) srgb-linear
138
+ clamp(0, ((0.2126 * r + 0.7152 * g + 0.0722 * b) - 0.179) * infinity, 1)
139
+ clamp(0, ((0.2126 * r + 0.7152 * g + 0.0722 * b) - 0.179) * infinity, 1)
140
+ clamp(0, ((0.2126 * r + 0.7152 * g + 0.0722 * b) - 0.179) * infinity, 1) / 1);
141
+ }
142
+ }
143
+
144
+ /* brand 的底与前景走语义令牌、随主题翻,所以它的 shift 也必须跟着翻,
145
+ 否则深色下前景是深字而 shift 仍往深处挪,悬停与按下反而压低对比度
146
+ (实测 5.29 → 4.25 → 3.49,而不写 tone 的品牌链是 5.29 → 7.33 → 10.85)。
147
+ 其余五族的底是固定原语、不随主题翻,shift 也就不必翻 */
148
+ :is([data-theme='dark'] *, [data-theme='dark'])[data-tone='brand'] {
149
+ --xh-_tone-shift: var(--xh-color-neutral-0);
150
+ }
151
+
152
+ /* 语气轴对外的那一族:上面每支私有槽一对一指过来,取值恒等。
153
+ 使用者在自己的节点上写 data-tone,就能在那个节点里取到整族颜色,
154
+ 六族与深浅两态都跟着上面的推导走。
155
+ --xh-_tone-shift 是兑色的方向、不是一档颜色,不出公开令牌。
156
+ 声明在 [data-tone] 上而不是 :root 上:值里带 var(),自定义属性的 var()
157
+ 在「声明它的那个元素」上就替换掉了,落在 :root 上会把语气冻死在根元素。 */
158
+ [data-tone] {
159
+ --xh-tone-solid: var(--xh-_tone);
160
+ --xh-tone-solid-hover: var(--xh-_tone-hover);
161
+ --xh-tone-solid-active: var(--xh-_tone-active);
162
+ --xh-tone-on: var(--xh-_tone-on);
163
+ --xh-tone-subtle: var(--xh-_tone-subtle);
164
+ --xh-tone-subtle-hover: var(--xh-_tone-subtle-hover);
165
+ --xh-tone-subtle-active: var(--xh-_tone-subtle-active);
166
+ --xh-tone-fg: var(--xh-_tone-fg);
167
+ --xh-tone-border: var(--xh-_tone-border);
168
+ --xh-tone-border-control: var(--xh-_tone-border-control);
169
+ --xh-tone-soft: var(--xh-_tone-soft);
170
+ }
171
+
172
+ /* 实心面顶边的内高光:一条极浅的内阴影,把一块实心底衬出厚度。
173
+ 全库十六处实心档共用这两条,别再逐个组件把式子抄一遍。
174
+
175
+ 两条的差别只在取哪个前景色:--xh-_highlight-tone 跟着语气走(实心底上的前景色,
176
+ 没写语气时退回品牌那一档),--xh-_highlight-brand 恒取品牌色——底色本身不跟语气走的
177
+ 那几处(表单提交、引导主按钮、不带语气的实心标签)用它。
178
+
179
+ 声明在 :where([data-scope]) 上而不是 :root 上:它俩的值里带 var(),而自定义属性的
180
+ var() 在「声明它的那个元素」上就替换掉了。落在 :root 上,--xh-_tone-on 与
181
+ --xh-fg-on-brand 就都按根元素解析,语气与嵌套主题一并冻死;逐元素声明才是现取。
182
+ 同理它也不能收成 tokens 那边的一支令牌。 */
183
+ :where([data-scope]) {
184
+ --xh-_highlight-tone: inset 0 var(--xh-stroke-thin) 0 0
185
+ color-mix(in oklab, var(--xh-_tone-on, var(--xh-fg-on-brand)) 14%, transparent);
186
+ --xh-_highlight-brand: inset 0 var(--xh-stroke-thin) 0 0
187
+ color-mix(in oklab, var(--xh-fg-on-brand) 14%, transparent);
188
+ }
189
+ }
@@ -0,0 +1,429 @@
1
+ /*
2
+ * Copyright (c) 2021-Present XiHanFun and contributors.
3
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
4
+ */
5
+
6
+ /* 定义 tool call 样式。 */
7
+
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立;
9
+ 独立皮肤同时传递引入开关的动作控件配方,full bundle 递归去重。 */
10
+ @import '../family/motion.css';
11
+ @import '../family/action-control.css';
12
+
13
+ @layer xihan.components {
14
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
15
+ .xh-scope-tool-call {
16
+ --xh-tool-call-skin: 1;
17
+ }
18
+
19
+ /* 一张静态内容面:--xh-border-default 描边 + surface 底 + 无影(边界只由描边承担);
20
+ 审批位与详情区的内部分隔线走另一支 --xh-tool-call-divider(subtle)。
21
+ 卡片建出来时淡入并向上收一段,一屏若干张的错峰交给外层容器排 */
22
+ .xh-scope-tool-call[data-part='root'] {
23
+ /* 尺寸缺省档:不写 data-size 时的取值。三档都只写这三个私有槽,
24
+ 公开槽留在读处当首选,作者在祖先上设的覆盖才赢得过档位声明 */
25
+ --xh-_tool-call-px: var(--xh-space-4);
26
+ --xh-_tool-call-py: var(--xh-space-4);
27
+ --xh-_tool-call-font-size: var(--xh-control-font-md);
28
+
29
+ /* 影另落一份进私有槽给语气档:它要把影与左侧色条叠成一条 box-shadow,缺省无影写成零影占位 */
30
+ --xh-_tool-call-shadow: var(--xh-tool-call-shadow, 0 0 0 transparent);
31
+
32
+ /* 作者塞进指示符里的图标与皮肤画的兜底字形同一把尺:md 档,开关上另按家族档取值 */
33
+ --xh-icon-size: var(--xh-tool-call-icon-size, var(--xh-glyph-size-md));
34
+
35
+ display: block;
36
+ overflow: hidden;
37
+ border: var(--xh-stroke-thin) solid var(--xh-tool-call-border, var(--xh-border-default));
38
+ border-radius: var(--xh-tool-call-radius, var(--xh-shape-surface));
39
+ background: var(--xh-tool-call-bg, var(--xh-bg-surface));
40
+ box-shadow: var(--xh-tool-call-shadow, none);
41
+ }
42
+
43
+ /* 卡片出现时滑入;落在首帧就在的列表条目里(历史消息)时直接呈现,不跟着整屏一起入场 */
44
+ .xh-scope-tool-call[data-part='root']:not([data-scope][data-part='item'][data-instant] :where(.xh-scope-tool-call)) {
45
+ animation: xh-item-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong) both;
46
+ }
47
+
48
+ /* 上面声明的 display 会盖掉 UA 的 [hidden]{display:none},这里补回 */
49
+ .xh-scope-tool-call[data-part='root'][hidden] {
50
+ display: none;
51
+ }
52
+
53
+ /* —— 形态 —— */
54
+ /* 缺省档即 outline,写在上面那条基础规则里 */
55
+ /* 底色分区:描边让位给底色,嵌在别人的面里时不再多一圈线;
56
+ 淡底承载面向开关下发 hover 200 → pressed 300 */
57
+ .xh-scope-tool-call[data-part='root'][data-variant='subtle'] {
58
+ --xh-action-host-bg-hover: var(--xh-bg-subtle-hover);
59
+ --xh-action-host-bg-pressed: var(--xh-bg-subtle-active);
60
+
61
+ border-color: transparent;
62
+ background: var(--xh-tool-call-bg, var(--xh-bg-subtle));
63
+ }
64
+
65
+ /* 无壳内联:卡中卡时不再自带一张面,跟着外层的底走 */
66
+ .xh-scope-tool-call[data-part='root'][data-variant='ghost'] {
67
+ --xh-_tool-call-shadow: 0 0 0 transparent;
68
+
69
+ overflow: visible;
70
+ border-color: transparent;
71
+ background: none;
72
+ box-shadow: none;
73
+ }
74
+
75
+ /* 无壳档的开关只占文字宽度(盖掉 profile 的铺满一行),自己带圆角,悬停时才浮出一块底 */
76
+ .xh-scope-tool-call[data-part='root'][data-variant='ghost'] .xh-scope-tool-call[data-part='trigger'] {
77
+ --xh-action-radius: var(--xh-tool-call-trigger-radius, var(--xh-shape-control));
78
+
79
+ inline-size: auto;
80
+ }
81
+
82
+ /* 出错态换描边色,与语气轴各管各的:语气是作者定的强调,出错是组件自己的阶段。
83
+ 根上的 data-state 已经被开合占着,阶段落成 data-errored 这一位;八个承载阶段的部件
84
+ 渲出任何一个时那条 :has() 同样命中,两条并列,作者只渲根也照样变色 */
85
+ .xh-scope-tool-call[data-part='root'][data-errored],
86
+ .xh-scope-tool-call[data-part='root']:has(.xh-scope-tool-call[data-state='output-error']) {
87
+ border-color: var(--xh-tool-call-border-error, var(--xh-border-invalid));
88
+ }
89
+
90
+ /* 开关铺满一行,盒型、悬停 / 按下 / 禁用面由 Action Control 的 disclosure-trigger 档给(ghost 形态:
91
+ 白底承载 hover 100 → pressed 200,按下只换面不缩放);这里只把使用者槽映射到桥接槽:
92
+ 圆角归零贴住卡边,内衬与字号沿用卡片的档位(不取 profile 的 md 档内衬,几何不变),
93
+ 字形按开关自己的档位取 --xh-_action-profile-glyph-size */
94
+ .xh-scope-tool-call[data-part='trigger'] {
95
+ --xh-action-radius: 0;
96
+ --xh-action-gap: var(--xh-tool-call-trigger-gap, var(--xh-space-2));
97
+ --xh-action-padding-inline: var(--xh-tool-call-px, var(--xh-_tool-call-px));
98
+ --xh-action-padding-block: var(--xh-tool-call-py, var(--xh-_tool-call-py));
99
+ --xh-action-font-size: var(--xh-tool-call-font-size, var(--xh-_tool-call-font-size));
100
+ --xh-action-fg-rest: var(--xh-tool-call-trigger-fg, var(--xh-fg-default));
101
+ --xh-action-bg-hover: var(--xh-tool-call-trigger-bg-hover, var(--xh-_action-variant-bg-hover));
102
+ --xh-icon-size: var(--xh-tool-call-icon-size, var(--xh-_action-profile-glyph-size));
103
+
104
+ border-width: 0;
105
+ }
106
+
107
+ .xh-scope-tool-call[data-part='trigger'][hidden] {
108
+ display: none;
109
+ }
110
+
111
+ /* 指示随开合转向,纯装饰 */
112
+ .xh-scope-tool-call[data-part='indicator'] {
113
+ display: inline-flex;
114
+ flex: none;
115
+ align-items: center;
116
+ justify-content: center;
117
+ color: var(--xh-tool-call-indicator-fg, var(--xh-fg-subtle));
118
+ }
119
+
120
+ /* 与详情开合同时长:箭头先转完、内容后到位会像是两件事。收起取 collapse 档,展开(下面的打开态)取 expand 档。
121
+ 首帧就在的朝向直接呈现(data-instant),第一次开合起才转 */
122
+ .xh-scope-tool-call[data-part='indicator']:not([data-instant]) {
123
+ transition: rotate var(--xh-motion-duration-collapse) var(--xh-motion-ease-enter-strong);
124
+ }
125
+
126
+ /* 没写内容就由皮肤画;作者往部件里塞了自己的图形,这条规则即不命中 */
127
+ .xh-scope-tool-call[data-part='indicator']:empty::before {
128
+ content: '';
129
+ display: inline-block;
130
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
131
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
132
+ vertical-align: var(--xh-glyph-baseline-shift);
133
+ background-color: currentColor;
134
+ -webkit-mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
135
+ mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
136
+ }
137
+
138
+ .xh-scope-tool-call[data-part='indicator'][hidden] {
139
+ display: none;
140
+ }
141
+
142
+ /* 收起时朝向行首、展开时转向下方,与逻辑书写方向一致:
143
+ rtl 下 inline-start 在右侧,箭头因此要反着转 */
144
+ .xh-scope-tool-call[data-part='indicator'][data-state='open'] {
145
+ rotate: 90deg;
146
+ }
147
+
148
+ .xh-scope-tool-call[data-part='indicator'][data-state='open']:not([data-instant]) {
149
+ transition: rotate var(--xh-motion-duration-expand) var(--xh-motion-ease-enter-strong);
150
+ }
151
+
152
+ [dir='rtl'] .xh-scope-tool-call[data-part='indicator'] {
153
+ rotate: 180deg;
154
+ }
155
+
156
+ [dir='rtl'] .xh-scope-tool-call[data-part='indicator'][data-state='open'] {
157
+ rotate: 90deg;
158
+ }
159
+
160
+ /* 工具名占满剩余宽度并单行截断:长名字不该把状态挤出去 */
161
+ .xh-scope-tool-call[data-part='label'] {
162
+ display: block;
163
+ overflow: hidden;
164
+ flex: 1 1 auto;
165
+ font-family: var(--xh-tool-call-label-font, var(--xh-font-family-mono));
166
+ /* 面级披露的触发条文字取标签角色,与 Accordion、Collapsible、Reasoning 同一档字重 */
167
+ font-weight: var(--xh-tool-call-label-font-weight, var(--xh-text-label-weight));
168
+ text-overflow: ellipsis;
169
+ white-space: nowrap;
170
+ }
171
+
172
+ .xh-scope-tool-call[data-part='label'][hidden] {
173
+ display: none;
174
+ }
175
+
176
+ /* 有摘要时把剩余宽度让给它:工具名只要放得下自己,摘要才是这一行里最想读到的东西 */
177
+ .xh-scope-tool-call[data-part='trigger']:has([data-part='summary']) [data-part='label']:where(.xh-scope-tool-call) {
178
+ flex: 0 1 auto;
179
+ }
180
+
181
+ /* 一行参数摘要:单行截断的浅底条,收起时也看得见这次查的是什么 */
182
+ .xh-scope-tool-call[data-part='summary'] {
183
+ display: block;
184
+ overflow: hidden;
185
+ flex: 1 1 auto;
186
+ min-inline-size: 0;
187
+ padding-inline: var(--xh-tool-call-summary-px, var(--xh-space-1_5));
188
+ border-radius: var(--xh-tool-call-summary-radius, var(--xh-shape-control));
189
+ background: var(--xh-tool-call-summary-bg, var(--xh-bg-subtle));
190
+ color: var(--xh-tool-call-summary-fg, var(--xh-fg-muted));
191
+ font-family: var(--xh-tool-call-label-font, var(--xh-font-family-mono));
192
+ font-size: var(--xh-tool-call-summary-font-size, var(--xh-text-caption-size));
193
+ text-overflow: ellipsis;
194
+ white-space: nowrap;
195
+ }
196
+
197
+ .xh-scope-tool-call[data-part='summary'][hidden] {
198
+ display: none;
199
+ }
200
+
201
+ .xh-scope-tool-call[data-part='status'] {
202
+ display: inline-flex;
203
+ flex: none;
204
+ align-items: center;
205
+ color: var(--xh-tool-call-status-fg, var(--xh-fg-muted));
206
+ font-size: var(--xh-tool-call-status-font-size, var(--xh-text-caption-size));
207
+ line-height: var(--xh-leading-none);
208
+ }
209
+
210
+ .xh-scope-tool-call[data-part='status'][hidden] {
211
+ display: none;
212
+ }
213
+
214
+ /* 三档落定的阶段收成状态 chip:几何与 Tag 的 sm 档同一把尺,淡底 + 同族可读字取连接层投影的语气。
215
+ 在跑的两档不带底,让扫光自己说话 */
216
+ .xh-scope-tool-call[data-part='status'][data-tone] {
217
+ padding-block: var(--xh-tool-call-status-py, var(--xh-space-0_5));
218
+ padding-inline: var(--xh-tool-call-status-px, var(--xh-space-1_5));
219
+ border: var(--xh-stroke-thin) solid transparent;
220
+ border-radius: var(--xh-tool-call-status-radius, var(--xh-shape-pill));
221
+ font-weight: var(--xh-font-weight-medium);
222
+ line-height: var(--xh-control-indicator-md);
223
+ }
224
+
225
+ /* 各阶段的覆盖槽仍按阶段给:作者可以单独改某一档 */
226
+ .xh-scope-tool-call[data-part='status'][data-state='awaiting-approval'] {
227
+ background: var(--xh-tool-call-status-bg-approval, var(--xh-_tone-subtle));
228
+ color: var(--xh-tool-call-status-fg-approval, var(--xh-_tone-fg));
229
+ }
230
+
231
+ .xh-scope-tool-call[data-part='status'][data-state='output-available'] {
232
+ background: var(--xh-tool-call-status-bg-done, var(--xh-_tone-subtle));
233
+ color: var(--xh-tool-call-status-fg-done, var(--xh-_tone-fg));
234
+ }
235
+
236
+ .xh-scope-tool-call[data-part='status'][data-state='output-error'] {
237
+ background: var(--xh-tool-call-status-bg-error, var(--xh-_tone-subtle));
238
+ color: var(--xh-tool-call-status-fg-error, var(--xh-_tone-fg));
239
+ }
240
+
241
+ /* 在跑的时候状态文字自己扫一道光:data-loading 此前在皮肤里没有任何表达,
242
+ 参数在传与参数齐了两档在屏幕上一模一样。
243
+ 渐变画在背景上再裁进字形,前景与 WebKit 的字形填充色都要透明才透得出底下那道光 */
244
+ .xh-scope-tool-call[data-part='root'][data-loading] [data-part='status']:where(.xh-scope-tool-call) {
245
+ background-image: linear-gradient(
246
+ 90deg,
247
+ var(--xh-tool-call-status-shimmer-base, var(--xh-fg-subtle)) 35%,
248
+ var(--xh-tool-call-status-shimmer-sheen, var(--xh-fg-default)) 50%,
249
+ var(--xh-tool-call-status-shimmer-base, var(--xh-fg-subtle)) 65%
250
+ );
251
+ background-size: 200% 100%;
252
+ -webkit-background-clip: text;
253
+ background-clip: text;
254
+ color: transparent;
255
+ -webkit-text-fill-color: transparent;
256
+ animation: xh-shimmer var(--xh-tool-call-shimmer-duration, var(--xh-motion-loop-shimmer)) var(--xh-motion-ease-loop) infinite;
257
+ }
258
+
259
+ /* 减少动效:整道光撤掉换回平色,"在跑"仍由状态文字本身表达——只停动画的话,
260
+ 字形里会留着一段冻住的渐变。两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
261
+ @media (prefers-reduced-motion: reduce) {
262
+ .xh-scope-tool-call[data-part='root'][data-loading] [data-part='status']:where(.xh-scope-tool-call) {
263
+ background-image: none;
264
+ color: var(--xh-tool-call-status-fg, var(--xh-fg-muted));
265
+ -webkit-text-fill-color: currentColor;
266
+ animation: none;
267
+ }
268
+ }
269
+
270
+ :where([data-motion='reduce']) .xh-scope-tool-call[data-part='root'][data-loading] [data-part='status']:where(.xh-scope-tool-call) {
271
+ background-image: none;
272
+ color: var(--xh-tool-call-status-fg, var(--xh-fg-muted));
273
+ -webkit-text-fill-color: currentColor;
274
+ animation: none;
275
+ }
276
+
277
+ /* 耗时:等宽数位,秒数跳动时不左右挪 */
278
+ .xh-scope-tool-call[data-part='duration'] {
279
+ display: block;
280
+ flex: none;
281
+ color: var(--xh-tool-call-duration-fg, var(--xh-fg-subtle));
282
+ font-size: var(--xh-tool-call-status-font-size, var(--xh-text-caption-size));
283
+ font-variant-numeric: tabular-nums;
284
+ }
285
+
286
+ .xh-scope-tool-call[data-part='duration'][hidden] {
287
+ display: none;
288
+ }
289
+
290
+ /* 审批闸门常驻在开关与详情之间,不随折叠一起藏起来 */
291
+ .xh-scope-tool-call[data-part='approval'] {
292
+ display: block;
293
+ padding-inline: var(--xh-tool-call-px, var(--xh-_tool-call-px));
294
+ padding-block: var(--xh-tool-call-py, var(--xh-_tool-call-py));
295
+ border-block-start: var(--xh-stroke-thin) solid var(--xh-tool-call-divider, var(--xh-border-subtle));
296
+ }
297
+
298
+ .xh-scope-tool-call[data-part='approval'][hidden] {
299
+ display: none;
300
+ }
301
+
302
+ /* 这里刻意没有 [data-part='content'][hidden] { display: none }:补了退场一帧都播不出来。
303
+ 收起由适配器在退场结束后写内联 display 落成,退场窗口内靠 connect 发的 inert 挡住焦点。
304
+ 详情区按阶段取舍,参数、结果与错误同一时刻只排一格,收起时整格随行高归零 */
305
+ .xh-scope-tool-call[data-part='content'] {
306
+ /* 行高与内缩同帧动的共享关键帧读这两条槽:两端内缩同值 */
307
+ --xh-_disclosure-pt: var(--xh-tool-call-py, var(--xh-_tool-call-py));
308
+ --xh-_disclosure-pb: var(--xh-tool-call-py, var(--xh-_tool-call-py));
309
+
310
+ display: grid;
311
+ grid-template-rows: 1fr;
312
+ /* 块轴照旧裁掉:收起靠行高归零,多出来的那一截不能露在外面。
313
+ 行内一轴改成可横滚,宽过卡片的参数与结果推得回来看,不再被裁掉且取不回 */
314
+ overflow-x: auto;
315
+ overflow-y: clip;
316
+ gap: var(--xh-tool-call-content-gap, var(--xh-space-2));
317
+ padding-inline: var(--xh-tool-call-px, var(--xh-_tool-call-px));
318
+ padding-block: var(--xh-tool-call-py, var(--xh-_tool-call-py));
319
+ border-block-start: var(--xh-stroke-thin) solid var(--xh-tool-call-divider, var(--xh-border-subtle));
320
+ }
321
+
322
+ /* 展开与收起只在开合时播:首帧就在的展开 / 收起直接呈现(data-instant) */
323
+ .xh-scope-tool-call[data-part='content'][data-state='open']:not([data-instant]) {
324
+ animation: xh-disclosure-expand var(--xh-motion-duration-expand) var(--xh-motion-ease-enter-strong);
325
+ }
326
+
327
+ /* 退场走 exit 档曲线 */
328
+ .xh-scope-tool-call[data-part='content'][data-state='closed']:not([data-instant]) {
329
+ animation: xh-disclosure-collapse var(--xh-motion-duration-collapse) var(--xh-motion-ease-exit) forwards;
330
+ }
331
+
332
+ .xh-scope-tool-call[data-part='input'],
333
+ .xh-scope-tool-call[data-part='output'] {
334
+ display: block;
335
+ min-inline-size: 0;
336
+ }
337
+
338
+ .xh-scope-tool-call[data-part='input'][hidden],
339
+ .xh-scope-tool-call[data-part='output'][hidden] {
340
+ display: none;
341
+ }
342
+
343
+ /* 出错时开关的 aria-describedby 指过来;流被中止时这里可能是空的 */
344
+ .xh-scope-tool-call[data-part='error'] {
345
+ display: block;
346
+ color: var(--xh-tool-call-error-fg, var(--xh-fg-danger));
347
+ font-size: var(--xh-tool-call-status-font-size, var(--xh-text-caption-size));
348
+ }
349
+
350
+ .xh-scope-tool-call[data-part='error'][hidden] {
351
+ display: none;
352
+ }
353
+
354
+ /* 尺寸档只改内衬与字号 */
355
+ .xh-scope-tool-call[data-part='root'][data-size='sm'] {
356
+ --xh-_tool-call-px: var(--xh-space-3);
357
+ --xh-_tool-call-py: var(--xh-space-3);
358
+ --xh-_tool-call-font-size: var(--xh-control-font-sm);
359
+ }
360
+
361
+ .xh-scope-tool-call[data-part='root'][data-size='md'] {
362
+ --xh-_tool-call-px: var(--xh-space-4);
363
+ --xh-_tool-call-py: var(--xh-space-4);
364
+ --xh-_tool-call-font-size: var(--xh-control-font-md);
365
+ }
366
+
367
+ .xh-scope-tool-call[data-part='root'][data-size='lg'] {
368
+ --xh-_tool-call-px: var(--xh-space-5);
369
+ --xh-_tool-call-py: var(--xh-space-5);
370
+ --xh-_tool-call-font-size: var(--xh-control-font-lg);
371
+ }
372
+
373
+ /* 语气只染左侧那一条色条,不夺走阶段色;海拔跟着一起写回来,
374
+ box-shadow 是整条替换的,只写色条会把上面那层投影抹掉 */
375
+ .xh-scope-tool-call[data-part='root'][data-tone] {
376
+ box-shadow:
377
+ inset var(--xh-tool-call-tone-bar, var(--xh-stroke-thick)) 0 0 0 var(--xh-tool-call-tone-fg, var(--xh-_tone-soft, var(--xh-bg-brand))),
378
+ var(--xh-_tool-call-shadow);
379
+ }
380
+
381
+ @media (forced-colors: active) {
382
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
383
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
384
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
385
+ 禁用取 GrayText */
386
+ .xh-scope-tool-call[data-part='indicator']:empty::before {
387
+ forced-color-adjust: none;
388
+ background-color: ButtonText;
389
+ }
390
+
391
+ .xh-scope-tool-call[data-part='trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]) .xh-scope-tool-call[data-part='indicator']:empty::before {
392
+ background-color: HighlightText;
393
+ }
394
+
395
+ .xh-scope-tool-call[data-part='trigger'][data-disabled] .xh-scope-tool-call[data-part='indicator']:empty::before {
396
+ background-color: GrayText;
397
+ }
398
+ }
399
+ }
400
+
401
+ @layer xihan.motion {
402
+ @media print {
403
+ /* 打印:动画会被定格在任意一帧——转了一半的圈、扫到一半的微光。这里换成静止的表达,
404
+ 取值与减弱动效那一档相同。两档各写一块、不并进一条媒体查询列表:减弱动效另有一份
405
+ :where([data-motion='reduce']) 的钩子版本与它成对,打印没有对应的钩子可配,
406
+ 并成一条之后这层配对关系在文本上就断了。 */
407
+ .xh-scope-tool-call[data-part='root'][data-loading] [data-part='status']:where(.xh-scope-tool-call) {
408
+ background-image: none;
409
+ color: var(--xh-tool-call-status-fg, var(--xh-fg-muted));
410
+ -webkit-text-fill-color: currentColor;
411
+ animation: none;
412
+ }
413
+ }
414
+
415
+ /*
416
+ * 高对比档:扫光那层渐变被丢弃,只剩被裁成透明的字形填充,状态文字整段消失。
417
+ * 把两处填充都还回去,在跑这件事仍由状态文字本身表出。
418
+ */
419
+ @media (forced-colors: active) {
420
+ .xh-scope-tool-call[data-part='root'][data-loading] [data-part='status']:where(.xh-scope-tool-call) {
421
+ background-image: none;
422
+ -webkit-background-clip: border-box;
423
+ background-clip: border-box;
424
+ color: CanvasText;
425
+ -webkit-text-fill-color: currentColor;
426
+ animation: none;
427
+ }
428
+ }
429
+ }