@xihan-ui/styles 1.0.0-alpha.2 → 1.0.0-alpha.3

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 (118) hide show
  1. package/CHANGELOG.md +447 -0
  2. package/README.md +1 -1
  3. package/css/accordion.css +56 -5
  4. package/css/alert.css +30 -5
  5. package/css/anchor.css +7 -7
  6. package/css/avatar-group.css +3 -3
  7. package/css/avatar.css +3 -3
  8. package/css/back-top.css +16 -1
  9. package/css/badge.css +16 -2
  10. package/css/breadcrumb.css +24 -4
  11. package/css/button-group.css +14 -13
  12. package/css/button.css +42 -11
  13. package/css/calendar.css +119 -2
  14. package/css/card.css +5 -5
  15. package/css/carousel.css +36 -5
  16. package/css/cascader.css +183 -102
  17. package/css/checkbox-group.css +41 -19
  18. package/css/checkbox.css +54 -8
  19. package/css/clipboard.css +2 -1
  20. package/css/code-block.css +12 -7
  21. package/css/collapsible.css +42 -2
  22. package/css/color-picker.css +111 -45
  23. package/css/combobox.css +114 -53
  24. package/css/composer.css +5 -3
  25. package/css/context-menu.css +97 -69
  26. package/css/date-field.css +91 -14
  27. package/css/date-picker.css +276 -65
  28. package/css/descriptions.css +2 -2
  29. package/css/dialog.css +40 -13
  30. package/css/download-trigger.css +76 -0
  31. package/css/drawer.css +45 -14
  32. package/css/dynamic-input.css +68 -17
  33. package/css/editable.css +12 -10
  34. package/css/ellipsis.css +1 -1
  35. package/css/empty-state.css +7 -7
  36. package/css/field.css +9 -6
  37. package/css/fieldset.css +69 -0
  38. package/css/file-upload.css +45 -7
  39. package/css/float-button.css +16 -1
  40. package/css/floating-panel.css +310 -0
  41. package/css/form.css +4 -4
  42. package/css/heatmap.css +410 -0
  43. package/css/highlight.css +1 -1
  44. package/css/hotkeys.css +90 -0
  45. package/css/hover-card.css +40 -45
  46. package/css/image-cropper.css +231 -0
  47. package/css/image-viewer.css +137 -12
  48. package/css/image.css +2 -2
  49. package/css/infinite-scroll.css +1 -1
  50. package/css/json-viewer.css +241 -0
  51. package/css/layout.css +6 -10
  52. package/css/list.css +8 -8
  53. package/css/listbox.css +20 -4
  54. package/css/loading-bar.css +7 -1
  55. package/css/log.css +1 -1
  56. package/css/marquee.css +9 -0
  57. package/css/masonry.css +63 -0
  58. package/css/mention.css +87 -33
  59. package/css/menu.css +113 -47
  60. package/css/menubar.css +146 -60
  61. package/css/navigation-menu.css +54 -29
  62. package/css/number-animation.css +3 -3
  63. package/css/number-field.css +173 -16
  64. package/css/overlay-arrow.css +51 -0
  65. package/css/page-header.css +5 -5
  66. package/css/pagination.css +13 -13
  67. package/css/password-input.css +356 -0
  68. package/css/pin-input.css +13 -10
  69. package/css/popconfirm.css +46 -10
  70. package/css/popover.css +70 -45
  71. package/css/popselect.css +183 -53
  72. package/css/progress.css +47 -7
  73. package/css/qr-code.css +1 -1
  74. package/css/radio-group.css +14 -8
  75. package/css/rating.css +2 -2
  76. package/css/reset.css +17 -0
  77. package/css/result.css +6 -6
  78. package/css/scroll-area.css +61 -89
  79. package/css/scrollbar.css +167 -0
  80. package/css/segmented.css +215 -0
  81. package/css/select.css +274 -125
  82. package/css/separator.css +2 -2
  83. package/css/side-nav.css +111 -19
  84. package/css/signature-pad.css +144 -0
  85. package/css/skeleton.css +7 -1
  86. package/css/slider.css +10 -10
  87. package/css/space.css +117 -0
  88. package/css/spinner.css +22 -10
  89. package/css/statistic.css +9 -9
  90. package/css/steps.css +25 -8
  91. package/css/switch.css +48 -6
  92. package/css/table.css +131 -37
  93. package/css/tabs.css +4 -4
  94. package/css/tag.css +177 -0
  95. package/css/tags-input.css +78 -40
  96. package/css/text-field.css +149 -36
  97. package/css/thread.css +1 -1
  98. package/css/time-field.css +97 -16
  99. package/css/time-picker.css +259 -69
  100. package/css/timeline.css +10 -10
  101. package/css/timer.css +119 -0
  102. package/css/toast.css +34 -10
  103. package/css/toaster.css +1 -1
  104. package/css/toggle.css +6 -6
  105. package/css/tone.css +10 -5
  106. package/css/toolbar.css +1 -1
  107. package/css/tooltip.css +36 -33
  108. package/css/tour.css +103 -58
  109. package/css/transfer.css +68 -17
  110. package/css/tree-select.css +185 -84
  111. package/css/tree.css +85 -1
  112. package/css/typography.css +4 -4
  113. package/css/undefined.css +11 -1
  114. package/css/virtualizer.css +3 -1
  115. package/css/watermark.css +1 -2
  116. package/index.css +17 -0
  117. package/index.unlayered.css +8659 -2799
  118. package/package.json +19 -3
@@ -7,6 +7,9 @@
7
7
  /* 组容器:label、trigger、条目都是它的直接子节点,排布方式只能读 data-orientation——
8
8
  连接层明说了 role=group 不接受 aria-orientation,那个属性它根本没输出 */
9
9
  [data-scope='checkbox-group'][data-part='root'] {
10
+ /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
11
+ --xh-icon-size: var(--xh-checkbox-group-icon-size, var(--xh-glyph-size-text));
12
+
10
13
  display: flex;
11
14
  gap: var(--xh-checkbox-group-gap, var(--xh-stack-gap-md));
12
15
  }
@@ -62,7 +65,6 @@
62
65
  [data-scope='checkbox-group'][data-part='item'][data-disabled] {
63
66
  color: var(--xh-checkbox-group-item-fg-disabled, var(--xh-fg-disabled));
64
67
  cursor: not-allowed;
65
- opacity: var(--xh-state-disabled-opacity);
66
68
  }
67
69
 
68
70
  /* 只读整组都改不动,但不是禁用:颜色照常,只把"可点"这个暗示收回来。
@@ -81,13 +83,13 @@
81
83
  flex: none;
82
84
  align-items: center;
83
85
  justify-content: center;
84
- inline-size: var(--xh-checkbox-group-indicator-size, 16px);
85
- block-size: var(--xh-checkbox-group-indicator-size, 16px);
86
+ inline-size: var(--xh-checkbox-group-indicator-size, var(--xh-control-indicator-size));
87
+ block-size: var(--xh-checkbox-group-indicator-size, var(--xh-control-indicator-size));
86
88
  border: var(--xh-stroke-thin) solid var(--xh-checkbox-group-indicator-border, var(--xh-border-control));
87
- border-radius: var(--xh-checkbox-group-indicator-radius, var(--xh-radius-sm));
89
+ border-radius: var(--xh-checkbox-group-indicator-radius, var(--xh-shape-inset));
88
90
  background: var(--xh-checkbox-group-indicator-bg, var(--xh-bg-canvas));
89
91
  color: var(--xh-checkbox-group-indicator-fg, var(--xh-fg-on-brand));
90
- font-size: var(--xh-checkbox-group-indicator-font-size, 12px);
92
+ font-size: var(--xh-checkbox-group-indicator-font-size, calc(var(--xh-control-indicator-size) * 0.75));
91
93
  line-height: var(--xh-leading-none);
92
94
  transition:
93
95
  background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
@@ -107,32 +109,53 @@
107
109
  border-color: var(--xh-checkbox-group-indicator-border-hover, var(--xh-border-control-hover));
108
110
  }
109
111
 
110
- /* 选中即填色:条目是二态,全选格三态里 allsome 都算"有值",共用同一副填色 */
112
+ /* 选中即填色:条目是二态,全选格三态里 checkedindeterminate 都算"有值",共用同一副填色 */
111
113
  [data-scope='checkbox-group'][data-part='indicator'][data-state='checked'],
112
- [data-scope='checkbox-group'][data-part='trigger']:is([data-state='all'], [data-state='some'])::before {
114
+ [data-scope='checkbox-group'][data-part='trigger']:is([data-state='checked'], [data-state='indeterminate'])::before {
113
115
  border-color: var(--xh-checkbox-group-indicator-border-checked, var(--xh-bg-brand));
114
116
  background: var(--xh-checkbox-group-indicator-bg-checked, var(--xh-bg-brand));
115
117
  }
116
118
 
117
119
  /* 勾号由皮肤补,作者不必自己往 indicator 里塞字符 */
118
- [data-scope='checkbox-group'][data-part='indicator'][data-state='checked']::before {
119
- content: '';
120
- }
121
-
122
- /* 全选格的三态:all 打勾、some 打横杠、none 留空。
123
- some 就是读屏听到的 aria-checked=mixed,两者同源,样式只认 data-state */
124
- [data-scope='checkbox-group'][data-part='trigger'][data-state='all']::before {
125
- content: '✓';
120
+ [data-scope='checkbox-group'][data-part='indicator'][data-state='checked']:empty::before {
121
+ content: '';
122
+ display: inline-block;
123
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
124
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
125
+ vertical-align: var(--xh-glyph-baseline-shift);
126
+ background-color: currentColor;
127
+ -webkit-mask: var(--xh-glyph-mark-check) center / contain no-repeat;
128
+ mask: var(--xh-glyph-mark-check) center / contain no-repeat;
129
+ }
130
+
131
+ /* 全选格的三态:checked 打勾、indeterminate 打横杠、unchecked 留空。
132
+ indeterminate 就是读屏听到的 aria-checked=mixed,两者同源,样式只认 data-state */
133
+ [data-scope='checkbox-group'][data-part='trigger'][data-state='checked']::before {
134
+ content: '';
135
+ display: inline-block;
136
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
137
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
138
+ vertical-align: var(--xh-glyph-baseline-shift);
139
+ background-color: currentColor;
140
+ -webkit-mask: var(--xh-glyph-mark-check) center / contain no-repeat;
141
+ mask: var(--xh-glyph-mark-check) center / contain no-repeat;
126
142
  }
127
143
 
128
- [data-scope='checkbox-group'][data-part='trigger'][data-state='some']::before {
129
- content: '';
144
+ [data-scope='checkbox-group'][data-part='trigger'][data-state='indeterminate']::before {
145
+ content: '';
146
+ display: inline-block;
147
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
148
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
149
+ vertical-align: var(--xh-glyph-baseline-shift);
150
+ background-color: currentColor;
151
+ -webkit-mask: var(--xh-glyph-mark-minus) center / contain no-repeat;
152
+ mask: var(--xh-glyph-mark-minus) center / contain no-repeat;
130
153
  }
131
154
 
132
155
  /* 校验失败标在每个方框上:root 上的 data-invalid 是唯一出处,条目自己不带这个属性 */
133
156
  [data-scope='checkbox-group'][data-part='root'][data-invalid]
134
157
  [data-scope='checkbox-group'][data-part='indicator'] {
135
- border-color: var(--xh-checkbox-group-indicator-border-invalid, var(--xh-color-danger-500));
158
+ border-color: var(--xh-checkbox-group-indicator-border-invalid, var(--xh-border-invalid));
136
159
  }
137
160
 
138
161
  [data-scope='checkbox-group'][data-part='item-text'] {
@@ -163,7 +186,6 @@
163
186
  [data-scope='checkbox-group'][data-part='trigger'][data-disabled] {
164
187
  color: var(--xh-checkbox-group-trigger-fg-disabled, var(--xh-fg-disabled));
165
188
  cursor: not-allowed;
166
- opacity: var(--xh-state-disabled-opacity);
167
189
  }
168
190
 
169
191
  /* 只读与禁用会同时出现在 trigger 上(连接层两个属性各写各的),
package/css/checkbox.css CHANGED
@@ -5,9 +5,12 @@
5
5
  }
6
6
 
7
7
  [data-scope='checkbox'][data-part='root'] {
8
+ /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
9
+ --xh-icon-size: var(--xh-checkbox-icon-size, var(--xh-glyph-size-text));
10
+
8
11
  /* 尺寸档只换方框边长这一个私有槽,勾的字号按边长的比例跟着走;
9
12
  指示器从 root 继承这两个槽,换档不必给每个部件各写一条选择器 */
10
- --xh-_checkbox-box: var(--xh-space-4);
13
+ --xh-_checkbox-box: var(--xh-control-indicator-size);
11
14
  --xh-_checkbox-glyph: calc(var(--xh-_checkbox-box) * 0.75);
12
15
 
13
16
  /* 选中态的底与描边随语气走。没写 data-tone 时 --xh-_tone 没被声明,退回原来的品牌色 */
@@ -23,8 +26,8 @@
23
26
  inline-size: var(--xh-_checkbox-box);
24
27
  block-size: var(--xh-_checkbox-box);
25
28
  padding: 0;
26
- border: 1px solid var(--xh-checkbox-border, var(--xh-border-control));
27
- border-radius: var(--xh-radius-sm);
29
+ border: var(--xh-stroke-thin) solid var(--xh-checkbox-border, var(--xh-border-control));
30
+ border-radius: var(--xh-shape-inset);
28
31
  background: var(--xh-checkbox-bg, var(--xh-bg-canvas));
29
32
  color: var(--xh-checkbox-fg, var(--xh-_checkbox-on-accent));
30
33
  cursor: pointer;
@@ -41,6 +44,20 @@
41
44
  background: var(--xh-checkbox-bg-checked, var(--xh-_checkbox-accent));
42
45
  }
43
46
 
47
+ /* 校验失败:方框描边转警示色。未勾时只有描边,勾上后填充仍走语气色,
48
+ 两者不打架——描边压在 checked 规则之后,选择器同特指度靠书写顺序取胜 */
49
+ [data-scope='checkbox'][data-part='root'][data-invalid],
50
+ [data-scope='checkbox'][data-part='root'][data-state='checked'][data-invalid],
51
+ [data-scope='checkbox'][data-part='root'][data-state='indeterminate'][data-invalid] {
52
+ border-color: var(--xh-checkbox-border-invalid, var(--xh-border-invalid));
53
+ }
54
+
55
+ /* 文字随方框一起转警示色:给了默认插槽时外层是 <label>,方框自己的描边够不到那段文字 */
56
+ [data-scope='checkbox'][data-part='label'][data-invalid],
57
+ [data-scope='checkbox'][data-part='text'][data-invalid] {
58
+ color: var(--xh-checkbox-fg-invalid, var(--xh-fg-danger));
59
+ }
60
+
44
61
  [data-scope='checkbox'][data-part='root'][data-disabled] {
45
62
  cursor: not-allowed;
46
63
  opacity: var(--xh-state-disabled-opacity);
@@ -54,11 +71,11 @@
54
71
  /* —— 尺寸 —— */
55
72
  /* 缺省档写在上面的基础规则里 */
56
73
  [data-scope='checkbox'][data-part='root'][data-size='sm'] {
57
- --xh-_checkbox-box: var(--xh-space-3);
74
+ --xh-_checkbox-box: calc(var(--xh-control-indicator-size) - var(--xh-space-1));
58
75
  }
59
76
 
60
77
  [data-scope='checkbox'][data-part='root'][data-size='lg'] {
61
- --xh-_checkbox-box: var(--xh-space-5);
78
+ --xh-_checkbox-box: calc(var(--xh-control-indicator-size) + var(--xh-space-1));
62
79
  }
63
80
 
64
81
  [data-scope='checkbox'][data-part='indicator'] {
@@ -71,8 +88,16 @@
71
88
  font-size: var(--xh-_checkbox-glyph);
72
89
  }
73
90
 
74
- [data-scope='checkbox'][data-part='indicator']::before {
75
- content: '✓';
91
+ /* 没写内容就由皮肤画;作者往部件里塞了自己的图形,这条规则即不命中 */
92
+ [data-scope='checkbox'][data-part='indicator']:empty::before {
93
+ content: '';
94
+ display: inline-block;
95
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
96
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
97
+ vertical-align: var(--xh-glyph-baseline-shift);
98
+ background-color: currentColor;
99
+ -webkit-mask: var(--xh-glyph-mark-check) center / contain no-repeat;
100
+ mask: var(--xh-glyph-mark-check) center / contain no-repeat;
76
101
  }
77
102
 
78
103
  [data-scope='checkbox'][data-part='indicator'][data-state='unchecked'] {
@@ -89,8 +114,29 @@
89
114
  position: absolute;
90
115
  inline-size: 60%;
91
116
  block-size: var(--xh-stroke-thick);
92
- border-radius: var(--xh-radius-full);
117
+ border-radius: var(--xh-shape-pill);
93
118
  background: var(--xh-checkbox-indicator-fg, var(--xh-_checkbox-on-accent));
94
119
  content: '';
95
120
  }
121
+
122
+ /* —— 带文字的整行:<label> 包住控件与文字,与 checkbox-group 的条目同一副样子 —— */
123
+ [data-scope='checkbox'][data-part='label'] {
124
+ display: inline-flex;
125
+ align-items: center;
126
+ gap: var(--xh-checkbox-label-gap, var(--xh-control-gap-md));
127
+ color: var(--xh-checkbox-label-fg, var(--xh-fg-default));
128
+ font-size: var(--xh-checkbox-label-font-size, var(--xh-text-label-size));
129
+ line-height: var(--xh-leading-none);
130
+ cursor: pointer;
131
+ }
132
+
133
+ [data-scope='checkbox'][data-part='label'][data-disabled] {
134
+ color: var(--xh-checkbox-label-fg-disabled, var(--xh-fg-disabled));
135
+ cursor: not-allowed;
136
+ }
137
+
138
+ [data-scope='checkbox'][data-part='text'] {
139
+ color: inherit;
140
+ user-select: none;
141
+ }
96
142
  }
package/css/clipboard.css CHANGED
@@ -39,7 +39,7 @@
39
39
  }
40
40
 
41
41
  [data-scope='clipboard'][data-part='input'] {
42
- min-inline-size: var(--xh-clipboard-input-min-w, 12rem);
42
+ min-inline-size: var(--xh-clipboard-input-min-w, var(--xh-control-min-w));
43
43
  block-size: var(--xh-clipboard-input-h, var(--xh-control-h-md));
44
44
  padding-inline: var(--xh-clipboard-input-px, var(--xh-control-px-md));
45
45
  padding-block: 0;
@@ -58,6 +58,7 @@
58
58
  }
59
59
 
60
60
  [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)));
61
62
  outline: var(--xh-ring-width) solid var(--xh-ring-focus);
62
63
  outline-offset: var(--xh-ring-offset);
63
64
  }
@@ -50,15 +50,20 @@
50
50
  white-space: pre;
51
51
  }
52
52
 
53
- /* 语法配色。令牌集里没有语法色这一类,所以就地声明成可覆盖的变量:
54
- 注释与标点复用既有语义色,只有三种真需要色相的走 light-dark()。
55
- 浏览器不认 light-dark() 时这几条整体失效,退化成单色代码,读起来照常。 */
53
+ /* 开了换行:pre-wrap 仍保留缩进与连续空白,只是允许行尾折下来;
54
+ anywhere 让没有空格的长串(url、base64)也能断,否则照样撑出横向滚动 */
55
+ [data-scope='code-block'][data-part='code'][data-wrap] {
56
+ white-space: pre-wrap;
57
+ overflow-wrap: anywhere;
58
+ }
59
+
60
+ /* 语法配色:注释与标点复用既有语义色,三种真需要色相的走 --xh-syntax-* 语法令牌(随主题明暗切换)。 */
56
61
  [data-scope='code-block'][data-part='root'] {
57
62
  --xh-code-block-comment-fg: var(--xh-fg-muted);
58
63
  --xh-code-block-punctuation-fg: var(--xh-fg-subtle);
59
- --xh-code-block-string-fg: light-dark(#0a7f5f, #7ee2b8);
60
- --xh-code-block-number-fg: light-dark(#9a5b00, #f0b866);
61
- --xh-code-block-keyword-fg: light-dark(#8250df, #d2a8ff);
64
+ --xh-code-block-string-fg: var(--xh-syntax-string);
65
+ --xh-code-block-number-fg: var(--xh-syntax-number);
66
+ --xh-code-block-keyword-fg: var(--xh-syntax-keyword);
62
67
  }
63
68
 
64
69
  /* 记号本身不带任何布局,只上色:它是 <code> 里的行内片段,多一点盒模型都会破坏对齐 */
@@ -77,7 +82,7 @@
77
82
 
78
83
  [data-scope='code-block'][data-part='token'][data-kind='keyword'] {
79
84
  color: var(--xh-code-block-keyword-fg);
80
- font-weight: var(--xh-code-block-keyword-weight, 600);
85
+ font-weight: var(--xh-code-block-keyword-weight, var(--xh-font-weight-semibold));
81
86
  }
82
87
 
83
88
  [data-scope='code-block'][data-part='token'][data-kind='punctuation'] {
@@ -19,14 +19,14 @@
19
19
  --xh-_collapsible-trigger-h: var(--xh-control-h-sm);
20
20
  --xh-_collapsible-trigger-px: var(--xh-control-px-sm);
21
21
  --xh-_collapsible-trigger-gap: var(--xh-control-gap-sm);
22
- --xh-_collapsible-trigger-font-size: var(--xh-font-size-sm);
22
+ --xh-_collapsible-trigger-font-size: var(--xh-control-font-sm);
23
23
  }
24
24
 
25
25
  [data-scope='collapsible'][data-part='root'][data-size='lg'] {
26
26
  --xh-_collapsible-trigger-h: var(--xh-control-h-lg);
27
27
  --xh-_collapsible-trigger-px: var(--xh-control-px-lg);
28
28
  --xh-_collapsible-trigger-gap: var(--xh-control-gap-lg);
29
- --xh-_collapsible-trigger-font-size: var(--xh-font-size-lg);
29
+ --xh-_collapsible-trigger-font-size: var(--xh-control-font-lg);
30
30
  }
31
31
 
32
32
  [data-scope='collapsible'][data-part='trigger'] {
@@ -61,8 +61,48 @@
61
61
  opacity: var(--xh-state-disabled-opacity);
62
62
  }
63
63
 
64
+ /* 这里刻意没有 [data-part='content'][hidden] { display: none }:补了动画一帧都播不出来。
65
+ 收起由适配器在退场结束后写内联 display 落成,退场窗口内靠 connect 发的 inert 挡住焦点 */
64
66
  [data-scope='collapsible'][data-part='content'] {
67
+ display: grid;
68
+ grid-template-rows: 1fr;
69
+ overflow: clip;
65
70
  padding-block: var(--xh-collapsible-content-py, var(--xh-stack-gap-md));
66
71
  color: var(--xh-collapsible-content-fg, var(--xh-fg-default));
67
72
  }
73
+
74
+ [data-scope='collapsible'][data-part='content'][data-state='open'] {
75
+ animation: xh-collapsible-expand var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
76
+ }
77
+
78
+ [data-scope='collapsible'][data-part='content'][data-state='closed'] {
79
+ animation: xh-collapsible-collapse var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
80
+ }
81
+ }
82
+
83
+ @layer xihan.motion {
84
+ /* 行高与内缩同帧动:内缩不归零的话,border-box 会把盒子钳在上下内缩之和上,收不干净 */
85
+ @keyframes xh-collapsible-expand {
86
+ from {
87
+ grid-template-rows: 0fr;
88
+ padding-block: 0;
89
+ }
90
+
91
+ to {
92
+ grid-template-rows: 1fr;
93
+ padding-block: var(--xh-collapsible-content-py, var(--xh-stack-gap-md));
94
+ }
95
+ }
96
+
97
+ @keyframes xh-collapsible-collapse {
98
+ from {
99
+ grid-template-rows: 1fr;
100
+ padding-block: var(--xh-collapsible-content-py, var(--xh-stack-gap-md));
101
+ }
102
+
103
+ to {
104
+ grid-template-rows: 0fr;
105
+ padding-block: 0;
106
+ }
107
+ }
68
108
  }
@@ -1,5 +1,5 @@
1
1
  @layer xihan.motion {
2
- @keyframes xh-color-picker-fade-in {
2
+ @keyframes xh-pop-in {
3
3
  from {
4
4
  opacity: 0;
5
5
  scale: var(--xh-motion-scale-enter);
@@ -10,6 +10,17 @@
10
10
  scale: 1;
11
11
  }
12
12
  }
13
+ @keyframes xh-pop-out {
14
+ from {
15
+ opacity: 1;
16
+ scale: 1;
17
+ }
18
+
19
+ to {
20
+ opacity: 0;
21
+ scale: var(--xh-motion-scale-enter);
22
+ }
23
+ }
13
24
  }
14
25
 
15
26
  @layer xihan.components {
@@ -23,15 +34,25 @@
23
34
  全都由连接层每帧写进内联样式。这份皮肤一个都不碰:内联优先级高于样式表,
24
35
  写了不但是死声明,还会跟连接层抢位置。皮肤只管尺寸、颜色、圆角与状态表现。 */
25
36
 
37
+ /* 标签一行、盒一行的两行堆叠 */
26
38
  [data-scope='color-picker'][data-part='root'] {
39
+ /* 盒的度量与标签字号走私有槽中转,与同族的下拉选择一处对一处 */
40
+ --xh-_color-picker-h: var(--xh-control-h-md);
41
+ --xh-_color-picker-px: var(--xh-control-px-md);
42
+ --xh-_color-picker-gap: var(--xh-control-gap-md);
43
+ --xh-_color-picker-label-font-size: var(--xh-text-label-size);
44
+
45
+ /* 聚焦环恒用固定环色 */
46
+ --xh-_color-picker-ring: var(--xh-ring-focus);
47
+
27
48
  display: inline-flex;
28
49
  flex-direction: column;
29
- gap: var(--xh-color-picker-gap, var(--xh-stack-gap-md));
50
+ gap: var(--xh-color-picker-gap, var(--xh-space-1));
30
51
  }
31
52
 
32
53
  [data-scope='color-picker'][data-part='label'] {
33
- color: var(--xh-color-picker-label-fg, var(--xh-fg-muted));
34
- font-size: var(--xh-color-picker-label-font-size, var(--xh-text-label-size));
54
+ color: var(--xh-color-picker-label-fg, var(--xh-fg-default));
55
+ font-size: var(--xh-color-picker-label-font-size, var(--xh-_color-picker-label-font-size));
35
56
  font-weight: var(--xh-color-picker-label-font-weight, var(--xh-text-label-weight));
36
57
  line-height: var(--xh-leading-none);
37
58
  }
@@ -40,44 +61,74 @@
40
61
  color: var(--xh-fg-subtle);
41
62
  }
42
63
 
43
- /* 画成一个框的是 trigger:里面的色块与值文本要看起来是一体的 */
44
- [data-scope='color-picker'][data-part='trigger'] {
64
+ /* 盒:描边、底色、控件高度与行内内衬都落在这一层 */
65
+ [data-scope='color-picker'][data-part='control'] {
45
66
  display: inline-flex;
46
67
  align-items: center;
47
- gap: var(--xh-color-picker-trigger-gap, var(--xh-control-gap-md));
48
- min-inline-size: var(--xh-color-picker-trigger-min-w, 9rem);
49
- block-size: var(--xh-color-picker-trigger-h, var(--xh-control-h-md));
50
- padding-inline: var(--xh-color-picker-trigger-px, var(--xh-control-px-md));
51
- border: var(--xh-stroke-thin) solid var(--xh-color-picker-trigger-border, var(--xh-border-control));
52
- border-radius: var(--xh-color-picker-trigger-radius, var(--xh-shape-control));
53
- background: var(--xh-color-picker-trigger-bg, var(--xh-bg-canvas));
54
- color: var(--xh-color-picker-trigger-fg, var(--xh-fg-default));
55
- font-family: inherit;
56
- font-size: var(--xh-color-picker-trigger-font-size, var(--xh-text-body-size));
57
- line-height: var(--xh-leading-none);
68
+ gap: var(--xh-color-picker-control-gap, var(--xh-_color-picker-gap));
69
+ min-inline-size: var(--xh-color-picker-control-min-w, var(--xh-control-min-w));
70
+ block-size: var(--xh-color-picker-control-h, var(--xh-_color-picker-h));
71
+ padding-inline: var(--xh-color-picker-control-px, var(--xh-_color-picker-px));
72
+ border: var(--xh-stroke-thin) solid var(--xh-color-picker-control-border, var(--xh-border-control));
73
+ border-radius: var(--xh-color-picker-control-radius, var(--xh-shape-control));
74
+ background: var(--xh-color-picker-control-bg, var(--xh-bg-canvas));
75
+ color: var(--xh-color-picker-control-fg, var(--xh-fg-default));
58
76
  cursor: pointer;
59
77
  transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
60
78
  }
61
79
 
62
- [data-scope='color-picker'][data-part='trigger']:hover:not(:disabled) {
63
- border-color: var(--xh-color-picker-trigger-border-hover, var(--xh-border-control-hover));
80
+ [data-scope='color-picker'][data-part='control'][hidden] {
81
+ display: none;
64
82
  }
65
83
 
66
- [data-scope='color-picker'][data-part='trigger']:focus-visible {
67
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
84
+ [data-scope='color-picker'][data-part='control']:hover:not([data-disabled]) {
85
+ border-color: var(--xh-color-picker-control-border-hover, var(--xh-border-control-hover));
86
+ }
87
+
88
+ /* Tab 停靠点是盒里的 trigger,环画在盒上 */
89
+ [data-scope='color-picker'][data-part='control']:focus-within:not([data-disabled]) {
90
+ border-color: var(--xh-color-picker-control-border-focus, var(--xh-_tone, var(--xh-border-control-focus)));
91
+ outline: var(--xh-ring-width) solid var(--xh-_color-picker-ring);
68
92
  outline-offset: var(--xh-ring-offset);
69
93
  }
70
94
 
71
- /* 单体控件用原生 disabled(预设色板那些格子也是原生按钮,同一条规矩) */
72
- [data-scope='color-picker'][data-part='trigger']:disabled {
73
- background: var(--xh-color-picker-trigger-bg-disabled, var(--xh-bg-subtle));
95
+ [data-scope='color-picker'][data-part='control'][data-disabled] {
96
+ background: var(--xh-color-picker-control-bg-disabled, var(--xh-bg-subtle));
74
97
  color: var(--xh-fg-disabled);
75
98
  cursor: not-allowed;
76
99
  }
77
100
 
78
101
  /* 只读仍可聚焦、浮层照开,只是改不动:不置灰,只把底色压下去 */
79
- [data-scope='color-picker'][data-part='trigger'][data-readonly] {
80
- background: var(--xh-color-picker-trigger-bg-readonly, var(--xh-bg-subtle));
102
+ [data-scope='color-picker'][data-part='control'][data-readonly] {
103
+ background: var(--xh-color-picker-control-bg-readonly, var(--xh-bg-subtle));
104
+ }
105
+
106
+ /* 盒内那颗可聚焦的按钮:不画描边不铺底也不自带高度,只占满剩余宽度、排布色块与值文本 */
107
+ [data-scope='color-picker'][data-part='trigger'] {
108
+ display: inline-flex;
109
+ flex: 1 1 auto;
110
+ align-items: center;
111
+ gap: var(--xh-color-picker-trigger-gap, var(--xh-_color-picker-gap));
112
+ min-inline-size: 0;
113
+ padding: 0;
114
+ border: 0;
115
+ background: transparent;
116
+ color: var(--xh-color-picker-trigger-fg, var(--xh-fg-default));
117
+ font-family: inherit;
118
+ font-size: var(--xh-color-picker-trigger-font-size, var(--xh-text-body-size));
119
+ line-height: var(--xh-leading-none);
120
+ text-align: start;
121
+ cursor: pointer;
122
+ }
123
+
124
+ [data-scope='color-picker'][data-part='trigger'][hidden] {
125
+ display: none;
126
+ }
127
+
128
+ /* 单体控件用原生 disabled(预设色板那些格子也是原生按钮,同一条规矩) */
129
+ [data-scope='color-picker'][data-part='trigger']:disabled {
130
+ color: var(--xh-fg-disabled);
131
+ cursor: not-allowed;
81
132
  }
82
133
 
83
134
  /* 当前颜色的色块。背景由连接层内联给(含透明度),这里只给形状与描边。
@@ -86,15 +137,17 @@
86
137
  [data-scope='color-picker'][data-part='swatch'] {
87
138
  display: inline-block;
88
139
  flex: none;
89
- inline-size: var(--xh-color-picker-swatch-size, 1.125rem);
90
- block-size: var(--xh-color-picker-swatch-size, 1.125rem);
140
+ inline-size: var(--xh-color-picker-swatch-size, 18px);
141
+ block-size: var(--xh-color-picker-swatch-size, 18px);
91
142
  border: var(--xh-stroke-thin) solid var(--xh-color-picker-swatch-border, var(--xh-border-default));
92
- border-radius: var(--xh-color-picker-swatch-radius, var(--xh-radius-sm));
143
+ border-radius: var(--xh-color-picker-swatch-radius, var(--xh-shape-inset));
93
144
  }
94
145
 
146
+ /* 值串可能很长,必须能截断;框宽由盒定,这里得允许自己被压缩 */
95
147
  [data-scope='color-picker'][data-part='value-text'] {
96
148
  overflow: hidden;
97
- flex: 1;
149
+ flex: 1 1 auto;
150
+ min-inline-size: 0;
98
151
  color: var(--xh-color-picker-value-fg, var(--xh-fg-default));
99
152
  font-size: var(--xh-color-picker-value-font-size, var(--xh-text-body-size));
100
153
  text-align: start;
@@ -104,8 +157,12 @@
104
157
 
105
158
  /* 坐标由定位引擎写进 style(inset-inline-start / inset-block-start),这里只定位模式与层级 */
106
159
  [data-scope='color-picker'][data-part='positioner'] {
160
+ /* 落定那一侧的可用高度,由定位引擎经内联自定义属性写进来,内容层继承它限高;
161
+ 引擎没算(尚未落位 / 无引擎 / 贴边归零)时不额外限高 */
162
+ --xh-_color-picker-available-h: 100vh;
163
+
107
164
  position: fixed;
108
- z-index: var(--xh-layer-popover);
165
+ z-index: var(--xh-color-picker-layer, var(--xh-layer-popover));
109
166
  /* 空壳不吃指针,交互只发生在 content 上 */
110
167
  pointer-events: none;
111
168
  }
@@ -119,18 +176,27 @@
119
176
  display: flex;
120
177
  flex-direction: column;
121
178
  gap: var(--xh-color-picker-content-gap, var(--xh-space-3));
122
- inline-size: var(--xh-color-picker-content-w, 15rem);
123
- padding: var(--xh-color-picker-content-p, var(--xh-space-3));
179
+ inline-size: var(--xh-color-picker-content-w, var(--xh-overlay-min-w));
180
+ padding-block: var(--xh-color-picker-content-py, var(--xh-space-3));
181
+ padding-inline: var(--xh-color-picker-content-px, var(--xh-space-3));
182
+ max-block-size: min(var(--xh-color-picker-max-h, var(--xh-viewport-h-md)), var(--xh-_color-picker-available-h));
183
+ overflow-y: auto;
124
184
  border: var(--xh-stroke-thin) solid var(--xh-color-picker-content-border, var(--xh-border-default));
125
185
  border-radius: var(--xh-color-picker-content-radius, var(--xh-shape-surface));
126
186
  background: var(--xh-color-picker-content-bg, var(--xh-bg-surface));
127
187
  color: var(--xh-color-picker-content-fg, var(--xh-fg-default));
128
- box-shadow: var(--xh-color-picker-content-shadow, var(--xh-elevation-2));
188
+ box-shadow: var(--xh-color-picker-content-shadow, var(--xh-elevation-floating));
129
189
  pointer-events: auto;
130
190
  }
131
191
 
132
192
  [data-scope='color-picker'][data-part='content'][data-state='open'] {
133
- animation: xh-color-picker-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
193
+ animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
194
+ }
195
+
196
+ /* 退场:收起那一帧 hidden 由退场闸门押后,这里才播得出来。forwards 把末态钉住,
197
+ 免得动画结束到真收起之间闪一下原样 */
198
+ [data-scope='color-picker'][data-part='content'][data-state='closed'] {
199
+ animation: xh-pop-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
134
200
  }
135
201
 
136
202
  /* 浮层本身只是个容器(tabindex=-1 是焦点域的兜底落点),落焦时不画环 */
@@ -147,7 +213,7 @@
147
213
  position: relative;
148
214
  display: block;
149
215
  block-size: var(--xh-color-picker-area-h, 9rem);
150
- border-radius: var(--xh-color-picker-area-radius, var(--xh-radius-md));
216
+ border-radius: var(--xh-color-picker-area-radius, var(--xh-shape-control));
151
217
  cursor: crosshair;
152
218
  }
153
219
 
@@ -194,7 +260,7 @@
194
260
  border: var(--xh-stroke-thick) solid var(--xh-color-picker-thumb-border, var(--xh-color-neutral-0));
195
261
  border-radius: var(--xh-color-picker-thumb-radius, var(--xh-shape-pill));
196
262
  background: transparent;
197
- box-shadow: var(--xh-color-picker-thumb-shadow, var(--xh-elevation-1));
263
+ box-shadow: var(--xh-color-picker-thumb-shadow, var(--xh-elevation-raised));
198
264
  cursor: grab;
199
265
  transition: scale var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
200
266
  }
@@ -220,7 +286,7 @@
220
286
  放大用 scale 而不是改尺寸:变换绕自身中心发生,圆心不动,负外边距的算式不用跟着改 */
221
287
  [data-scope='color-picker'][data-part='area-thumb'][data-dragging],
222
288
  [data-scope='color-picker'][data-part='channel-slider-thumb'][data-dragging] {
223
- scale: var(--xh-color-picker-thumb-scale-dragging, 1.15);
289
+ scale: var(--xh-color-picker-thumb-scale-dragging, var(--xh-motion-scale-drag));
224
290
  cursor: grabbing;
225
291
  }
226
292
 
@@ -312,12 +378,12 @@
312
378
  color: inherit;
313
379
  /* 表单控件不继承页面字体,只给字号会让输入框用 UA 默认字族,与周围文本对不上 */
314
380
  font-family: inherit;
315
- font-size: var(--xh-color-picker-input-font-size, var(--xh-font-size-sm));
381
+ font-size: var(--xh-color-picker-input-font-size, var(--xh-text-secondary-size));
316
382
  text-align: center;
317
383
  }
318
384
 
319
385
  [data-scope='color-picker'][data-part='channel-input']:focus-visible {
320
- border-color: var(--xh-color-picker-input-border-focus, var(--xh-ring-focus));
386
+ border-color: var(--xh-color-picker-input-border-focus, var(--xh-_tone, var(--xh-border-control-focus)));
321
387
  outline: var(--xh-ring-width) solid var(--xh-ring-focus);
322
388
  /* 往内收一道发丝:输入框贴着面板边排,环画在外沿会压到相邻通道 */
323
389
  outline-offset: calc(-1 * var(--xh-stroke-thin));
@@ -325,7 +391,7 @@
325
391
 
326
392
  /* 打到一半的串:框里留着草稿,但值没动——描边提示"这串还没被收下" */
327
393
  [data-scope='color-picker'][data-part='channel-input'][data-invalid] {
328
- border-color: var(--xh-color-picker-input-border-invalid, var(--xh-color-danger-500));
394
+ border-color: var(--xh-color-picker-input-border-invalid, var(--xh-border-invalid));
329
395
  }
330
396
 
331
397
  [data-scope='color-picker'][data-part='channel-input'][data-readonly] {
@@ -343,15 +409,15 @@
343
409
  flex: none;
344
410
  align-items: center;
345
411
  justify-content: center;
346
- inline-size: var(--xh-color-picker-action-size, 1.75rem);
347
- block-size: var(--xh-color-picker-action-size, 1.75rem);
412
+ inline-size: var(--xh-color-picker-action-size, var(--xh-control-action-size));
413
+ block-size: var(--xh-color-picker-action-size, var(--xh-control-action-size));
348
414
  padding: 0;
349
415
  border: var(--xh-stroke-thin) solid var(--xh-color-picker-action-border, var(--xh-border-control));
350
416
  border-radius: var(--xh-color-picker-action-radius, var(--xh-shape-control));
351
417
  background: var(--xh-color-picker-action-bg, transparent);
352
418
  color: var(--xh-color-picker-action-fg, var(--xh-fg-muted));
353
419
  font-family: inherit;
354
- font-size: var(--xh-color-picker-action-font-size, var(--xh-font-size-sm));
420
+ font-size: var(--xh-color-picker-action-font-size, var(--xh-text-secondary-size));
355
421
  line-height: var(--xh-leading-none);
356
422
  cursor: pointer;
357
423
  user-select: none;
@@ -391,7 +457,7 @@
391
457
  block-size: var(--xh-color-picker-swatch-item-size, 1.25rem);
392
458
  padding: 0;
393
459
  border: var(--xh-stroke-thin) solid var(--xh-color-picker-swatch-border, var(--xh-border-control));
394
- border-radius: var(--xh-color-picker-swatch-radius, var(--xh-radius-sm));
460
+ border-radius: var(--xh-color-picker-swatch-radius, var(--xh-shape-inset));
395
461
  cursor: pointer;
396
462
  }
397
463