@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
@@ -1,9 +1,25 @@
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
+ /* 定义 infinite scroll 样式。 */
7
+
8
+ /* 独立皮肤同时传递引入取下一页按钮的动作控件配方,full bundle 递归去重。 */
9
+ @import '../family/action-control.css';
10
+
1
11
  @layer xihan.components {
2
12
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
13
  [data-scope='infinite-scroll'] {
4
14
  --xh-infinite-scroll-skin: 1;
5
15
  }
6
16
 
17
+ /* 这里的 data-disabled 关掉的是「还取不取下一页」,列表本身照常可读可点:
18
+ 光标交回给作者,不摆共享层那个「不可用」的样子 */
19
+ [data-scope='infinite-scroll'][data-part='root'][data-disabled] {
20
+ cursor: inherit;
21
+ }
22
+
7
23
  /*
8
24
  * 列表外壳这一层刻意不给盒模型:滚的是页面还是作者自己的 overflow 容器、列表长什么样,
9
25
  * 都归作者的布局管,组件只往它身上挂 data-loading / data-disabled 供作者取用。
@@ -20,8 +36,42 @@
20
36
  pointer-events: none;
21
37
  }
22
38
 
39
+ /*
40
+ * 取下一页的按钮:与哨兵同一条通路的键盘入口,摆在列表末尾自成一行——铺满一行的独立动作条目,
41
+ * 接 Action Control 的 row 档、outline 形态(§9.2):宽度由容器给、高度随内容、按下只换面不缩放;
42
+ * 盒几何、中性描边与透明底、悬停 / 按下 / 禁用面、焦点环与粗指针热区全由家族给,
43
+ * 白底承载 hover 100 → pressed 200。这里只把使用者槽映射到桥接槽,并把文案居中:
44
+ * 它是一行里唯一的东西,靠起始缘会像一条没对齐的列表项。
45
+ * 行高不吃家族的单行档:row 档允许换行、高度随内容,折行的文案走正文行高(§6.4)
46
+ */
47
+ [data-scope='infinite-scroll'][data-part='load-more-trigger'] {
48
+ --xh-action-gap: var(--xh-infinite-scroll-load-more-gap, var(--xh-_action-profile-gap));
49
+ --xh-action-visual-size: var(--xh-infinite-scroll-load-more-h, var(--xh-_action-profile-visual-size));
50
+ --xh-action-padding-inline: var(--xh-infinite-scroll-load-more-px, var(--xh-_action-profile-padding-inline));
51
+ --xh-action-radius: var(--xh-infinite-scroll-load-more-radius, var(--xh-_action-profile-radius));
52
+ --xh-action-border-rest: var(--xh-infinite-scroll-load-more-border, var(--xh-_action-variant-border-rest));
53
+ --xh-action-border-hover: var(--xh-infinite-scroll-load-more-border-hover, var(--xh-_action-variant-border-hover));
54
+ --xh-action-border-pressed: var(--xh-infinite-scroll-load-more-border-hover, var(--xh-_action-variant-border-pressed));
55
+ --xh-action-bg-rest: var(--xh-infinite-scroll-load-more-bg, var(--xh-_action-variant-bg-rest));
56
+ --xh-action-bg-hover: var(--xh-infinite-scroll-load-more-bg-hover, var(--xh-_action-variant-bg-hover));
57
+ --xh-action-bg-pressed: var(--xh-infinite-scroll-load-more-bg-active, var(--xh-_action-variant-bg-pressed));
58
+ --xh-action-fg-rest: var(--xh-infinite-scroll-load-more-fg, var(--xh-_action-variant-fg-rest));
59
+ --xh-action-fg-hover: var(--xh-infinite-scroll-load-more-fg, var(--xh-_action-variant-fg-hover));
60
+ --xh-action-fg-pressed: var(--xh-infinite-scroll-load-more-fg, var(--xh-_action-variant-fg-pressed));
61
+ --xh-action-font-size: var(--xh-infinite-scroll-load-more-font-size, var(--xh-text-body-size));
62
+ /* 作者塞进钮里的图标按档取字形尺寸,与文案同一把尺 */
63
+ --xh-icon-size: var(--xh-infinite-scroll-load-more-icon-size, var(--xh-_action-profile-glyph-size));
64
+
65
+ justify-content: center;
66
+ text-align: center;
67
+ line-height: var(--xh-leading-normal);
68
+ /* 到底了连点是常态,触屏上默认会当成双击缩放 */
69
+ touch-action: manipulation;
70
+ }
71
+
23
72
  /* 声明过 display 就盖掉了 UA 的 [hidden]{display:none},作者要藏哨兵时得自己补回来 */
24
- [data-scope='infinite-scroll'][data-part='sentinel'][hidden] {
73
+ [data-scope='infinite-scroll'][data-part='sentinel'][hidden],
74
+ [data-scope='infinite-scroll'][data-part='load-more-trigger'][hidden] {
25
75
  display: none;
26
76
  }
27
77
  }
@@ -0,0 +1,277 @@
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
+ /* 定义 input group 样式。 */
7
+
8
+ @layer xihan.components {
9
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
10
+ [data-scope='input-group'] {
11
+ --xh-input-group-skin: 1;
12
+ }
13
+
14
+ /* 输入框、前后缀和动作共用一个输入表面。组只统一材质与外轮廓,语义仍归各子控件。 */
15
+ [data-scope='input-group'][data-part='root'] {
16
+ /* 段之间那几档层号只在自己人之间排序:建一个层叠上下文把它们关起来,
17
+ 既不让页面上的层号插进来,也不让它们漏出去压住别人 */
18
+ isolation: isolate;
19
+
20
+ /* 缺省档。item 的高度、内衬与字号从这三个槽取,换档只改这里 */
21
+ --xh-_input-group-h: var(--xh-control-h-md);
22
+ --xh-_input-group-px: var(--xh-control-px-md);
23
+ --xh-_input-group-font-size: var(--xh-control-font-md);
24
+ --xh-_input-group-bg: transparent;
25
+ --xh-_input-group-bg-hover: color-mix(in oklab, var(--xh-bg-subtle) 45%, transparent);
26
+ /* 组壳按字段外壳画 outline 描边(§8.3):静息 --xh-border-control、悬停升 -hover、聚焦换 -focus,无影 */
27
+ --xh-_input-group-border: var(--xh-border-control);
28
+ --xh-_input-group-border-hover: var(--xh-border-control-hover);
29
+ --xh-_input-group-border-focus: var(--xh-border-control-focus);
30
+ --xh-_input-group-shadow: none;
31
+
32
+ position: relative;
33
+ display: inline-flex;
34
+ box-sizing: border-box;
35
+ align-items: stretch;
36
+ border-radius: var(--xh-input-group-radius, var(--xh-shape-control));
37
+ background: var(--xh-input-group-bg, var(--xh-_input-group-bg));
38
+ box-shadow: var(--xh-input-group-shadow, var(--xh-_input-group-shadow));
39
+
40
+ /* 父容器给不出这么宽时整组跟着收,不越出父容器 */
41
+ max-inline-size: 100%;
42
+ transition:
43
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
44
+ box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
45
+ }
46
+
47
+ /* 外轮廓不占盒尺寸,组内控件仍保持与独立使用时相同的高度。 */
48
+ [data-scope='input-group'][data-part='root']::before {
49
+ content: '';
50
+ position: absolute;
51
+ z-index: 2;
52
+ inset: 0;
53
+ border: var(--xh-stroke-thin) solid var(--xh-input-group-border, var(--xh-_input-group-border));
54
+ border-radius: inherit;
55
+ pointer-events: none;
56
+ transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
57
+ }
58
+
59
+ /* subtle:淡底顶替描边表达边界,描边只退成透明而不去掉;悬停浮出 --xh-border-control(§8.3) */
60
+ [data-scope='input-group'][data-part='root'][data-variant='subtle'] {
61
+ --xh-_input-group-bg: var(--xh-bg-subtle);
62
+ --xh-_input-group-bg-hover: var(--xh-bg-subtle-hover);
63
+ --xh-_input-group-border: transparent;
64
+ --xh-_input-group-border-hover: var(--xh-border-control);
65
+ --xh-_input-group-shadow: none;
66
+ }
67
+
68
+ /* ghost:静息不画壳,悬停与聚焦沿用下面的 hover / focus 规则浮出 --xh-border-control 与底 */
69
+ [data-scope='input-group'][data-part='root'][data-variant='ghost'] {
70
+ --xh-_input-group-bg: transparent;
71
+ --xh-_input-group-border: transparent;
72
+ --xh-_input-group-border-hover: var(--xh-border-control);
73
+ --xh-_input-group-shadow: none;
74
+ }
75
+
76
+ [data-scope='input-group'][data-part='root']:not(:has([data-disabled])):hover {
77
+ background: var(--xh-input-group-bg-hover, var(--xh-_input-group-bg-hover));
78
+ }
79
+
80
+ [data-scope='input-group'][data-part='root']:not(:has([data-disabled])):hover::before {
81
+ border-color: var(--xh-input-group-border-hover, var(--xh-_input-group-border-hover));
82
+ }
83
+
84
+ [data-scope='input-group'][data-part='root']:focus-within {
85
+ outline: var(--xh-ring-width) solid var(--xh-input-group-ring-focus, var(--xh-ring-focus));
86
+ outline-offset: var(--xh-ring-offset);
87
+ }
88
+
89
+ [data-scope='input-group'][data-part='root']:focus-within::before {
90
+ border-color: var(--xh-input-group-border-focus, var(--xh-_input-group-border-focus));
91
+ }
92
+
93
+ [data-scope='input-group'][data-part='root']:has([data-invalid], [aria-invalid='true'])::before {
94
+ border-color: var(--xh-input-group-border-invalid, var(--xh-border-invalid));
95
+ }
96
+
97
+ [data-scope='input-group'][data-part='root']:has([data-invalid], [aria-invalid='true']):focus-within {
98
+ outline-color: var(--xh-input-group-ring-invalid, var(--xh-ring-invalid));
99
+ }
100
+
101
+ /* 给了 display 的角色节点必须补这条:display 声明会盖掉 UA 的 [hidden]{display:none},
102
+ 不补的话作者给节点加 hidden 后它照样显示 */
103
+ [data-scope='input-group'][data-part='root'][hidden] {
104
+ display: none;
105
+ }
106
+
107
+ /* —— 尺寸 —— */
108
+ /* 档位跟着组内控件走:控件的档位就写在它自己的根上,组读得到,
109
+ 于是前后缀块与邻座同高,作者不必把同一档在组上再写一遍。
110
+ 缺省档写在上面的基础规则里 */
111
+ [data-scope='input-group'][data-part='root']:has([data-size='sm']) {
112
+ --xh-_input-group-h: var(--xh-control-h-sm);
113
+ --xh-_input-group-px: var(--xh-control-px-sm);
114
+ --xh-_input-group-font-size: var(--xh-control-font-sm);
115
+ }
116
+
117
+ [data-scope='input-group'][data-part='root']:has([data-size='lg']) {
118
+ --xh-_input-group-h: var(--xh-control-h-lg);
119
+ --xh-_input-group-px: var(--xh-control-px-lg);
120
+ --xh-_input-group-font-size: var(--xh-control-font-lg);
121
+ }
122
+
123
+ /* 组上写了档位时以它为准:排在自动档后面,同特指度下压得住 */
124
+ [data-scope='input-group'][data-part='root'][data-size='sm'] {
125
+ --xh-_input-group-h: var(--xh-control-h-sm);
126
+ --xh-_input-group-px: var(--xh-control-px-sm);
127
+ --xh-_input-group-font-size: var(--xh-control-font-sm);
128
+ }
129
+
130
+ [data-scope='input-group'][data-part='root'][data-size='md'] {
131
+ --xh-_input-group-h: var(--xh-control-h-md);
132
+ --xh-_input-group-px: var(--xh-control-px-md);
133
+ --xh-_input-group-font-size: var(--xh-control-font-md);
134
+ }
135
+
136
+ [data-scope='input-group'][data-part='root'][data-size='lg'] {
137
+ --xh-_input-group-h: var(--xh-control-h-lg);
138
+ --xh-_input-group-px: var(--xh-control-px-lg);
139
+ --xh-_input-group-font-size: var(--xh-control-font-lg);
140
+ }
141
+
142
+ /* 子控件不再各画一层输入表面,悬停与聚焦统一落在组的外轮廓。 */
143
+ [data-scope='input-group'][data-part='root'] [data-xh-field-chrome] {
144
+ --xh-field-bg-rest: transparent;
145
+ --xh-field-bg-hover: transparent;
146
+ --xh-field-bg-read-only: transparent;
147
+ --xh-field-bg-disabled: transparent;
148
+ --xh-field-border-rest: transparent;
149
+ --xh-field-border-hover: transparent;
150
+ --xh-field-border-focus: transparent;
151
+ --xh-field-border-invalid: transparent;
152
+ --xh-field-shadow-rest: none;
153
+ --xh-field-shadow-read-only: none;
154
+ --xh-field-shadow-disabled: none;
155
+
156
+ border-color: transparent;
157
+ background-color: transparent;
158
+ box-shadow: none;
159
+ /* 内层表面始终透明,不播放字段自身的边框/背景过渡,避免失焦时短暂闪回。 */
160
+ transition: none;
161
+ }
162
+
163
+ /* 焦点环由组的外表面绘制,子输入保留焦点语义但不再重复画第二圈。 */
164
+ [data-scope='input-group'][data-part='root'] [data-xh-field-chrome]:focus-within {
165
+ outline: none;
166
+ }
167
+
168
+ [data-scope='input-group'][data-part='root'] [data-xh-field-chrome]:hover,
169
+ [data-scope='input-group'][data-part='root'] [data-xh-field-chrome]:focus-within,
170
+ [data-scope='input-group'][data-part='root'] [data-xh-field-chrome][data-invalid],
171
+ [data-scope='input-group'][data-part='root'] [data-xh-field-chrome][data-readonly],
172
+ [data-scope='input-group'][data-part='root'] [data-xh-field-chrome][data-disabled] {
173
+ border-color: transparent;
174
+ background-color: transparent;
175
+ box-shadow: none;
176
+ }
177
+
178
+ /* 每一段都是定位元素,动作控件的悬停面可以浮到外轮廓之上。 */
179
+ [data-scope='input-group'][data-part='root'] > * {
180
+ position: relative;
181
+ }
182
+
183
+ /* 悬停或拿到焦点的动作段抬到外轮廓同层,自己的反馈不会被邻座覆盖。 */
184
+ [data-scope='input-group'][data-part='root'] > *:hover,
185
+ [data-scope='input-group'][data-part='root'] > *:focus-within {
186
+ z-index: 2;
187
+ }
188
+
189
+ /* —— 两端圆角 —— */
190
+ /* 画着描边的那个盒子有三种落点:段本身、段就是控件盒、控件盒在段里一层。
191
+ :is() 取其中最特指的一支算特指度,三种落点因此都压得住控件自己那条圆角规则。
192
+ 只认直接子节点与它下面那一层:更深的节点是控件的内部结构,圆错了位置比直角更糟 */
193
+ [data-scope='input-group'][data-part='root'] > :is(*, [data-part='control'], [data-part='root']),
194
+ [data-scope='input-group'][data-part='root'] > * :is([data-part='control'], [data-part='root']) {
195
+ border-start-start-radius: 0;
196
+ border-start-end-radius: 0;
197
+ border-end-start-radius: 0;
198
+ border-end-end-radius: 0;
199
+ }
200
+
201
+ /* 逻辑角属性写,rtl 跟着换边,不必另写一套 */
202
+ [data-scope='input-group'][data-part='root'] > :is(*, [data-part='control'], [data-part='root']):first-child,
203
+ [data-scope='input-group'][data-part='root'] > *:first-child :is([data-part='control'], [data-part='root']) {
204
+ border-start-start-radius: var(--xh-input-group-radius, var(--xh-shape-control));
205
+ border-end-start-radius: var(--xh-input-group-radius, var(--xh-shape-control));
206
+ }
207
+
208
+ [data-scope='input-group'][data-part='root'] > :is(*, [data-part='control'], [data-part='root']):last-child,
209
+ [data-scope='input-group'][data-part='root'] > *:last-child :is([data-part='control'], [data-part='root']) {
210
+ border-start-end-radius: var(--xh-input-group-radius, var(--xh-shape-control));
211
+ border-end-end-radius: var(--xh-input-group-radius, var(--xh-shape-control));
212
+ }
213
+
214
+ /* —— 前后缀块 —— */
215
+ /* 前后缀是输入表面里的静态内容,不单独绘制底色、描边或焦点状态。 */
216
+ [data-scope='input-group'][data-part='item'] {
217
+ display: inline-flex;
218
+ box-sizing: border-box;
219
+ flex: none;
220
+ align-items: center;
221
+ block-size: var(--xh-input-group-item-h, var(--xh-_input-group-h));
222
+ padding-inline: var(--xh-input-group-item-px, var(--xh-_input-group-px));
223
+ color: var(--xh-input-group-item-fg, var(--xh-fg-muted));
224
+ font-size: var(--xh-input-group-item-font-size, var(--xh-_input-group-font-size));
225
+ line-height: var(--xh-leading-none);
226
+ white-space: nowrap;
227
+ }
228
+
229
+ /* 与输入控件相邻的一侧由控件自己的内距留白,避免前后缀之间出现双份间距。 */
230
+ [data-scope='input-group'][data-part='root'] > [data-scope='input-group'][data-part='item']:first-child {
231
+ padding-inline-end: 0;
232
+ }
233
+
234
+ [data-scope='input-group'][data-part='root'] > [data-scope='input-group'][data-part='item']:last-child {
235
+ padding-inline-start: 0;
236
+ }
237
+
238
+ [data-scope='input-group'][data-part='item'][hidden] {
239
+ display: none;
240
+ }
241
+
242
+ @media (prefers-reduced-motion: reduce) {
243
+ [data-scope='input-group'][data-part='root'],
244
+ [data-scope='input-group'][data-part='root']::before {
245
+ transition: none;
246
+ }
247
+ }
248
+
249
+ :where([data-motion='reduce']) [data-scope='input-group'][data-part='root'],
250
+ :where([data-motion='reduce']) [data-scope='input-group'][data-part='root']::before {
251
+ transition: none;
252
+ }
253
+
254
+ @media (forced-colors: active) {
255
+ [data-scope='input-group'][data-part='root'] {
256
+ background: Canvas;
257
+ box-shadow: none;
258
+ }
259
+
260
+ [data-scope='input-group'][data-part='root']::before {
261
+ border-color: ButtonText;
262
+ }
263
+
264
+ [data-scope='input-group'][data-part='root']:focus-within {
265
+ outline-color: Highlight;
266
+ }
267
+
268
+ [data-scope='input-group'][data-part='root']:focus-within::before,
269
+ [data-scope='input-group'][data-part='root']:has([data-invalid], [aria-invalid='true'])::before {
270
+ border-color: Highlight;
271
+ }
272
+
273
+ [data-scope='input-group'][data-part='item'] {
274
+ color: CanvasText;
275
+ }
276
+ }
277
+ }
@@ -1,3 +1,10 @@
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
+ /* 定义 json viewer 样式。 */
7
+
1
8
  @layer xihan.components {
2
9
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
10
  [data-scope='json-viewer'] {
@@ -7,24 +14,15 @@
7
14
  /* 缺省态(md 档)。等宽字体让层级缩进与值的长度对得齐。
8
15
  逐类型配色走 --xh-syntax-* 语法令牌,与代码块的记号色同源、随主题明暗切换。 */
9
16
  [data-scope='json-viewer'][data-part='root'] {
10
- /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
11
- --xh-icon-size: var(--xh-json-viewer-icon-size, var(--xh-glyph-size-text));
17
+ /* 作者放进空态格里的图标按档取尺,缺省 md,随 data-size 换档(§6.5)。
18
+ 查看器自绘的状态字形(分支行首的展开方向 chevron)不读这支:它是指示符,与所在的把手盒同走
19
+ --xh-control-indicator-* 一族——branch-trigger 在自己身上把 --xh-icon-size 改接到盒的尺 */
20
+ --xh-icon-size: var(--xh-json-viewer-icon-size, var(--xh-glyph-size-md));
12
21
 
13
22
  --xh-_json-viewer-font-size: var(--xh-control-caption-md);
14
23
  --xh-_json-viewer-row-py: var(--xh-space-0_5);
15
24
  --xh-_json-viewer-indent: var(--xh-space-4);
16
- --xh-json-viewer-key-fg: var(--xh-fg-brand-strong);
17
- --xh-json-viewer-punctuation-fg: var(--xh-fg-subtle);
18
- --xh-json-viewer-null-fg: var(--xh-fg-subtle);
19
- --xh-json-viewer-string-fg: var(--xh-syntax-string);
20
- --xh-json-viewer-number-fg: var(--xh-syntax-number);
21
- --xh-json-viewer-boolean-fg: var(--xh-syntax-keyword);
22
-
23
- /* 两档的自绘条浮在内容之上:轨道不上底色,实色轨道会沿容器边缘糊出一条灰带 */
24
- --xh-scrollbar-track-bg: transparent;
25
-
26
- /* 自绘条绝对定位贴这一层的内边距盒,它得是定位盒 */
27
- position: relative;
25
+
28
26
  display: block;
29
27
  color: var(--xh-json-viewer-fg, var(--xh-fg-default));
30
28
  font-family: var(--xh-json-viewer-font, var(--xh-font-family-mono));
@@ -37,20 +35,38 @@
37
35
  display: none;
38
36
  }
39
37
 
38
+ /* 面画在两档容器与空态格上,root 只是壳。三档按 §8.3 边界三选一:outline 即基础规则
39
+ (--xh-border-default 描边 + surface 底 + 无影);淡底档描边透明、底换 subtle;
40
+ 无框档边框留着但透明——去掉外框不会让行的位置跳一格 */
41
+ [data-scope='json-viewer'][data-part='root'][data-variant='subtle']
42
+ :is([data-scope='json-viewer'][data-part='tree'], [data-scope='json-viewer'][data-part='text'], [data-scope='json-viewer'][data-part='empty']) {
43
+ border-color: transparent;
44
+ background: var(--xh-json-viewer-bg, var(--xh-bg-subtle));
45
+ }
46
+
47
+ [data-scope='json-viewer'][data-part='root'][data-variant='ghost']
48
+ :is([data-scope='json-viewer'][data-part='tree'], [data-scope='json-viewer'][data-part='text'], [data-scope='json-viewer'][data-part='empty']) {
49
+ border-color: transparent;
50
+ background: transparent;
51
+ }
52
+
40
53
  /* 尺寸轴只换私有槽,部件从 root 继承 */
41
54
  [data-scope='json-viewer'][data-part='root'][data-size='sm'] {
55
+ --xh-icon-size: var(--xh-json-viewer-icon-size, var(--xh-glyph-size-sm));
42
56
  --xh-_json-viewer-font-size: var(--xh-control-caption-sm);
43
57
  --xh-_json-viewer-row-py: var(--xh-space-0);
44
58
  --xh-_json-viewer-indent: var(--xh-space-3);
45
59
  }
46
60
 
47
61
  [data-scope='json-viewer'][data-part='root'][data-size='lg'] {
62
+ --xh-icon-size: var(--xh-json-viewer-icon-size, var(--xh-glyph-size-lg));
48
63
  --xh-_json-viewer-font-size: var(--xh-control-caption-lg);
49
64
  --xh-_json-viewer-row-py: var(--xh-space-1);
50
65
  --xh-_json-viewer-indent: var(--xh-space-5);
51
66
  }
52
67
 
53
- /* 常驻容器不是浮层:两轴都可能溢出——深层缩进往行首方向推、长字符串往行尾伸 */
68
+ /* 常驻容器不是浮层:两轴都可能溢出——深层缩进往行首方向推、长字符串往行尾伸;
69
+ 页内结构容器走 reset 层的原生细条(§6.6),不写 overscroll */
54
70
  [data-scope='json-viewer'][data-part='tree'] {
55
71
  display: flex;
56
72
  flex-direction: column;
@@ -60,6 +76,7 @@
60
76
  border: var(--xh-stroke-thin) solid var(--xh-json-viewer-border, var(--xh-border-default));
61
77
  border-radius: var(--xh-json-viewer-radius, var(--xh-shape-surface));
62
78
  background: var(--xh-json-viewer-bg, var(--xh-bg-surface));
79
+ box-shadow: var(--xh-json-viewer-shadow, none);
63
80
  overflow: auto;
64
81
  }
65
82
 
@@ -73,6 +90,7 @@
73
90
  border: var(--xh-stroke-thin) solid var(--xh-json-viewer-border, var(--xh-border-default));
74
91
  border-radius: var(--xh-json-viewer-radius, var(--xh-shape-surface));
75
92
  background: var(--xh-json-viewer-bg, var(--xh-bg-surface));
93
+ box-shadow: var(--xh-json-viewer-shadow, none);
76
94
  color: var(--xh-json-viewer-text-fg, var(--xh-fg-default));
77
95
  font-family: var(--xh-json-viewer-font, var(--xh-font-family-mono));
78
96
  /* 缩进靠空格,行不折:折行会把层级读乱,横向给滚动条 */
@@ -85,12 +103,6 @@
85
103
  [data-scope='json-viewer'][data-part='text'][hidden] {
86
104
  display: none;
87
105
  }
88
-
89
- /* 这一档没有行可落焦,环画在整块上 */
90
- [data-scope='json-viewer'][data-part='text']:focus-visible {
91
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
92
- outline-offset: calc(-1 * var(--xh-ring-width));
93
- }
94
106
  [data-scope='json-viewer'][data-part='tree'][hidden] {
95
107
  display: none;
96
108
  }
@@ -108,8 +120,7 @@
108
120
 
109
121
  [data-scope='json-viewer'][data-part='branch']:focus-visible > [data-scope='json-viewer'][data-part='branch-control'] {
110
122
  outline: var(--xh-ring-width) solid var(--xh-ring-focus);
111
- /* 往内收:行是撑满宽度的,环画在外沿会被容器裁掉 */
112
- outline-offset: calc(-1 * var(--xh-ring-width));
123
+ outline-offset: var(--xh-ring-offset);
113
124
  }
114
125
 
115
126
  /* 行:标量与分支共用同一套盒子,两种行在视觉上必须齐平 */
@@ -143,17 +154,17 @@
143
154
  user-select: none;
144
155
  }
145
156
 
146
- /* 标量行的焦点环直接画在行上:它自己就是 treeitem */
147
- [data-scope='json-viewer'][data-part='item']:focus-visible {
148
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
149
- outline-offset: calc(-1 * var(--xh-ring-width));
157
+ /* 按下只换面不缩放:白底承载 hover 100 → pressed 200(§7.2 / §9.2 集合行不允许零反馈);
158
+ 指针按住由 :active 表出,Space / Enter 与触屏按住由 Headless 投影 data-pressed,三种输入同一副面 */
159
+ [data-scope='json-viewer'][data-part='branch-control']:not([data-disabled]):is(:active, [data-pressed]) {
160
+ background: var(--xh-json-viewer-row-bg-active, var(--xh-bg-subtle-hover));
150
161
  }
151
162
 
152
163
  /* 键名不参与压缩:它短且必须完整可读,被挤掉的应该是值 */
153
164
  [data-scope='json-viewer'][data-part='item-key'],
154
165
  [data-scope='json-viewer'][data-part='branch-text'] {
155
166
  flex: none;
156
- color: var(--xh-json-viewer-key-fg);
167
+ color: var(--xh-json-viewer-key-fg, var(--xh-fg-brand-strong));
157
168
  font-weight: var(--xh-json-viewer-key-font-weight, var(--xh-font-weight-medium));
158
169
  }
159
170
 
@@ -161,7 +172,7 @@
161
172
  [data-scope='json-viewer'][data-part='item-key']::after,
162
173
  [data-scope='json-viewer'][data-part='branch-text']::after {
163
174
  content: ':';
164
- color: var(--xh-json-viewer-punctuation-fg);
175
+ color: var(--xh-json-viewer-punctuation-fg, var(--xh-fg-subtle));
165
176
  }
166
177
 
167
178
  /* 弹性子项默认最小尺寸是内容尺寸,不归零则长字符串把行撑破、省略号轮不上 */
@@ -173,26 +184,26 @@
173
184
  text-overflow: ellipsis;
174
185
  }
175
186
 
176
- [data-scope='json-viewer'][data-part='item-value'][data-type='string'] {
177
- color: var(--xh-json-viewer-string-fg);
187
+ [data-scope='json-viewer'][data-part='item-value'][data-value-type='string'] {
188
+ color: var(--xh-json-viewer-string-fg, var(--xh-syntax-string));
178
189
  }
179
190
 
180
- [data-scope='json-viewer'][data-part='item-value'][data-type='number'] {
181
- color: var(--xh-json-viewer-number-fg);
191
+ [data-scope='json-viewer'][data-part='item-value'][data-value-type='number'] {
192
+ color: var(--xh-json-viewer-number-fg, var(--xh-syntax-number));
182
193
  }
183
194
 
184
- [data-scope='json-viewer'][data-part='item-value'][data-type='boolean'] {
185
- color: var(--xh-json-viewer-boolean-fg);
195
+ [data-scope='json-viewer'][data-part='item-value'][data-value-type='boolean'] {
196
+ color: var(--xh-json-viewer-boolean-fg, var(--xh-syntax-keyword));
186
197
  }
187
198
 
188
- [data-scope='json-viewer'][data-part='item-value'][data-type='null'] {
189
- color: var(--xh-json-viewer-null-fg);
199
+ [data-scope='json-viewer'][data-part='item-value'][data-value-type='null'] {
200
+ color: var(--xh-json-viewer-null-fg, var(--xh-fg-subtle));
190
201
  }
191
202
 
192
203
  /* 空对象与空数组只剩一对括号,跟标点同色 */
193
- [data-scope='json-viewer'][data-part='item-value'][data-type='array'],
194
- [data-scope='json-viewer'][data-part='item-value'][data-type='object'] {
195
- color: var(--xh-json-viewer-punctuation-fg);
204
+ [data-scope='json-viewer'][data-part='item-value'][data-value-type='array'],
205
+ [data-scope='json-viewer'][data-part='item-value'][data-value-type='object'] {
206
+ color: var(--xh-json-viewer-punctuation-fg, var(--xh-fg-subtle));
196
207
  }
197
208
 
198
209
  /* 循环引用与截断占位不是真数据,排在语气之外用弱化字重表达 */
@@ -202,8 +213,13 @@
202
213
  font-style: italic;
203
214
  }
204
215
 
205
- /* 展开箭头与勾选标记同宽,两种行的文字起点才对得齐 */
216
+ /* 展开箭头与勾选标记同宽,两种行的文字起点才对得齐。
217
+ 盒里的 chevron 是指示符(§6.5):与盒同边长,--xh-icon-size 在自己身上改接到盒的尺——
218
+ root 那支只管作者图标的 --xh-json-viewer-icon-size 不流进来,按图标档取的 20px 会比 16px 的盒还大;
219
+ branch-indicator 由适配器固定渲染在这颗把手里,继承即可 */
206
220
  [data-scope='json-viewer'][data-part='branch-trigger'] {
221
+ --xh-icon-size: var(--xh-json-viewer-indicator-size, var(--xh-control-indicator-size));
222
+
207
223
  display: inline-flex;
208
224
  flex: none;
209
225
  align-items: center;
@@ -219,19 +235,20 @@
219
235
  [data-scope='json-viewer'][data-part='branch-indicator'] {
220
236
  display: inline-block;
221
237
  line-height: var(--xh-leading-none);
222
- transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
238
+ transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter-strong);
223
239
  }
224
240
 
225
241
  [data-scope='json-viewer'][data-part='branch-indicator'][hidden] {
226
242
  display: none;
227
243
  }
228
244
 
229
- /* 没写内容就由皮肤画;作者往部件里塞了自己的图形,这条规则即不命中 */
245
+ /* 没写内容就由皮肤画;作者往部件里塞了自己的图形,这条规则即不命中。
246
+ 尺寸读把手改接过的 --xh-icon-size:chevron 与盒同边长(见 branch-trigger 块) */
230
247
  [data-scope='json-viewer'][data-part='branch-indicator']:empty::before {
231
248
  content: '';
232
249
  display: inline-block;
233
- inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
234
- block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
250
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
251
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
235
252
  vertical-align: var(--xh-glyph-baseline-shift);
236
253
  background-color: currentColor;
237
254
  -webkit-mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
@@ -271,4 +288,44 @@
271
288
  [data-scope='json-viewer'][data-part='branch-content'][hidden] {
272
289
  display: none;
273
290
  }
291
+
292
+ /* —— 空态 —— */
293
+ /* 一行也摊不出来时占位的那一格:与两档容器共用同一套边框与高度令牌,空与不空盒子不跳 */
294
+ [data-scope='json-viewer'][data-part='empty'] {
295
+ display: flex;
296
+ flex-direction: column;
297
+ align-items: center;
298
+ justify-content: center;
299
+ gap: var(--xh-json-viewer-empty-gap, var(--xh-space-2));
300
+ padding-block: var(--xh-json-viewer-empty-py, var(--xh-space-6));
301
+ padding-inline: var(--xh-json-viewer-empty-px, var(--xh-space-4));
302
+ border: var(--xh-stroke-thin) solid var(--xh-json-viewer-border, var(--xh-border-default));
303
+ border-radius: var(--xh-json-viewer-radius, var(--xh-shape-surface));
304
+ background: var(--xh-json-viewer-bg, var(--xh-bg-surface));
305
+ box-shadow: var(--xh-json-viewer-shadow, none);
306
+ color: var(--xh-json-viewer-empty-fg, var(--xh-fg-muted));
307
+ text-align: center;
308
+ }
309
+
310
+ [data-scope='json-viewer'][data-part='empty'][hidden] {
311
+ display: none;
312
+ }
313
+
314
+ /*
315
+ * 高对比档:分支行的按压面常态靠底色深一档表出,这一档里底色被统一换成 Canvas,
316
+ * 按住那一下与静息塌成同一个样子;用系统高亮反色画回来,行里的键名、摘要与箭头跟着换成反色前景
317
+ */
318
+ @media (forced-colors: active) {
319
+ [data-scope='json-viewer'][data-part='branch-control']:not([data-disabled]):is(:active, [data-pressed]) {
320
+ background: Highlight;
321
+ color: HighlightText;
322
+ }
323
+
324
+ [data-scope='json-viewer'][data-part='branch-control']:not([data-disabled]):is(:active, [data-pressed]) [data-scope='json-viewer'][data-part='branch-text'],
325
+ [data-scope='json-viewer'][data-part='branch-control']:not([data-disabled]):is(:active, [data-pressed]) [data-scope='json-viewer'][data-part='branch-text']::after,
326
+ [data-scope='json-viewer'][data-part='branch-control']:not([data-disabled]):is(:active, [data-pressed]) [data-scope='json-viewer'][data-part='preview'],
327
+ [data-scope='json-viewer'][data-part='branch-control']:not([data-disabled]):is(:active, [data-pressed]) [data-scope='json-viewer'][data-part='branch-indicator'] {
328
+ color: HighlightText;
329
+ }
330
+ }
274
331
  }