@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,3 +1,12 @@
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
+ /* PasswordInput 独立皮肤同时传递引入字段视觉盒与字段内动作;full bundle 递归去重。 */
7
+ @import '../family/field-chrome.css';
8
+ @import '../family/action-control.css';
9
+
1
10
  @layer xihan.components {
2
11
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
12
  [data-scope='password-input'] {
@@ -5,40 +14,38 @@
5
14
  }
6
15
 
7
16
  /* root 只负责纵向堆叠:标签一行、输入行一行。
8
- 输入框、切换钮与大写锁定提示收进 control 部件即得一体式:视觉盒画在 control 上,
17
+ 输入框、切换钮与大写锁定提示收进 control 部件即得一体式:视觉盒由 Field Chrome 家族画在 control 上,
9
18
  框内三件都退成透明分段,整枚控件从头到尾只有一道描边。
10
- 不写 control 时退回三件并排的观感(下面 input 与按钮的基础规则原样生效) */
19
+ 不写 control 时是独立输入框和按钮,不再绘制独立外壳(与 text-field 同)。
20
+ 组件层只把既有公开槽映射到家族合同;形态三档由家族按 control 上的 data-variant 生成,皮肤不再声明。 */
11
21
  [data-scope='password-input'][data-part='root'] {
12
22
  /* 作者塞进来的图标跟着控件的尺寸档走;它自己写了 size 时仍以作者的为准 */
13
23
  --xh-icon-size: var(--xh-password-input-icon-size, var(--xh-glyph-size-md));
14
24
 
15
- /* 尺寸档只换这几个私有槽,标题、输入框、切换钮与提示都从 root 继承它们,
25
+ /* 尺寸档只换这几个私有槽,输入框、切换钮与提示都从 root 继承它们,
16
26
  换档不必给每个部件各写一条选择器 */
17
27
  --xh-_password-input-h: var(--xh-control-h-md);
18
28
  --xh-_password-input-px: var(--xh-control-px-md);
19
29
  --xh-_password-input-gap: var(--xh-control-gap-md);
20
30
  --xh-_password-input-font-size: var(--xh-control-font-md);
21
- --xh-_password-input-label-font-size: var(--xh-control-font-md);
22
31
  /* 提示与切换钮里的字形都比正文小一档,三个尺寸档各自换过,不是一个恒等的中转 */
23
- --xh-_password-input-hint-font-size: var(--xh-control-caption-md);
32
+ --xh-_password-input-caps-lock-font-size: var(--xh-control-caption-md);
24
33
  --xh-_password-input-trigger-font-size: var(--xh-control-caption-md);
34
+ /* 切换钮与输入之间那道线的长度:取钮高的一半,线的上下各留一截空白 */
35
+ --xh-_password-input-divider-h: 50%;
25
36
 
26
- /* 盒子的圆角 */
27
- --xh-_password-input-radius: var(--xh-password-input-input-radius, var(--xh-shape-control));
28
-
29
- /* 形态档只换这三个私有槽:底、常态描边、悬停描边 */
30
- --xh-_password-input-bg: var(--xh-bg-canvas);
31
- --xh-_password-input-border: var(--xh-border-control);
32
- --xh-_password-input-border-hover: var(--xh-border-control-hover);
33
-
34
- /* 聚焦这一处的两个取值。语气只改描边,环恒用 --xh-ring-focus:
35
- 环要在各种底上都读得出来,跟着语气走会掉到 warning 那种够不着对比度的档上 */
36
- --xh-_password-input-border-focus: var(--xh-border-control-focus);
37
+ /* 盒里有两个 Tab 停靠点,切换钮上再画一圈自己的环;环恒用 --xh-ring-focus,校验失败时随盒换红 */
37
38
  --xh-_password-input-ring: var(--xh-ring-focus);
38
39
 
39
40
  display: inline-flex;
40
41
  flex-direction: column;
41
42
  gap: var(--xh-password-input-gap, var(--xh-space-1));
43
+
44
+ /* 不传尺寸时一律这么宽,宽度不随内容走;父容器给不出这么宽时整件跟着收,地板同步让步,不越出父容器。
45
+ 地板不高过缺省宽:使用者把缺省宽钉到底线以下时不必再放开底线 */
46
+ inline-size: var(--xh-password-input-control-w, var(--xh-control-w));
47
+ min-inline-size: min(var(--xh-password-input-control-w, var(--xh-control-w)), var(--xh-password-input-control-min-w, var(--xh-control-min-w)), 100%);
48
+ max-inline-size: 100%;
42
49
  }
43
50
 
44
51
  /* 只要声明了 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去 */
@@ -54,8 +61,7 @@
54
61
  --xh-_password-input-px: var(--xh-control-px-sm);
55
62
  --xh-_password-input-gap: var(--xh-control-gap-sm);
56
63
  --xh-_password-input-font-size: var(--xh-control-font-sm);
57
- --xh-_password-input-label-font-size: var(--xh-control-font-sm);
58
- --xh-_password-input-hint-font-size: var(--xh-control-caption-sm);
64
+ --xh-_password-input-caps-lock-font-size: var(--xh-control-caption-sm);
59
65
  --xh-_password-input-trigger-font-size: var(--xh-control-caption-sm);
60
66
  }
61
67
 
@@ -65,57 +71,15 @@
65
71
  --xh-_password-input-px: var(--xh-control-px-lg);
66
72
  --xh-_password-input-gap: var(--xh-control-gap-lg);
67
73
  --xh-_password-input-font-size: var(--xh-control-font-lg);
68
- --xh-_password-input-label-font-size: var(--xh-control-font-lg);
69
- --xh-_password-input-hint-font-size: var(--xh-control-caption-lg);
74
+ --xh-_password-input-caps-lock-font-size: var(--xh-control-caption-lg);
70
75
  --xh-_password-input-trigger-font-size: var(--xh-control-caption-lg);
71
76
  }
72
77
 
73
- /* —— 形态 —— */
74
- /* 形态只决定底与描边怎么画;用哪族颜色由 data-tone 决定。
75
- 输入框没有实心档:填满一个要往里打字的框,字与底就没法同时读 */
76
- [data-scope='password-input'][data-part='root'][data-variant='outline'] {
77
- --xh-_password-input-bg: var(--xh-bg-canvas);
78
- --xh-_password-input-border: var(--xh-border-control);
79
- --xh-_password-input-border-hover: var(--xh-border-control-hover);
80
- }
81
-
82
- /* 填充式:底色顶替描边表达边界。描边只退成透明而不去掉,1px 的位置留着,
83
- 与 outline 档并排时基线对得上、聚焦画描边也不引起跳动 */
84
- [data-scope='password-input'][data-part='root'][data-variant='subtle'] {
85
- --xh-_password-input-bg: var(--xh-bg-subtle);
86
- --xh-_password-input-border: transparent;
87
- --xh-_password-input-border-hover: var(--xh-border-control-hover);
88
- }
89
-
90
- /* 无框:底与常态描边透明,悬停与聚焦浮出描边 */
91
- [data-scope='password-input'][data-part='root'][data-variant='ghost'] {
92
- --xh-_password-input-bg: transparent;
93
- --xh-_password-input-border: transparent;
94
- --xh-_password-input-border-hover: var(--xh-border-control-hover);
95
- }
96
-
97
- /* —— 语气 —— */
98
- /* 必须带 [data-tone] 限定:语气槽是沿继承流下来的,不限定的话,
99
- 放进带语气的容器(提示条、通知)里的密码框会跟着一起改色,作者根本没写过 tone */
100
- [data-scope='password-input'][data-part='root'][data-tone] {
101
- --xh-_password-input-border-focus: var(--xh-_tone);
102
- }
103
-
104
- [data-scope='password-input'][data-part='root'][data-variant='subtle'][data-tone] {
105
- --xh-_password-input-bg: var(--xh-_tone-subtle);
106
- --xh-_password-input-border-hover: var(--xh-_tone-border);
107
- }
108
-
109
- [data-scope='password-input'][data-part='root'][data-variant='ghost'][data-tone] {
110
- --xh-_password-input-border-hover: var(--xh-_tone-border);
111
- }
112
-
113
78
  /* —— 标题 —— */
114
79
  [data-scope='password-input'][data-part='label'] {
115
80
  color: var(--xh-password-input-label-fg, var(--xh-fg-default));
116
- font-size: var(--xh-password-input-label-font-size, var(--xh-_password-input-label-font-size));
81
+ font-size: var(--xh-password-input-label-font-size, var(--xh-text-label-size));
117
82
  font-weight: var(--xh-password-input-label-font-weight, var(--xh-text-label-weight));
118
- line-height: var(--xh-leading-none);
119
83
  }
120
84
 
121
85
  [data-scope='password-input'][data-part='label'][data-disabled] {
@@ -123,154 +87,72 @@
123
87
  }
124
88
 
125
89
  /* —— 一体式 control:输入框、切换钮与提示同框 —— */
126
- /* control 是唯一的视觉盒:描边、底色、圆角、聚焦环全画在它身上。
90
+ /* control 是唯一的视觉盒:描边、底色、圆角、聚焦环、五态与悬停由 Field Chrome 家族画。
127
91
  :focus-within 而不是 :focus——盒里有两个 Tab 停靠点,输入框与切换钮各占一个 */
128
92
  [data-scope='password-input'][data-part='control'] {
129
- display: inline-flex;
130
- min-inline-size: var(--xh-password-input-control-min-w, var(--xh-control-min-w));
131
- /* 居中而不是拉伸:提示是一行文字,被拉满整框高度后字会贴着框顶 */
132
- align-items: center;
133
- gap: var(--xh-password-input-control-gap, var(--xh-_password-input-gap));
134
- block-size: var(--xh-password-input-control-h, var(--xh-_password-input-h));
135
- padding-inline: var(--xh-password-input-control-px, var(--xh-_password-input-px));
136
- border: var(--xh-stroke-thin) solid var(--xh-password-input-control-border, var(--xh-_password-input-border));
137
- border-radius: var(--xh-_password-input-radius);
138
- background: var(--xh-password-input-control-bg, var(--xh-_password-input-bg));
139
- transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
93
+ --xh-field-control-height: var(--xh-password-input-control-h, var(--xh-_password-input-h));
94
+ --xh-field-control-padding-inline: var(--xh-password-input-control-px, var(--xh-_password-input-px));
95
+ --xh-field-control-gap: var(--xh-password-input-control-gap, var(--xh-_password-input-gap));
96
+ --xh-field-control-min-inline-size: var(--xh-password-input-control-min-w, var(--xh-control-min-w));
97
+ --xh-field-control-radius: var(--xh-password-input-control-radius, var(--xh-shape-control));
98
+ --xh-field-bg-rest: var(--xh-password-input-control-bg, var(--xh-_field-variant-bg-rest));
99
+ --xh-field-bg-hover: var(--xh-password-input-control-bg-hover, var(--xh-_field-variant-bg-hover));
100
+ --xh-field-bg-read-only: var(--xh-password-input-control-bg-readonly, var(--xh-_field-variant-bg-read-only));
101
+ --xh-field-bg-disabled: var(--xh-password-input-control-bg-disabled, var(--xh-_field-variant-bg-disabled));
102
+ --xh-field-fg-rest: var(--xh-password-input-control-fg, var(--xh-fg-default));
103
+ --xh-field-border-rest: var(--xh-password-input-control-border, var(--xh-_field-variant-border-rest));
104
+ --xh-field-border-hover: var(--xh-password-input-control-border-hover, var(--xh-_field-variant-border-hover));
105
+ --xh-field-border-focus: var(--xh-password-input-control-border-focus, var(--xh-_field-variant-border-focus));
106
+ --xh-field-border-invalid: var(--xh-password-input-control-border-invalid, var(--xh-_field-variant-border-invalid));
107
+ --xh-field-ring-focus: var(--xh-ring-focus);
108
+ --xh-field-ring-invalid: var(--xh-ring-invalid);
109
+ --xh-field-shadow-rest: var(--xh-password-input-control-shadow, none);
110
+ /* 家族 chrome 会重写 --xh-icon-size,使用者图标槽必须经桥接槽下发才能在盒内生效 */
111
+ --xh-field-glyph-size: var(--xh-password-input-icon-size, var(--xh-_field-size-glyph-size));
140
112
  }
141
113
 
142
114
  [data-scope='password-input'][data-part='control'][hidden] {
143
115
  display: none;
144
116
  }
145
117
 
146
- /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
147
- [data-scope='password-input'][data-part='control']:hover:not([data-disabled], [data-invalid]) {
148
- border-color: var(--xh-password-input-control-border-hover, var(--xh-_password-input-border-hover));
149
- }
150
-
151
- [data-scope='password-input'][data-part='control']:focus-within:not([data-disabled]) {
152
- border-color: var(--xh-password-input-control-border-focus, var(--xh-_password-input-border-focus));
153
- outline: var(--xh-ring-width) solid var(--xh-_password-input-ring);
154
- outline-offset: var(--xh-ring-offset);
155
- }
156
-
118
+ /* 校验失败时切换钮自己那圈环也跟着变红:两圈等宽相邻却一红一蓝,读的人不知道哪个才是当前状态 */
157
119
  [data-scope='password-input'][data-part='control'][data-invalid] {
158
- border-color: var(--xh-password-input-control-border-invalid, var(--xh-border-invalid));
159
-
160
- /* 校验失败时环也跟着变红:两圈等宽相邻却一红一蓝,读的人不知道哪个才是当前状态 */
161
120
  --xh-_password-input-ring: var(--xh-ring-invalid);
162
121
  }
163
122
 
164
- /* 只读仍可聚焦、可复制,所以不置灰,只把底色压下去表示"改不动" */
165
- [data-scope='password-input'][data-part='control'][data-readonly] {
166
- background: var(--xh-password-input-control-bg-readonly, var(--xh-bg-subtle));
167
- }
168
-
169
- [data-scope='password-input'][data-part='control'][data-disabled] {
170
- background: var(--xh-password-input-control-bg-disabled, var(--xh-bg-subtle));
171
- color: var(--xh-fg-disabled);
172
- cursor: not-allowed;
173
- }
174
-
175
123
  /* —— 输入框 —— */
176
- /* 不写 control 时它自己就是那个框:描边、圆角、底都长在它身上 */
124
+ /* 无边框无底无圆角无落影由家族重置,观感全归 control;自动填充由家族用 canvas 实体底与默认前景重绘 */
177
125
  [data-scope='password-input'][data-part='input'] {
178
- min-inline-size: var(--xh-password-input-input-min-w, var(--xh-control-min-w));
179
- block-size: var(--xh-password-input-input-h, var(--xh-_password-input-h));
180
- padding-inline: var(--xh-password-input-input-px, var(--xh-_password-input-px));
181
- padding-block: 0;
182
- border: var(--xh-stroke-thin) solid var(--xh-password-input-input-border, var(--xh-_password-input-border));
183
- border-radius: var(--xh-_password-input-radius);
184
- background: var(--xh-password-input-input-bg, var(--xh-_password-input-bg));
185
- color: var(--xh-password-input-input-fg, var(--xh-fg-default));
186
- /* 表单控件不继承页面字体,只给字号会让输入框用 UA 默认字族,与周围文本对不上 */
187
- font-family: inherit;
188
- font-size: var(--xh-password-input-input-font-size, var(--xh-_password-input-font-size));
189
- line-height: var(--xh-leading-normal);
190
- transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
191
- }
192
-
193
- [data-scope='password-input'][data-part='input']::placeholder {
194
- color: var(--xh-password-input-placeholder-fg, var(--xh-fg-subtle));
195
- }
196
-
197
- [data-scope='password-input'][data-part='input']:hover:not(:disabled, [data-invalid]) {
198
- border-color: var(--xh-password-input-input-border-hover, var(--xh-_password-input-border-hover));
199
- }
200
-
201
- [data-scope='password-input'][data-part='input']:focus-visible {
202
- border-color: var(--xh-password-input-input-border-focus, var(--xh-_password-input-border-focus));
203
- outline: var(--xh-ring-width) solid var(--xh-_password-input-ring);
204
- outline-offset: var(--xh-ring-offset);
205
- }
206
-
207
- [data-scope='password-input'][data-part='input'][data-invalid] {
208
- border-color: var(--xh-password-input-input-border-invalid, var(--xh-border-invalid));
209
-
210
- --xh-_password-input-ring: var(--xh-ring-invalid);
211
- }
212
-
213
- [data-scope='password-input'][data-part='input'][readonly] {
214
- background: var(--xh-password-input-input-bg-readonly, var(--xh-bg-subtle));
215
- }
216
-
217
- /* input 是单体表单控件,禁用走原生 disabled */
218
- [data-scope='password-input'][data-part='input']:disabled {
219
- background: var(--xh-password-input-input-bg-disabled, var(--xh-bg-subtle));
220
- color: var(--xh-fg-disabled);
221
- cursor: not-allowed;
222
- }
223
-
224
- /* 盒内 input:无边框无底无圆角,观感全归 control;它自己不画聚焦环,
225
- 盒子已经在 :focus-within 上画了一圈,再画一圈就成了两道 */
226
- [data-scope='password-input'][data-part='control'] [data-part='input'] {
227
- flex: 1 1 auto;
228
- min-inline-size: 0;
229
- block-size: 100%;
230
- padding-inline: 0;
231
- border: 0;
232
- border-radius: 0;
233
- background: transparent;
234
- }
235
-
236
- [data-scope='password-input'][data-part='control'] [data-part='input']:hover:not(:disabled, [data-invalid]) {
237
- border-color: transparent;
238
- }
239
-
240
- [data-scope='password-input'][data-part='control'] [data-part='input']:focus-visible {
241
- border-color: transparent;
242
- outline: none;
243
- }
244
-
245
- [data-scope='password-input'][data-part='control'] [data-part='input'][data-invalid] {
246
- border-color: transparent;
247
- }
248
-
249
- [data-scope='password-input'][data-part='control'] [data-part='input']:disabled,
250
- [data-scope='password-input'][data-part='control'] [data-part='input'][readonly] {
251
- background: transparent;
126
+ --xh-field-input-fg: var(--xh-password-input-input-fg, var(--xh-fg-default));
127
+ --xh-field-input-font-size: var(--xh-password-input-input-font-size, var(--xh-_password-input-font-size));
128
+ --xh-field-placeholder-fg: var(--xh-password-input-placeholder-fg, var(--xh-fg-subtle));
129
+ --xh-field-autofill-bg: var(--xh-password-input-input-autofill-bg, var(--xh-bg-canvas));
130
+ --xh-field-autofill-fg: var(--xh-password-input-input-autofill-fg, var(--xh-fg-default));
252
131
  }
253
132
 
254
133
  /* —— 明暗切换钮 —— */
255
- /* 行内动作方钮,常态只剩符号:自带一块底色会把一个盒子读成两块拼起来的 */
134
+ /* 正方视觉盒、inset 圆角、悬停 100 / 按下 200 与 0.97 按压、粗指针热区全由 Action Control 的
135
+ field-inset ghost 档给,组件只映射既有覆盖槽;常态只剩符号,符号取次级前景 */
256
136
  [data-scope='password-input'][data-part='visibility-trigger'] {
257
- display: inline-flex;
258
- flex: none;
259
- align-items: center;
260
- justify-content: center;
261
- inline-size: var(--xh-password-input-trigger-size, var(--xh-control-action-size));
262
- block-size: var(--xh-password-input-trigger-size, var(--xh-control-action-size));
263
- padding: 0;
264
- border: 0;
265
- border-radius: var(--xh-password-input-trigger-radius, var(--xh-shape-control));
266
- background: var(--xh-password-input-trigger-bg, transparent);
267
- color: var(--xh-password-input-trigger-fg, var(--xh-fg-muted));
268
- font-family: inherit;
269
- font-size: var(--xh-password-input-trigger-font-size, var(--xh-_password-input-trigger-font-size));
270
- line-height: var(--xh-leading-none);
271
- cursor: pointer;
272
- user-select: none;
273
- transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
137
+ --xh-action-visual-size: var(--xh-password-input-trigger-size, var(--xh-_action-profile-visual-size));
138
+ --xh-action-min-inline-size: var(--xh-password-input-trigger-size, var(--xh-_action-profile-visual-size));
139
+ --xh-action-radius: var(--xh-password-input-trigger-radius, var(--xh-shape-inset));
140
+ /* 底色阶梯由 ghost 档给(透明 → 100 → 200),使用者槽只作覆盖入口 */
141
+ --xh-action-bg-rest: var(--xh-password-input-trigger-bg, var(--xh-_action-variant-bg-rest));
142
+ --xh-action-bg-hover: var(--xh-password-input-trigger-bg-hover, var(--xh-_action-variant-bg-hover));
143
+ --xh-action-bg-pressed: var(--xh-password-input-trigger-bg-active, var(--xh-_action-variant-bg-pressed));
144
+ --xh-action-fg-rest: var(--xh-password-input-trigger-fg, var(--xh-fg-muted));
145
+ --xh-action-fg-hover: var(--xh-password-input-trigger-fg-hover, var(--xh-fg-default));
146
+ --xh-action-border-rest: transparent;
147
+ --xh-action-highlight-rest: transparent;
148
+ --xh-action-shadow-rest: none;
149
+ --xh-action-shadow-hover: none;
150
+ --xh-action-shadow-pressed: none;
151
+ --xh-action-font-size: var(--xh-password-input-trigger-font-size, var(--xh-_password-input-trigger-font-size));
152
+ /* 配方尚未把 profile 字形档灌进 --xh-icon-size,钮内字形先按 field-inset 档逐钮取值 */
153
+ --xh-icon-size: var(--xh-_action-profile-glyph-size);
154
+
155
+ border-width: 0;
274
156
 
275
157
  --xh-_password-input-mark: var(--xh-glyph-mark-eye);
276
158
  }
@@ -279,29 +161,50 @@
279
161
  display: none;
280
162
  }
281
163
 
282
- [data-scope='password-input'][data-part='visibility-trigger']:hover:not(:disabled) {
283
- background: var(--xh-password-input-trigger-bg-hover, var(--xh-bg-subtle-hover));
284
- color: var(--xh-password-input-trigger-fg-hover, var(--xh-fg-default));
164
+ /* 显隐动作与输入/状态区之间留一道半高分隔。::before 是兜底字形、::after 在粗指针下是家族给的
165
+ 44px 热区,线只能落在 background-image 上:一条 1px × 半高的渐变贴在靠输入的那一侧,
166
+ 不随按钮悬停底或圆角变化。background-position 是物理轴,RTL 另给两条换边。 */
167
+ [data-scope='password-input'][data-part='control']
168
+ > [data-scope='password-input'][data-part='visibility-trigger']:has(~ [data-scope='password-input'][data-part='input']),
169
+ [data-scope='password-input'][data-part='control']
170
+ > [data-scope='password-input'][data-part='input'] ~ [data-scope='password-input'][data-part='visibility-trigger'] {
171
+ background-image: linear-gradient(
172
+ var(--xh-password-input-visibility-trigger-separator-color, var(--xh-border-subtle)),
173
+ var(--xh-password-input-visibility-trigger-separator-color, var(--xh-border-subtle))
174
+ );
175
+ background-repeat: no-repeat;
176
+ background-size: var(--xh-stroke-thin) var(--xh-password-input-visibility-trigger-separator-h, var(--xh-_password-input-divider-h));
285
177
  }
286
178
 
287
- [data-scope='password-input'][data-part='visibility-trigger']:active:not(:disabled) {
288
- background: var(--xh-password-input-trigger-bg-active, var(--xh-bg-subtle-active));
179
+ /* 钮排在输入之前:线贴在钮的逻辑末端 */
180
+ [data-scope='password-input'][data-part='control']
181
+ > [data-scope='password-input'][data-part='visibility-trigger']:has(~ [data-scope='password-input'][data-part='input']) {
182
+ background-position: 100% 50%;
289
183
  }
290
184
 
291
- /* 不写 control 时它自成一件,落焦要有自己的环 */
292
- [data-scope='password-input'][data-part='visibility-trigger']:focus-visible {
293
- outline: var(--xh-ring-width) solid var(--xh-_password-input-ring);
294
- outline-offset: var(--xh-ring-offset);
185
+ [dir='rtl'] [data-scope='password-input'][data-part='control']
186
+ > [data-scope='password-input'][data-part='visibility-trigger']:has(~ [data-scope='password-input'][data-part='input']) {
187
+ background-position: 0 50%;
188
+ }
189
+
190
+ /* 钮排在输入之后:线贴在钮的逻辑起始侧 */
191
+ [data-scope='password-input'][data-part='control']
192
+ > [data-scope='password-input'][data-part='input'] ~ [data-scope='password-input'][data-part='visibility-trigger'] {
193
+ background-position: 0 50%;
194
+ }
195
+
196
+ [dir='rtl'] [data-scope='password-input'][data-part='control']
197
+ > [data-scope='password-input'][data-part='input'] ~ [data-scope='password-input'][data-part='visibility-trigger'] {
198
+ background-position: 100% 50%;
295
199
  }
296
200
 
297
201
  /* 盒里有两个 Tab 停靠点,盒子在 :focus-within 上画的那一圈两者完全一样,
298
- 分不出焦点在框里还是在钮上,所以钮上再画一圈自己的环。
202
+ 分不出焦点在框里还是在钮上,所以钮上再画一圈自己的环(家族只对不占 Tab 位的内嵌钮关环)。
299
203
  环往内收,压在按钮自己的方框里,不与盒子外面那一圈叠在一起。
300
- 指示焦点只能用环不能用底色:可用的几档底色压在盒底上都到不了 3:1
301
- 换了色也等于没画 */
204
+ 指示焦点只能用环不能用底色:可用的几档底色压在盒底上都到不了 3:1,换了色也等于没画 */
302
205
  [data-scope='password-input'][data-part='control'] [data-part='visibility-trigger']:focus-visible {
303
206
  outline: var(--xh-ring-width) solid var(--xh-_password-input-ring);
304
- outline-offset: calc(-1 * var(--xh-ring-offset));
207
+ outline-offset: var(--xh-ring-offset);
305
208
  }
306
209
 
307
210
  /* 明暗两态各一枚字形:换的是这个槽,兜底盒的量法只写一遍 */
@@ -321,24 +224,45 @@
321
224
  mask: var(--xh-_password-input-mark) center / contain no-repeat;
322
225
  }
323
226
 
324
- [data-scope='password-input'][data-part='visibility-trigger']:disabled {
325
- background: var(--xh-password-input-trigger-bg, transparent);
326
- color: var(--xh-fg-disabled);
327
- cursor: not-allowed;
328
- }
329
-
330
227
  /* —— 大写锁定提示 —— */
331
228
  /* 贴在切换钮之前的一小块提示,字号比正文小一档,颜色取次级前景:
332
- 它是提示不是报错,抢不过框里正在输入的内容 */
229
+ 它是提示不是报错,抢不过框里正在输入的内容。
230
+ 槽名按部件取 --xh-password-input-caps-lock-* */
231
+ /* 这一格恒占一个字形的宽,开合都不变:区内那句话有多长,控件就跟着宽多少,
232
+ 而这个盒是收缩包裹的(见 control 的 inline-flex),一开大写锁定控件当场宽出一整句话。
233
+ 文字留在盒里不动——它是活区域要念的内容——只是被 text-indent 推出可视范围,
234
+ 露出来的是 ::before 画的那枚字形。 */
333
235
  [data-scope='password-input'][data-part='caps-lock-indicator'] {
236
+ position: relative;
334
237
  display: inline-flex;
335
238
  flex: none;
336
239
  align-items: center;
337
- gap: var(--xh-password-input-hint-gap, var(--xh-space-1));
338
- padding-inline: var(--xh-password-input-hint-px, var(--xh-space-1));
339
- color: var(--xh-password-input-hint-fg, var(--xh-fg-muted));
340
- font-size: var(--xh-password-input-hint-font-size, var(--xh-_password-input-hint-font-size));
240
+ justify-content: center;
241
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
242
+ overflow: hidden;
243
+ color: var(--xh-password-input-caps-lock-fg, var(--xh-fg-muted));
244
+ font-size: var(--xh-password-input-caps-lock-font-size, var(--xh-_password-input-caps-lock-font-size));
341
245
  line-height: var(--xh-leading-none);
246
+ white-space: nowrap;
247
+ text-indent: 100%;
248
+ }
249
+
250
+ [data-scope='password-input'][data-part='control'][data-disabled]
251
+ > [data-scope='password-input'][data-part='caps-lock-indicator'] {
252
+ color: var(--xh-password-input-caps-lock-fg-disabled, var(--xh-fg-disabled));
253
+ }
254
+
255
+ /* 开着时才画字形:这一格的宽两态一样,画不画只换露不露出记号 */
256
+ [data-scope='password-input'][data-part='caps-lock-indicator'][data-state='visible']::before {
257
+ content: '';
258
+ position: absolute;
259
+ inset: 0;
260
+ margin: auto;
261
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
262
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
263
+ background-color: currentColor;
264
+ -webkit-mask: var(--xh-glyph-mark-arrow-up) center / contain no-repeat;
265
+ mask: var(--xh-glyph-mark-arrow-up) center / contain no-repeat;
342
266
  }
343
267
 
344
268
  /* 作者自己给这个节点写 hidden 时照样收起;上面那条 display 会把 UA 规则盖掉,
@@ -349,8 +273,87 @@
349
273
 
350
274
  /* 没开大写锁定时只把这块收成零宽,不用 display / visibility 撤掉它:
351
275
  播报区一旦从渲染树里消失就不再是活区域,重新出现时读屏当作插入了个新节点,多数不念。
352
- 此时区内文字是空串,留着的只有一个不占位的空盒 */
353
- [data-scope='password-input'][data-part='caps-lock-indicator'][data-state='hidden'] {
354
- padding-inline: 0;
276
+ 这一格的宽两态一样,关着时只是不画字形,盒本身照旧在场 */
277
+ [data-scope='password-input'][data-part='caps-lock-indicator'][data-state='hidden']::before {
278
+ content: none;
279
+ }
280
+
281
+ /* 强度条:一条底槽,填充按档位加长。档位由调用方打分后经 strength 传进来,
282
+ 0 与 1 判弱、2 判中、3 与 4 判强,三段各取一族语义色 */
283
+ [data-scope='password-input'][data-part='strength-meter'] {
284
+ --xh-_password-input-strength-fill: 0%;
285
+ --xh-_password-input-strength-fg: var(--xh-fg-muted);
286
+
287
+ display: block;
288
+ overflow: hidden;
289
+ inline-size: 100%;
290
+ block-size: var(--xh-password-input-strength-thickness, var(--xh-track-thickness));
291
+ border-radius: var(--xh-password-input-strength-radius, var(--xh-shape-pill));
292
+ background: var(--xh-password-input-strength-track, var(--xh-bg-subtle-active));
293
+ }
294
+
295
+ /* 没给 strength 时连接层给的是 hidden 属性;上面那条 display 会把 UA 规则盖掉 */
296
+ [data-scope='password-input'][data-part='strength-meter'][hidden] {
297
+ display: none;
298
+ }
299
+
300
+ /* 没写内容就由皮肤画填充;作者往部件里塞了自己的分段,这几条即不命中 */
301
+ [data-scope='password-input'][data-part='strength-meter']:empty::before {
302
+ content: '';
303
+ display: block;
304
+ inline-size: var(--xh-_password-input-strength-fill);
305
+ block-size: 100%;
306
+ border-radius: inherit;
307
+ background: var(--xh-password-input-strength-fg, var(--xh-_password-input-strength-fg));
308
+ }
309
+
310
+ [data-scope='password-input'][data-part='strength-meter'][data-level='1'] {
311
+ --xh-_password-input-strength-fill: 25%;
312
+ --xh-_password-input-strength-fg: var(--xh-fg-danger);
313
+ }
314
+
315
+ [data-scope='password-input'][data-part='strength-meter'][data-level='2'] {
316
+ --xh-_password-input-strength-fill: 50%;
317
+ --xh-_password-input-strength-fg: var(--xh-fg-warning);
318
+ }
319
+
320
+ [data-scope='password-input'][data-part='strength-meter'][data-level='3'] {
321
+ --xh-_password-input-strength-fill: 75%;
322
+ --xh-_password-input-strength-fg: var(--xh-fg-success);
323
+ }
324
+
325
+ [data-scope='password-input'][data-part='strength-meter'][data-level='4'] {
326
+ --xh-_password-input-strength-fill: 100%;
327
+ --xh-_password-input-strength-fg: var(--xh-fg-success);
328
+ }
329
+
330
+ [data-scope='password-input'][data-part='strength-meter'][data-disabled] {
331
+ --xh-_password-input-strength-fg: var(--xh-fg-disabled);
332
+ }
333
+
334
+ /* 高对比档整层丢弃 background-image,分隔线改用系统色重画,并关掉这一颗钮的强制换色 */
335
+ @media (forced-colors: active) {
336
+ [data-scope='password-input'][data-part='control']
337
+ > [data-scope='password-input'][data-part='visibility-trigger']:has(~ [data-scope='password-input'][data-part='input']),
338
+ [data-scope='password-input'][data-part='control']
339
+ > [data-scope='password-input'][data-part='input'] ~ [data-scope='password-input'][data-part='visibility-trigger'] {
340
+ background-image: linear-gradient(CanvasText, CanvasText);
341
+ forced-color-adjust: none;
342
+ }
343
+
344
+ [data-scope='password-input'][data-part='control'][data-disabled]
345
+ > [data-scope='password-input'][data-part='visibility-trigger']:has(~ [data-scope='password-input'][data-part='input']),
346
+ [data-scope='password-input'][data-part='control'][data-disabled]
347
+ > [data-scope='password-input'][data-part='input'] ~ [data-scope='password-input'][data-part='visibility-trigger'] {
348
+ background-image: linear-gradient(GrayText, GrayText);
349
+ }
350
+
351
+ /* 关掉强制换色后,上面那圈自留的 :focus-visible 环与家族普通态的焦点底/字都会按作者色落地;
352
+ 家族高对比块的特指度压不过它们,这里用同一选择器把环、底、字改回系统色 */
353
+ [data-scope='password-input'][data-part='control'] [data-part='visibility-trigger']:focus-visible {
354
+ outline-color: Highlight;
355
+ background-color: ButtonFace;
356
+ color: ButtonText;
357
+ }
355
358
  }
356
359
  }