@xihan-ui/styles 1.0.0 → 2.0.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 (171) hide show
  1. package/CHANGELOG.md +5146 -6
  2. package/README.md +22 -2
  3. package/component-tokens.d.ts +4212 -0
  4. package/component-tokens.js +2 -0
  5. package/components.tokens.json +80116 -0
  6. package/css/accordion.css +131 -75
  7. package/css/affix.css +16 -1
  8. package/css/alert.css +114 -67
  9. package/css/anchor.css +99 -27
  10. package/css/approval.css +461 -0
  11. package/css/avatar-group.css +14 -6
  12. package/css/avatar.css +33 -9
  13. package/css/back-top.css +103 -48
  14. package/css/badge.css +42 -14
  15. package/css/bar-code.css +62 -0
  16. package/css/breadcrumb.css +40 -21
  17. package/css/button-group.css +146 -24
  18. package/css/button.css +116 -99
  19. package/css/calendar-picker.css +448 -0
  20. package/css/calendar-range-picker.css +517 -0
  21. package/css/card.css +54 -77
  22. package/css/carousel.css +412 -68
  23. package/css/cascader.css +429 -246
  24. package/css/checkbox-group.css +225 -77
  25. package/css/checkbox.css +174 -37
  26. package/css/clipboard.css +266 -47
  27. package/css/code-view.css +279 -0
  28. package/css/collapsible.css +108 -63
  29. package/css/color-field.css +158 -0
  30. package/css/color-picker.css +172 -184
  31. package/css/color-slider.css +274 -0
  32. package/css/color-swatch-picker.css +243 -0
  33. package/css/color-swatch.css +35 -0
  34. package/css/combobox.css +267 -273
  35. package/css/command.css +399 -0
  36. package/css/context-menu.css +183 -108
  37. package/css/date-field.css +94 -121
  38. package/css/date-picker.css +486 -249
  39. package/css/date-range-picker.css +559 -0
  40. package/css/description.css +51 -0
  41. package/css/descriptions.css +133 -87
  42. package/css/dialog.css +247 -68
  43. package/css/diff-view.css +428 -0
  44. package/css/download-trigger.css +155 -46
  45. package/css/drawer.css +194 -111
  46. package/css/editable.css +172 -152
  47. package/css/empty-state.css +80 -11
  48. package/css/field-array.css +190 -0
  49. package/css/field.css +84 -31
  50. package/css/fieldset.css +51 -18
  51. package/css/file-upload.css +134 -36
  52. package/css/flex.css +20 -1
  53. package/css/float-button.css +238 -57
  54. package/css/floating-panel.css +173 -92
  55. package/css/focus.css +46 -0
  56. package/css/forced-colors.css +93 -0
  57. package/css/form.css +254 -77
  58. package/css/gradient-text.css +34 -10
  59. package/css/grid.css +516 -5
  60. package/css/heatmap.css +87 -29
  61. package/css/highlight.css +20 -5
  62. package/css/hover-card.css +97 -47
  63. package/css/icon-wrapper.css +15 -3
  64. package/css/icon.css +74 -8
  65. package/css/image-cropper.css +143 -29
  66. package/css/image-viewer.css +150 -89
  67. package/css/image.css +30 -1
  68. package/css/infinite-scroll.css +51 -1
  69. package/css/input-group.css +277 -0
  70. package/css/json-viewer.css +102 -45
  71. package/css/kbd.css +72 -0
  72. package/css/label.css +78 -0
  73. package/css/layers.css +5 -0
  74. package/css/layout.css +175 -31
  75. package/css/list.css +24 -3
  76. package/css/listbox.css +208 -67
  77. package/css/loading-bar.css +52 -3
  78. package/css/log.css +144 -9
  79. package/css/markdown-stream.css +149 -0
  80. package/css/marquee.css +27 -1
  81. package/css/masonry.css +7 -0
  82. package/css/matrix-code.css +67 -0
  83. package/css/mention.css +234 -152
  84. package/css/menu.css +180 -83
  85. package/css/menubar.css +204 -136
  86. package/css/message-feed.css +197 -0
  87. package/css/navigation-menu.css +144 -90
  88. package/css/notification.css +178 -100
  89. package/css/number-animation.css +7 -0
  90. package/css/number-field.css +120 -277
  91. package/css/overlay-arrow.css +12 -7
  92. package/css/page-header.css +99 -14
  93. package/css/pagination.css +175 -135
  94. package/css/password-input.css +222 -219
  95. package/css/pin-input.css +101 -67
  96. package/css/pointer.css +36 -0
  97. package/css/popconfirm.css +185 -94
  98. package/css/popover.css +121 -77
  99. package/css/progress.css +60 -12
  100. package/css/prompt-input.css +224 -0
  101. package/css/question-flow.css +513 -0
  102. package/css/radio-group.css +147 -30
  103. package/css/rating.css +193 -43
  104. package/css/reasoning.css +336 -0
  105. package/css/reset.css +84 -6
  106. package/css/resizable.css +237 -0
  107. package/css/scroll-area.css +68 -7
  108. package/css/scrollbar.css +47 -13
  109. package/css/segmented.css +94 -29
  110. package/css/select.css +288 -274
  111. package/css/separator.css +101 -1
  112. package/css/side-nav.css +133 -75
  113. package/css/signature-pad.css +74 -54
  114. package/css/skeleton.css +84 -11
  115. package/css/slider.css +132 -39
  116. package/css/sortable.css +198 -0
  117. package/css/spinner.css +126 -12
  118. package/css/splitter.css +29 -8
  119. package/css/statistic.css +55 -0
  120. package/css/steps.css +206 -72
  121. package/css/switch.css +268 -36
  122. package/css/table.css +695 -118
  123. package/css/tabs.css +604 -42
  124. package/css/tag-group.css +219 -0
  125. package/css/tag.css +118 -28
  126. package/css/tags-input.css +156 -274
  127. package/css/text-field.css +101 -249
  128. package/css/time-field.css +100 -128
  129. package/css/time-picker.css +323 -252
  130. package/css/time-range-picker.css +719 -0
  131. package/css/timeline.css +202 -68
  132. package/css/timer.css +56 -52
  133. package/css/timestamp.css +34 -0
  134. package/css/toast.css +373 -144
  135. package/css/toggle-group.css +309 -76
  136. package/css/toggle.css +116 -85
  137. package/css/tone.css +47 -0
  138. package/css/tool-call.css +421 -0
  139. package/css/toolbar.css +181 -33
  140. package/css/tooltip.css +107 -36
  141. package/css/tour.css +183 -152
  142. package/css/transfer.css +351 -138
  143. package/css/tree-select.css +323 -216
  144. package/css/tree.css +294 -62
  145. package/css/truncate.css +65 -0
  146. package/css/typography.css +228 -5
  147. package/css/undefined.css +10 -1
  148. package/css/virtualizer.css +9 -10
  149. package/css/visually-hidden.css +7 -0
  150. package/css/watermark.css +7 -0
  151. package/family/action-control.css +487 -0
  152. package/family/collection-item.css +721 -0
  153. package/family/field-chrome.css +298 -0
  154. package/family/motion.css +166 -0
  155. package/family/swatch.css +59 -0
  156. package/index.css +41705 -126
  157. package/index.unlayered.css +33692 -17339
  158. package/package.json +51 -17
  159. package/css/calendar.css +0 -367
  160. package/css/code-block.css +0 -91
  161. package/css/composer.css +0 -134
  162. package/css/countdown.css +0 -26
  163. package/css/dynamic-input.css +0 -220
  164. package/css/ellipsis.css +0 -52
  165. package/css/hotkeys.css +0 -90
  166. package/css/popselect.css +0 -391
  167. package/css/qr-code.css +0 -55
  168. package/css/result.css +0 -119
  169. package/css/space.css +0 -117
  170. package/css/thread.css +0 -120
  171. package/css/time.css +0 -25
package/css/clipboard.css CHANGED
@@ -1,3 +1,32 @@
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
+ /* 定义 clipboard 样式。 */
7
+
8
+ @import '../family/action-control.css';
9
+
10
+ @layer xihan.motion {
11
+ @keyframes xh-clipboard-rotate {
12
+ to {
13
+ rotate: 360deg;
14
+ }
15
+ }
16
+
17
+ @keyframes xh-clipboard-loading-reveal {
18
+ to {
19
+ opacity: 1;
20
+ }
21
+ }
22
+
23
+ @keyframes xh-clipboard-loading-hide {
24
+ to {
25
+ opacity: 0;
26
+ }
27
+ }
28
+ }
29
+
1
30
  @layer xihan.components {
2
31
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
32
  [data-scope='clipboard'] {
@@ -6,9 +35,53 @@
6
35
 
7
36
  /* 与文本框同一套堆叠:标签一行、输入行一行 */
8
37
  [data-scope='clipboard'][data-part='root'] {
38
+ /* 输入框与按钮同取一档高度,换尺寸只改这一个私有槽 */
39
+ --xh-_clipboard-h: var(--xh-control-h-md);
40
+ --xh-_clipboard-px: var(--xh-control-px-md);
41
+ --xh-_clipboard-font-size: var(--xh-control-font-md);
42
+
9
43
  display: inline-flex;
10
44
  flex-direction: column;
11
45
  gap: var(--xh-clipboard-gap, var(--xh-space-1));
46
+ isolation: isolate;
47
+
48
+ /* 父容器给不出这么宽时整件跟着收,不越出父容器 */
49
+ max-inline-size: 100%;
50
+ }
51
+
52
+ /* 带输入框的用法是一件字段:不传尺寸时一律这么宽,宽度不随内容走;父容器给不出这么宽时整件跟着收,
53
+ 地板同步让步,地板不高过缺省宽。只放复制钮的用法是一颗独立按钮,宽随文字走,不吃字段缺省宽 */
54
+ [data-scope='clipboard'][data-part='root']:has([data-scope='clipboard'][data-part='control']) {
55
+ inline-size: var(--xh-clipboard-control-w, var(--xh-control-w));
56
+ min-inline-size: min(var(--xh-clipboard-control-w, var(--xh-control-w)), var(--xh-clipboard-control-min-w, var(--xh-control-min-w)), 100%);
57
+ }
58
+
59
+ /* —— 尺寸 —— */
60
+ /* 缺省档写在上面的基础规则里 */
61
+ [data-scope='clipboard'][data-part='root'][data-size='sm'] {
62
+ --xh-_clipboard-h: var(--xh-control-h-sm);
63
+ --xh-_clipboard-px: var(--xh-control-px-sm);
64
+ --xh-_clipboard-font-size: var(--xh-control-font-sm);
65
+ }
66
+
67
+ [data-scope='clipboard'][data-part='root'][data-size='lg'] {
68
+ --xh-_clipboard-h: var(--xh-control-h-lg);
69
+ --xh-_clipboard-px: var(--xh-control-px-lg);
70
+ --xh-_clipboard-font-size: var(--xh-control-font-lg);
71
+ }
72
+
73
+ /* —— 形态 ——
74
+ 四档的底 / 前景 / 描边由家族形态矩阵按复制钮上的 data-xh-action-variant 给出(缺省 subtle 由连接层显式
75
+ 投影,只有 solid 才品牌实心),用哪族颜色由 data-tone 决定;这里只剩实心面上的环色。
76
+ 面是实心的,环色与面同族,画在面里就看不出来;环改取面自己的前景色——
77
+ 那一族色本来就要在这块面上把字读清楚,与面的对比是按可读性定的 */
78
+ [data-scope='clipboard'][data-part='copy-trigger'][data-xh-action-variant='solid']:focus-visible {
79
+ --xh-action-ring-color-focus-visible: currentColor;
80
+ }
81
+
82
+ /* 整件禁用:输入框仍可选中复制,只有那颗写剪贴板的钮点不动 */
83
+ [data-scope='clipboard'][data-part='root'][data-disabled] {
84
+ cursor: not-allowed;
12
85
  }
13
86
 
14
87
  /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去 */
@@ -27,11 +100,11 @@
27
100
  display: none;
28
101
  }
29
102
 
30
- /* 输入框与按钮并排,两者各自把高度对齐即可成一体 */
103
+ /* 输入框与复制按钮共享一条连续轮廓。 */
31
104
  [data-scope='clipboard'][data-part='control'] {
32
105
  display: inline-flex;
33
106
  align-items: center;
34
- gap: var(--xh-clipboard-control-gap, var(--xh-control-gap-md));
107
+ gap: var(--xh-clipboard-control-gap, 0);
35
108
  }
36
109
 
37
110
  [data-scope='clipboard'][data-part='control'][hidden] {
@@ -39,9 +112,12 @@
39
112
  }
40
113
 
41
114
  [data-scope='clipboard'][data-part='input'] {
42
- min-inline-size: var(--xh-clipboard-input-min-w, var(--xh-control-min-w));
43
- block-size: var(--xh-clipboard-input-h, var(--xh-control-h-md));
44
- padding-inline: var(--xh-clipboard-input-px, var(--xh-control-px-md));
115
+ /* 撑满复制钮之外的剩余宽度;min-inline-size 归零解开原生 input 按字符数算出来的自动最小宽,
116
+ 根被压窄时输入框跟着收,不是钮先被顶出去 */
117
+ flex: 1 1 auto;
118
+ min-inline-size: 0;
119
+ block-size: var(--xh-clipboard-input-h, var(--xh-_clipboard-h));
120
+ padding-inline: var(--xh-clipboard-input-px, var(--xh-_clipboard-px));
45
121
  padding-block: 0;
46
122
  border: var(--xh-stroke-thin) solid var(--xh-clipboard-input-border, var(--xh-border-control));
47
123
  border-radius: var(--xh-clipboard-input-radius, var(--xh-shape-control));
@@ -57,83 +133,226 @@
57
133
  text-overflow: ellipsis;
58
134
  }
59
135
 
136
+ [data-scope='clipboard'][data-part='control'] > [data-scope='clipboard'][data-part='input']:not(:last-child) {
137
+ border-start-end-radius: 0;
138
+ border-end-end-radius: 0;
139
+ }
140
+
141
+ /* 焦点边一律 --xh-border-control-focus,不随 tone(§7.2 第 5 条) */
60
142
  [data-scope='clipboard'][data-part='input']:focus-visible {
61
- border-color: var(--xh-clipboard-input-border-focus, var(--xh-_tone, var(--xh-border-control-focus)));
62
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
63
- outline-offset: var(--xh-ring-offset);
143
+ border-color: var(--xh-clipboard-input-border-focus, var(--xh-border-control-focus));
64
144
  }
65
145
 
66
- [data-scope='clipboard'][data-part='trigger'] {
67
- display: inline-flex;
146
+ /* 自动填充:引擎给填过的框强加一套系统色(多数是黄底黑字),盖得过 background 与 color,
147
+ 深色档下就剩一个不跟主题走的黄框。底改用 inset 阴影铺回来,扩散量 100vmax 填满内容盒;
148
+ 字改用 -webkit-text-fill-color 接回来,那是这个态下唯一还听样式表的前景通道。
149
+ 不去拉长 transition 把系统底推迟显现:那只是把它藏起来,减弱动效通道一关过渡就当场露出。
150
+ 两个选择器名分写两条:一条选择器列表里只要有一个名字不被认识,整条规则就被丢弃。
151
+ 铺的底跟着这个框自己那档压低的底走,不然填过之后它会比没填时亮一块。 */
152
+ [data-scope='clipboard'][data-part='input']:autofill {
153
+ box-shadow: inset 0 0 0 100vmax var(--xh-clipboard-input-autofill-bg, var(--xh-bg-subtle));
154
+ -webkit-text-fill-color: var(--xh-clipboard-input-autofill-fg, var(--xh-fg-default));
155
+ }
156
+
157
+ [data-scope='clipboard'][data-part='input']:-webkit-autofill {
158
+ box-shadow: inset 0 0 0 100vmax var(--xh-clipboard-input-autofill-bg, var(--xh-bg-subtle));
159
+ -webkit-text-fill-color: var(--xh-clipboard-input-autofill-fg, var(--xh-fg-default));
160
+ }
161
+
162
+ /* 复制钮是定尺的独立动作按钮(§9.1):盒型、四态面、0.97 按压与粗指针命中区由家族配方给出,
163
+ 这里只把公开槽桥接到矩阵之前(使用者槽 → 形态矩阵 → 家族缺省)与几何槽映射。
164
+ 叠格布局要保留:两侧指示器叠在同一格,切换时不抖宽度 */
165
+ [data-scope='clipboard'][data-part='copy-trigger'] {
166
+ --xh-action-bg-rest: var(--xh-clipboard-copy-trigger-bg, var(--xh-_action-variant-bg-rest));
167
+ --xh-action-bg-hover: var(--xh-clipboard-copy-trigger-bg-hover, var(--xh-_action-variant-bg-hover));
168
+ --xh-action-bg-pressed: var(--xh-clipboard-copy-trigger-bg-active, var(--xh-_action-variant-bg-pressed));
169
+ --xh-action-bg-focus-visible: var(--xh-clipboard-copy-trigger-bg, var(--xh-_action-variant-bg-focus-visible));
170
+ --xh-action-bg-loading: var(--xh-clipboard-copy-trigger-bg, var(--xh-_action-variant-bg-loading));
171
+ --xh-action-bg-disabled: var(--xh-clipboard-copy-trigger-bg-disabled, var(--xh-_action-variant-bg-disabled));
172
+ --xh-action-fg-rest: var(--xh-clipboard-copy-trigger-fg, var(--xh-_action-variant-fg-rest));
173
+ --xh-action-fg-hover: var(--xh-clipboard-copy-trigger-fg, var(--xh-_action-variant-fg-hover));
174
+ --xh-action-fg-pressed: var(--xh-clipboard-copy-trigger-fg, var(--xh-_action-variant-fg-pressed));
175
+ --xh-action-fg-focus-visible: var(--xh-clipboard-copy-trigger-fg, var(--xh-_action-variant-fg-focus-visible));
176
+ --xh-action-fg-loading: var(--xh-clipboard-copy-trigger-fg, var(--xh-_action-variant-fg-loading));
177
+ --xh-action-border-rest: var(--xh-clipboard-copy-trigger-border, var(--xh-_action-variant-border-rest));
178
+ --xh-action-border-hover: var(--xh-clipboard-copy-trigger-border-hover, var(--xh-_action-variant-border-hover));
179
+ --xh-action-border-pressed: var(--xh-clipboard-copy-trigger-border-hover, var(--xh-_action-variant-border-pressed));
180
+ --xh-action-border-focus-visible: var(--xh-clipboard-copy-trigger-border, var(--xh-_action-variant-border-focus-visible));
181
+ --xh-action-border-disabled: var(--xh-clipboard-copy-trigger-border-disabled, var(--xh-_action-variant-border-disabled));
182
+ /* 矩阵各态无影(§8.3 淡底与阴影互斥):solid 也不再悬停抬影,使用者槽仍可自行加 */
183
+ --xh-action-shadow-hover: var(--xh-clipboard-copy-trigger-shadow-hover, none);
184
+ --xh-action-radius: var(--xh-clipboard-copy-trigger-radius, var(--xh-shape-control));
185
+ --xh-action-visual-size: var(--xh-clipboard-copy-trigger-h, var(--xh-_clipboard-h));
186
+ --xh-action-padding-inline: var(--xh-clipboard-copy-trigger-px, var(--xh-_clipboard-px));
187
+ --xh-action-gap: var(--xh-clipboard-copy-trigger-gap, var(--xh-control-gap-sm));
188
+ --xh-action-font-size: var(--xh-clipboard-copy-trigger-font-size, var(--xh-_clipboard-font-size));
189
+
190
+ display: inline-grid;
68
191
  flex: none;
69
- align-items: center;
70
- justify-content: center;
71
- gap: var(--xh-clipboard-trigger-gap, var(--xh-control-gap-sm));
72
- block-size: var(--xh-clipboard-trigger-h, var(--xh-control-h-md));
73
- padding-inline: var(--xh-clipboard-trigger-px, var(--xh-control-px-md));
74
- padding-block: 0;
75
- border: var(--xh-stroke-thin) solid var(--xh-clipboard-trigger-border, var(--xh-border-control));
76
- border-radius: var(--xh-clipboard-trigger-radius, var(--xh-shape-control));
77
- background: var(--xh-clipboard-trigger-bg, var(--xh-bg-canvas));
78
- color: var(--xh-clipboard-trigger-fg, var(--xh-fg-default));
79
- font-family: inherit;
80
- font-size: var(--xh-clipboard-trigger-font-size, var(--xh-text-body-size));
81
- line-height: var(--xh-leading-none);
82
- white-space: nowrap;
83
- cursor: pointer;
192
+ font-weight: var(--xh-clipboard-copy-trigger-font-weight, var(--xh-text-label-weight));
84
193
  /* 复制按钮常被连点,触屏上默认会当成双击缩放 */
85
- user-select: none;
86
194
  touch-action: manipulation;
87
- transition:
88
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
89
- border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
195
+ }
196
+
197
+ /* 接在只读输入框后面时补回外轮廓:与输入框共享一条连续的可操作区描边 */
198
+ [data-scope='clipboard'][data-part='control']
199
+ > [data-scope='clipboard'][data-part='copy-trigger']:not(:first-child) {
200
+ --xh-action-border-rest: var(--xh-clipboard-copy-trigger-border, var(--xh-border-control));
201
+ --xh-action-border-hover: var(--xh-clipboard-copy-trigger-border-hover, var(--xh-border-control-hover));
202
+ --xh-action-border-pressed: var(--xh-clipboard-copy-trigger-border-hover, var(--xh-border-control-hover));
203
+ --xh-action-border-focus-visible: var(--xh-clipboard-copy-trigger-border, var(--xh-border-control));
204
+ }
205
+
206
+ [data-scope='clipboard'][data-part='control'] > [data-scope='clipboard'][data-part='copy-trigger']:not(:first-child) {
207
+ position: relative;
208
+ margin-inline-start: calc(-1 * var(--xh-stroke-thin));
209
+ border-start-start-radius: 0;
210
+ border-end-start-radius: 0;
211
+ border-start-end-radius: var(
212
+ --xh-clipboard-copy-trigger-attached-radius,
213
+ var(--xh-clipboard-input-radius, var(--xh-shape-control))
214
+ );
215
+ border-end-end-radius: var(
216
+ --xh-clipboard-copy-trigger-attached-radius,
217
+ var(--xh-clipboard-input-radius, var(--xh-shape-control))
218
+ );
219
+ }
220
+
221
+ [data-scope='clipboard'][data-part='control'] > [data-scope='clipboard'][data-part='input']:focus-visible,
222
+ [data-scope='clipboard'][data-part='control'] > [data-scope='clipboard'][data-part='copy-trigger']:focus-visible,
223
+ [data-scope='clipboard'][data-part='control'] > [data-scope='clipboard'][data-part='copy-trigger']:hover {
224
+ position: relative;
225
+ z-index: var(--xh-clipboard-control-active-layer, 1);
90
226
  }
91
227
 
92
228
  /* 上面给了 display,UA 的 [hidden]{display:none} 已被盖掉,这里补回来 */
93
- [data-scope='clipboard'][data-part='trigger'][hidden] {
229
+ [data-scope='clipboard'][data-part='copy-trigger'][hidden] {
94
230
  display: none;
95
231
  }
96
232
 
97
- [data-scope='clipboard'][data-part='trigger']:hover {
98
- border-color: var(--xh-clipboard-trigger-border-hover, var(--xh-border-control-hover));
99
- background: var(--xh-clipboard-trigger-bg-hover, var(--xh-bg-subtle-hover));
233
+ /* 快速写入不显示瞬时圆环;超过一个 micro 周期后才平滑换成加载状态。 */
234
+ [data-scope='clipboard'][data-part='root'][data-state='copying']
235
+ [data-scope='clipboard'][data-part='copy-trigger'][aria-busy='true'] {
236
+ cursor: progress;
100
237
  }
101
238
 
102
- [data-scope='clipboard'][data-part='trigger']:active {
103
- background: var(--xh-clipboard-trigger-bg-active, var(--xh-bg-subtle-active));
239
+ [data-scope='clipboard'][data-part='copy-trigger'][aria-busy='true']::before {
240
+ content: '';
241
+ position: absolute;
242
+ inset: 0;
243
+ inline-size: 1em;
244
+ block-size: 1em;
245
+ margin: auto;
246
+ border: var(--xh-stroke-thin) solid transparent;
247
+ border-block-start-color: currentColor;
248
+ /* 正圆的加载环:正方盒取 circle(§6.3) */
249
+ border-radius: var(--xh-shape-circle);
250
+ opacity: 0;
251
+ animation:
252
+ xh-clipboard-rotate var(--xh-clipboard-loading-duration, var(--xh-spin-duration)) var(--xh-motion-ease-loop) infinite,
253
+ xh-clipboard-loading-reveal var(--xh-motion-duration-micro) var(--xh-motion-ease-enter) var(--xh-motion-duration-micro) forwards;
104
254
  }
105
255
 
106
- [data-scope='clipboard'][data-part='trigger']:focus-visible {
107
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
108
- outline-offset: var(--xh-ring-offset);
256
+ [data-scope='clipboard'][data-part='copy-trigger'][aria-busy='true']
257
+ > [data-scope='clipboard'][data-part='indicator']:not([data-copied]) {
258
+ animation: xh-clipboard-loading-hide var(--xh-motion-duration-micro) var(--xh-motion-ease-enter)
259
+ var(--xh-motion-duration-micro) forwards;
109
260
  }
110
261
 
111
- /* 写入在途:按钮还在原地不动,只把它压暗一点,免得连点 */
112
- [data-scope='clipboard'][data-part='trigger'][data-state='copying'] {
113
- cursor: progress;
114
- opacity: 0.7;
262
+ @media (prefers-reduced-motion: reduce) {
263
+ [data-scope='clipboard'][data-part='copy-trigger'][aria-busy='true']::before {
264
+ animation: none;
265
+ }
266
+
267
+ [data-scope='clipboard'][data-part='copy-trigger'][aria-busy='true']
268
+ > [data-scope='clipboard'][data-part='indicator']:not([data-copied]) {
269
+ animation: none;
270
+ }
115
271
  }
116
272
 
117
- /* 复制成功那一段换成成功色,撑住"这次真的写进去了"这句话 */
118
- [data-scope='clipboard'][data-part='trigger'][data-copied] {
119
- border-color: var(--xh-clipboard-trigger-border-copied, var(--xh-fg-success));
120
- color: var(--xh-clipboard-trigger-fg-copied, var(--xh-fg-success));
273
+ :where([data-motion='reduce']) [data-scope='clipboard'][data-part='copy-trigger'][aria-busy='true']::before,
274
+ :where([data-motion='reduce']) [data-scope='clipboard'][data-part='copy-trigger'][aria-busy='true']
275
+ > [data-scope='clipboard'][data-part='indicator']:not([data-copied]) {
276
+ animation: none;
277
+ }
278
+
279
+ /* 复制成功只换前景与内容,轮廓和尺寸保持稳定;各态前景经桥接槽换,悬停与按下不退回平时的字色 */
280
+ [data-scope='clipboard'][data-part='copy-trigger'][data-copied] {
281
+ --xh-action-fg-rest: var(--xh-clipboard-copy-trigger-fg-copied, var(--xh-fg-success));
282
+ --xh-action-fg-hover: var(--xh-clipboard-copy-trigger-fg-copied, var(--xh-fg-success));
283
+ --xh-action-fg-pressed: var(--xh-clipboard-copy-trigger-fg-copied, var(--xh-fg-success));
284
+ --xh-action-fg-focus-visible: var(--xh-clipboard-copy-trigger-fg-copied, var(--xh-fg-success));
285
+ }
286
+
287
+ /* 实心面上复制成功仍用面配对的前景色:成功色在语气底上读不清 */
288
+ [data-scope='clipboard'][data-part='copy-trigger'][data-xh-action-variant='solid'][data-copied] {
289
+ --xh-action-fg-rest: var(--xh-clipboard-copy-trigger-fg, var(--xh-_action-variant-fg-rest));
290
+ --xh-action-fg-hover: var(--xh-clipboard-copy-trigger-fg, var(--xh-_action-variant-fg-hover));
291
+ --xh-action-fg-pressed: var(--xh-clipboard-copy-trigger-fg, var(--xh-_action-variant-fg-pressed));
292
+ --xh-action-fg-focus-visible: var(--xh-clipboard-copy-trigger-fg, var(--xh-_action-variant-fg-focus-visible));
121
293
  }
122
294
 
123
295
  [data-scope='clipboard'][data-part='indicator'] {
124
296
  display: inline-flex;
125
297
  flex: none;
298
+ grid-area: 1 / 1;
126
299
  align-items: center;
127
300
  justify-content: center;
301
+ gap: var(
302
+ --xh-clipboard-indicator-gap,
303
+ var(--xh-clipboard-copy-trigger-gap, var(--xh-control-gap-sm))
304
+ );
305
+ opacity: 0;
306
+ visibility: hidden;
307
+ /* 视觉退出完成后才隐藏旧内容,避免旧侧先消失、新侧仍为透明时出现空白闪帧。 */
308
+ transition:
309
+ opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
310
+ visibility 0s var(--xh-motion-ease-exit) var(--xh-motion-duration-micro);
128
311
  }
129
312
 
130
- /* 两个指示器靠 hidden 互斥显隐;上面给了 display,这条不补的话收起态当场失效 */
313
+ /* 作者仍可用原生 hidden 收起整个指示器。 */
131
314
  [data-scope='clipboard'][data-part='indicator'][hidden] {
132
315
  display: none;
133
316
  }
134
317
 
318
+ [data-scope='clipboard'][data-part='indicator']:not([data-copied]),
319
+ [data-scope='clipboard'][data-part='root'][data-state='copied'] [data-scope='clipboard'][data-part='indicator'][data-copied] {
320
+ opacity: 1;
321
+ visibility: visible;
322
+ transition:
323
+ opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
324
+ visibility 0s var(--xh-motion-ease-enter) 0s;
325
+ }
326
+
327
+ [data-scope='clipboard'][data-part='root'][data-state='copied'] [data-scope='clipboard'][data-part='indicator']:not([data-copied]) {
328
+ opacity: 0;
329
+ visibility: hidden;
330
+ transition:
331
+ opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
332
+ visibility 0s var(--xh-motion-ease-exit) var(--xh-motion-duration-micro);
333
+ }
334
+
135
335
  /* 成功那一侧的图标用成功色;平时那侧跟着按钮的文字色走 */
136
336
  [data-scope='clipboard'][data-part='indicator'][data-copied] {
137
337
  color: var(--xh-clipboard-indicator-fg-copied, var(--xh-fg-success));
138
338
  }
339
+
340
+ [data-scope='clipboard'][data-part='copy-trigger'][data-xh-action-variant='solid'] > [data-scope='clipboard'][data-part='indicator'][data-copied] {
341
+ color: inherit;
342
+ }
343
+
344
+ /* 复制成功的文字回执:读屏念得到,屏幕上不占位。
345
+ 视觉隐藏的配方与 .xh-visually-hidden 逐条同值——1px 盒绝对定位、裁掉、文本不换行。
346
+ 刻意不写 display:display:none 的节点读屏根本不念 */
347
+ [data-scope='clipboard'][data-part='status'] {
348
+ position: absolute;
349
+ inline-size: 1px;
350
+ block-size: 1px;
351
+ padding: 0;
352
+ margin: -1px;
353
+ border: 0;
354
+ overflow: hidden;
355
+ clip-path: inset(50%);
356
+ white-space: nowrap;
357
+ }
139
358
  }
@@ -0,0 +1,279 @@
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
+ /* 定义 code view 样式。 */
7
+
8
+ /* 独立皮肤同时传递引入折叠条的动作控件配方;full bundle 递归去重。 */
9
+ @import '../family/action-control.css';
10
+
11
+ @layer xihan.components {
12
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
13
+ [data-scope='code-view'] {
14
+ --xh-code-view-skin: 1;
15
+ }
16
+
17
+ /* 一张静态内容面:--xh-border-default 描边 + surface 底 + 无影(§5.3 边界只由描边承担),
18
+ overflow: hidden 让内部 <pre> 的滚动内容沿圆角收边;不设 display,排布由作者决定。
19
+ color 给整段未着色的代码定基线,字号在这里消费一次,缺省即 sm 档。 */
20
+ [data-scope='code-view'][data-part='root'] {
21
+ /* 尺寸缺省档:不写 data-size 时的取值。三档都只写这个私有槽,
22
+ 公开槽留在读处当首选,作者在祖先上设的覆盖才赢得过档位声明 */
23
+ --xh-_code-view-font-size: var(--xh-control-font-sm);
24
+
25
+ overflow: hidden;
26
+ border: var(--xh-stroke-thin) solid var(--xh-code-view-border, var(--xh-border-default));
27
+ border-radius: var(--xh-code-view-radius, var(--xh-shape-surface));
28
+ background: var(--xh-code-view-bg, var(--xh-bg-surface));
29
+ box-shadow: var(--xh-code-view-shadow, none);
30
+ color: var(--xh-code-view-fg, var(--xh-fg-muted));
31
+ font-size: var(--xh-code-view-font-size, var(--xh-_code-view-font-size));
32
+ }
33
+
34
+ /* min-block-size 给头部条一个不随内容塌陷的定高:只放文件名时与放进按钮时一样高 */
35
+ [data-scope='code-view'][data-part='header'] {
36
+ display: flex;
37
+ min-block-size: var(--xh-code-view-header-h, var(--xh-control-h-lg));
38
+ align-items: center;
39
+ gap: var(--xh-code-view-header-gap, var(--xh-space-2));
40
+ padding-inline: var(--xh-code-view-header-px, var(--xh-space-4));
41
+ padding-block: var(--xh-code-view-header-py, var(--xh-space-2));
42
+ border-block-end: var(--xh-stroke-thin) solid var(--xh-code-view-header-border, var(--xh-border-subtle));
43
+ color: var(--xh-code-view-header-fg, var(--xh-fg-muted));
44
+ font-size: var(--xh-code-view-header-font-size, var(--xh-text-secondary-size));
45
+ line-height: var(--xh-leading-tight);
46
+ }
47
+
48
+ /* 上面声明的 display 会盖掉 UA 的 [hidden]{display:none},这里补回 */
49
+ [data-scope='code-view'][data-part='header'][hidden] {
50
+ display: none;
51
+ }
52
+
53
+ /* 文件名占满剩余宽度并单行截断:长路径不该把语言角标挤出去 */
54
+ [data-scope='code-view'][data-part='filename'] {
55
+ display: block;
56
+ overflow: hidden;
57
+ flex: 1 1 auto;
58
+ color: var(--xh-code-view-filename-fg, var(--xh-fg-default));
59
+ font-family: var(--xh-code-view-font, var(--xh-font-family-mono));
60
+ text-overflow: ellipsis;
61
+ white-space: nowrap;
62
+ }
63
+
64
+ [data-scope='code-view'][data-part='filename'][hidden] {
65
+ display: none;
66
+ }
67
+
68
+ /* user-select: none 让它不被框选带走 */
69
+ [data-scope='code-view'][data-part='lang-label'] {
70
+ display: block;
71
+ flex: 0 0 auto;
72
+ color: var(--xh-code-view-label-fg, var(--xh-fg-subtle));
73
+ font-size: var(--xh-code-view-label-font-size, var(--xh-text-caption-size));
74
+ line-height: var(--xh-leading-none);
75
+ user-select: none;
76
+ }
77
+
78
+ [data-scope='code-view'][data-part='lang-label'][hidden] {
79
+ display: none;
80
+ }
81
+
82
+ /* margin: 0 去掉 UA 给 <pre> 的默认外边距。
83
+ --xh-code-view-line-height 的兜底须与连接层内联 min-block-size 用的一致,
84
+ 且因参与 calc 乘法必须是长度而非无单位倍数。 */
85
+ [data-scope='code-view'][data-part='pre'] {
86
+ /* 逻辑属性 overflow-inline 尚无可用实现,写物理轴 */
87
+ overflow-x: auto;
88
+ padding-block: var(--xh-code-view-py, var(--xh-space-3));
89
+ margin: 0;
90
+ line-height: var(--xh-code-view-line-height, var(--xh-text-code-leading));
91
+ }
92
+
93
+ /* 折叠时 <pre> 的高度被连接层写死,超出部分沿圆角收边 */
94
+ [data-scope='code-view'][data-part='root'][data-clamped] [data-part='pre'] {
95
+ overflow-y: hidden;
96
+ }
97
+
98
+ [data-scope='code-view'][data-part='code'] {
99
+ display: block;
100
+ font-family: var(--xh-code-view-font, var(--xh-font-family-mono));
101
+ }
102
+
103
+ [data-scope='code-view'][data-part='code'][hidden] {
104
+ display: none;
105
+ }
106
+
107
+ /* 一行是一个块:行号槽与正文并排,空行也要占满一行高 */
108
+ [data-scope='code-view'][data-part='line'] {
109
+ display: flex;
110
+ min-block-size: var(--xh-code-view-line-height, var(--xh-text-code-leading));
111
+ padding-inline-end: var(--xh-code-view-px, var(--xh-space-3));
112
+ /* 标出来的那几行换的是底色与那条内阴影色条,两样一起过渡才是同一个动作 */
113
+ transition:
114
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
115
+ box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
116
+ }
117
+
118
+ [data-scope='code-view'][data-part='line'][hidden] {
119
+ display: none;
120
+ }
121
+
122
+ [data-scope='code-view'][data-part='line'][data-highlighted] {
123
+ background: var(--xh-code-view-highlight-bg, var(--xh-bg-brand-subtle));
124
+ box-shadow: inset var(--xh-code-view-highlight-bar, var(--xh-stroke-thick)) 0 0 0 var(--xh-code-view-highlight-fg, var(--xh-bg-brand));
125
+ }
126
+
127
+ /* 行号是画上去的不是写上去的:复制代码不带它,读屏也不逐行念数字。
128
+ 节点本身由适配器按 lineNumbers 决定渲不渲,槽宽按 root 上的位数枚举定,见下方 data-digits 各档。
129
+ 行与行之间无间隙,逐行的右边框首尾相接连成一条分隔线;行号比正文小一档、右对齐,多位数才齐。 */
130
+ [data-scope='code-view'][data-part='line-number'] {
131
+ display: block;
132
+ box-sizing: content-box;
133
+ flex: 0 0 auto;
134
+ padding-inline-start: var(--xh-code-view-px, var(--xh-space-3));
135
+ padding-inline-end: var(--xh-code-view-gutter-gap, var(--xh-space-1));
136
+ border-inline-end: var(--xh-stroke-thin) solid var(--xh-code-view-gutter-border, var(--xh-border-default));
137
+ color: var(--xh-code-view-number-fg, var(--xh-fg-subtle));
138
+ font-size: var(--xh-code-view-number-font-size, var(--xh-text-caption-size));
139
+ inline-size: var(--xh-_code-view-gutter);
140
+ text-align: end;
141
+ user-select: none;
142
+ }
143
+
144
+ [data-scope='code-view'][data-part='line-number']::before {
145
+ content: attr(data-line-number);
146
+ }
147
+
148
+ [data-scope='code-view'][data-part='line-number'][hidden] {
149
+ display: none;
150
+ }
151
+
152
+ /* white-space: pre 保留缩进不自动换行,长行改为横向滚动 */
153
+ [data-scope='code-view'][data-part='line-content'] {
154
+ display: block;
155
+ flex: 1 1 auto;
156
+ white-space: pre;
157
+ }
158
+
159
+ [data-scope='code-view'][data-part='line-content'][hidden] {
160
+ display: none;
161
+ }
162
+
163
+ /* 开了换行:pre-wrap 仍保留缩进与连续空白,只是允许行尾折下来;
164
+ anywhere 让没有空格的长串(url、base64)也能断,否则照样撑出横向滚动 */
165
+ [data-scope='code-view'][data-part='code'][data-wrap] [data-part='line-content'] {
166
+ white-space: pre-wrap;
167
+ overflow-wrap: anywhere;
168
+ }
169
+
170
+ /* 没开行号时正文自带左内衬;开了行号则由行号槽的内衬顶开 */
171
+ [data-scope='code-view'][data-part='root']:not([data-line-numbers]) [data-part='line-content'] {
172
+ padding-inline-start: var(--xh-code-view-px, var(--xh-space-3));
173
+ }
174
+
175
+ /* 行号槽宽按位数分档:连接层写不了 CSS 自定义属性,只能发 data-digits 让这里自己设 */
176
+ [data-scope='code-view'][data-part='root'] {
177
+ --xh-_code-view-gutter: 1ch;
178
+ }
179
+
180
+ [data-scope='code-view'][data-part='root'][data-digits='2'] {
181
+ --xh-_code-view-gutter: 2ch;
182
+ }
183
+
184
+ [data-scope='code-view'][data-part='root'][data-digits='3'] {
185
+ --xh-_code-view-gutter: 3ch;
186
+ }
187
+
188
+ [data-scope='code-view'][data-part='root'][data-digits='4'] {
189
+ --xh-_code-view-gutter: 4ch;
190
+ }
191
+
192
+ [data-scope='code-view'][data-part='root'][data-digits='5'] {
193
+ --xh-_code-view-gutter: 5ch;
194
+ }
195
+
196
+ [data-scope='code-view'][data-part='root'][data-digits='6'] {
197
+ --xh-_code-view-gutter: 6ch;
198
+ }
199
+
200
+ [data-scope='code-view'][data-part='root'][data-digits='7'] {
201
+ --xh-_code-view-gutter: 7ch;
202
+ }
203
+
204
+ /* 折叠条是铺满一行的 disclosure trigger,盒型、悬停与按下面由 Action Control 的 disclosure-trigger 档给
205
+ (ghost 形态:白底承载 hover 100 → pressed 200,按下只换面不缩放,§9.2);这里只把使用者槽映射到
206
+ 桥接槽:圆角归零贴住卡边,内衬沿用代码区的横向内距与折叠条自己的纵向内距,字号与头部条同档。
207
+ 顶边是与头部条同一支的内部分隔线:配方按状态写 border-color 简写,四个状态的边色槽都映射到
208
+ 这支分隔色,再把四边宽归零、只留顶边一条 */
209
+ [data-scope='code-view'][data-part='fold-trigger'] {
210
+ --xh-action-radius: 0;
211
+ --xh-action-padding-inline: var(--xh-code-view-px, var(--xh-space-3));
212
+ --xh-action-padding-block: var(--xh-code-view-fold-py, var(--xh-space-2));
213
+ --xh-action-font-size: var(--xh-code-view-header-font-size, var(--xh-text-secondary-size));
214
+ --xh-action-fg-rest: var(--xh-code-view-fold-fg, var(--xh-fg-muted));
215
+ --xh-action-bg-hover: var(--xh-code-view-fold-bg-hover, var(--xh-_action-variant-bg-hover));
216
+ --xh-action-border-rest: var(--xh-code-view-header-border, var(--xh-border-subtle));
217
+ --xh-action-border-hover: var(--xh-code-view-header-border, var(--xh-border-subtle));
218
+ --xh-action-border-pressed: var(--xh-code-view-header-border, var(--xh-border-subtle));
219
+ --xh-action-border-focus-visible: var(--xh-code-view-header-border, var(--xh-border-subtle));
220
+
221
+ border-width: 0;
222
+ border-block-start-width: var(--xh-stroke-thin);
223
+ }
224
+
225
+ [data-scope='code-view'][data-part='fold-trigger'][hidden] {
226
+ display: none;
227
+ }
228
+
229
+ /* 尺寸档写私有槽、由 root 基础规则消费一次:缺省与阶梯是同一个口子,
230
+ 作者设的 --xh-code-view-font-size 排在读处首选,不会被这三条盖掉。
231
+ 行号槽宽以 ch 计,随字号一起走 */
232
+ [data-scope='code-view'][data-part='root'][data-size='sm'] {
233
+ --xh-_code-view-font-size: var(--xh-control-font-sm);
234
+ }
235
+
236
+ [data-scope='code-view'][data-part='root'][data-size='md'] {
237
+ --xh-_code-view-font-size: var(--xh-control-font-md);
238
+ }
239
+
240
+ [data-scope='code-view'][data-part='root'][data-size='lg'] {
241
+ --xh-_code-view-font-size: var(--xh-control-font-lg);
242
+ }
243
+
244
+ /* 记号本身不带任何布局,只上色:它是行内片段,多一点盒模型都会破坏对齐 */
245
+ [data-scope='code-view'][data-part='token'][data-kind='comment'] {
246
+ color: var(--xh-code-view-comment-fg, var(--xh-fg-muted));
247
+ font-style: italic;
248
+ }
249
+
250
+ [data-scope='code-view'][data-part='token'][data-kind='string'] {
251
+ color: var(--xh-code-view-string-fg, var(--xh-syntax-string));
252
+ }
253
+
254
+ [data-scope='code-view'][data-part='token'][data-kind='number'] {
255
+ color: var(--xh-code-view-number-token-fg, var(--xh-syntax-number));
256
+ }
257
+
258
+ [data-scope='code-view'][data-part='token'][data-kind='keyword'] {
259
+ color: var(--xh-code-view-keyword-fg, var(--xh-syntax-keyword));
260
+ font-weight: var(--xh-code-view-keyword-weight, var(--xh-font-weight-semibold));
261
+ }
262
+
263
+ [data-scope='code-view'][data-part='token'][data-kind='punctuation'] {
264
+ color: var(--xh-code-view-punctuation-fg, var(--xh-fg-subtle));
265
+ }
266
+
267
+ /*
268
+ * 打印:标出来的那几行只有一层淡底加一条写在 inset 阴影上的实心条,
269
+ * 底多数打印机丢掉、投影本就不该上纸,标了哪几行在纸上完全读不出来。
270
+ * 改画一圈内收的实边把那几行框出来。用 outline 不用 border:
271
+ * 行是横排 flex,加边会把行号与正文一起往里推,没标的行不加边就对不齐了。
272
+ */
273
+ @media print {
274
+ [data-scope='code-view'][data-part='line'][data-highlighted] {
275
+ outline: var(--xh-code-view-highlight-bar, var(--xh-stroke-thick)) solid currentColor;
276
+ outline-offset: calc(-1 * var(--xh-code-view-highlight-bar, var(--xh-stroke-thick)));
277
+ }
278
+ }
279
+ }