@xihan-ui/styles 1.0.0-alpha.3 → 1.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 (96) hide show
  1. package/CHANGELOG.md +1730 -0
  2. package/css/accordion.css +25 -2
  3. package/css/alert.css +13 -9
  4. package/css/anchor.css +1 -1
  5. package/css/avatar-group.css +5 -1
  6. package/css/avatar.css +6 -0
  7. package/css/back-top.css +6 -0
  8. package/css/badge.css +94 -36
  9. package/css/breadcrumb.css +1 -1
  10. package/css/button-group.css +4 -0
  11. package/css/button.css +6 -0
  12. package/css/calendar.css +39 -13
  13. package/css/card.css +15 -6
  14. package/css/cascader.css +29 -23
  15. package/css/checkbox-group.css +38 -10
  16. package/css/checkbox.css +21 -10
  17. package/css/collapsible.css +7 -1
  18. package/css/color-picker.css +22 -2
  19. package/css/combobox.css +18 -7
  20. package/css/context-menu.css +17 -6
  21. package/css/date-field.css +2 -2
  22. package/css/date-picker.css +27 -17
  23. package/css/descriptions.css +9 -3
  24. package/css/dialog.css +7 -1
  25. package/css/drawer.css +15 -5
  26. package/css/dynamic-input.css +7 -0
  27. package/css/ellipsis.css +6 -0
  28. package/css/empty-state.css +16 -6
  29. package/css/field.css +7 -0
  30. package/css/file-upload.css +1 -1
  31. package/css/flex.css +13 -7
  32. package/css/float-button.css +7 -0
  33. package/css/floating-panel.css +6 -0
  34. package/css/grid.css +11 -5
  35. package/css/heatmap.css +3 -2
  36. package/css/highlight.css +1 -1
  37. package/css/hotkeys.css +1 -1
  38. package/css/hover-card.css +3 -0
  39. package/css/image-cropper.css +4 -2
  40. package/css/image-viewer.css +18 -5
  41. package/css/json-viewer.css +34 -1
  42. package/css/layout.css +15 -7
  43. package/css/list.css +14 -5
  44. package/css/listbox.css +4 -2
  45. package/css/loading-bar.css +5 -4
  46. package/css/log.css +6 -2
  47. package/css/masonry.css +5 -5
  48. package/css/mention.css +13 -3
  49. package/css/menu.css +16 -5
  50. package/css/menubar.css +14 -6
  51. package/css/navigation-menu.css +2 -2
  52. package/css/notification.css +377 -0
  53. package/css/number-field.css +9 -3
  54. package/css/page-header.css +7 -0
  55. package/css/pagination.css +158 -1
  56. package/css/password-input.css +4 -4
  57. package/css/pin-input.css +2 -2
  58. package/css/popconfirm.css +9 -2
  59. package/css/popover.css +3 -0
  60. package/css/popselect.css +24 -4
  61. package/css/progress.css +8 -2
  62. package/css/radio-group.css +17 -6
  63. package/css/rating.css +2 -2
  64. package/css/result.css +15 -5
  65. package/css/scrollbar.css +3 -0
  66. package/css/segmented.css +1 -1
  67. package/css/select.css +25 -7
  68. package/css/side-nav.css +16 -5
  69. package/css/slider.css +4 -2
  70. package/css/space.css +6 -6
  71. package/css/steps.css +6 -6
  72. package/css/switch.css +9 -2
  73. package/css/table.css +16 -8
  74. package/css/tabs.css +9 -1
  75. package/css/tag.css +3 -1
  76. package/css/tags-input.css +3 -3
  77. package/css/text-field.css +3 -3
  78. package/css/thread.css +10 -2
  79. package/css/time-field.css +2 -2
  80. package/css/time-picker.css +6 -6
  81. package/css/timeline.css +7 -7
  82. package/css/toast.css +103 -53
  83. package/css/toggle-group.css +30 -4
  84. package/css/toggle.css +6 -0
  85. package/css/tone.css +47 -17
  86. package/css/tooltip.css +7 -1
  87. package/css/tour.css +9 -3
  88. package/css/transfer.css +38 -12
  89. package/css/tree-select.css +30 -5
  90. package/css/tree.css +46 -4
  91. package/css/typography.css +1 -1
  92. package/css/undefined.css +1 -0
  93. package/index.css +1 -1
  94. package/index.unlayered.css +1746 -505
  95. package/package.json +3 -3
  96. package/css/toaster.css +0 -74
package/CHANGELOG.md CHANGED
@@ -1,5 +1,1734 @@
1
1
  # @xihan-ui/styles
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - bc7eeed: 徽标收窄成「只做角标」,并补齐角标该有的能力。
8
+
9
+ 原先 badge 与 tag 是一对孪生:`variant` 三形态、`size` 三档、默认插槽放任意内容,
10
+ 连档位取值都逐个相同。两个组件做同一件事,使用者只能靠猜。
11
+
12
+ 现在 badge 只做一件事——挂在别的元素角上的一枚标记:
13
+
14
+ ```vue
15
+ <XhBadge :count="5" tone="danger" label="5 条未读">
16
+ <XhButton>收件箱</XhButton>
17
+ </XhBadge>
18
+ ```
19
+
20
+ - 解剖从单层 `root` 变成 `root`(锚点)+ `indicator`(角标),定位归组件自己管,
21
+ 不再要宿主手写 `position: relative` 与负偏移。
22
+ - 新增 `placement`:`top-end`(默认)/ `top-start` / `bottom-end` / `bottom-start`,
23
+ 用逻辑属性写,rtl 下自动落到另一侧。
24
+ - `size` 换的是圆点直径、两位数时的最小宽度与字号,不再是药丸那套内衬与行高。
25
+ - Vue 侧另出 `XhBadgeRoot` / `XhBadgeIndicator`,要往角标里塞自定义内容时用它们。
26
+
27
+ **破坏性**:删掉 `variant`;行内的状态药丸请改用 `tag`(`XhTagRoot` + `XhTagLabel`)。
28
+ `data-size` 与 `data-tone` 从 `root` 挪到 `indicator`。
29
+
30
+ - 479bfcb: 级联选择皮肤全面翻修:展开路径改品牌淡底加粗、分支条目补右向箭头、列改内容撑宽定高、条目度量放宽。
31
+
32
+ 破坏性:4 个覆盖槽改名(不留旧名):
33
+
34
+ - `--xh-cascader-row-bg-highlight` → `--xh-cascader-row-bg-active`:展开路径的底色从中性灰二档改为品牌淡底(经 `--xh-_tone-subtle` 随语气、缺省 `--xh-bg-brand-subtle`)并加 `--xh-cascader-row-active-font-weight`(缺省 600);悬停与键盘锚点保持中性灰轻档,两档靠色相分家。
35
+ - `--xh-cascader-column-w` → `--xh-cascader-column-min-w`:列从定宽 11rem 改为内容撑宽 + 下限 7rem。
36
+ - `--xh-cascader-column-max-h` → `--xh-cascader-column-h`:列高从内容撑(上限 16rem)改为定高 11.25rem,切换展开路径浮层不再上下跳动。
37
+ - `--xh-cascader-indicator-size` → `--xh-cascader-item-indicator-size`:与既有 `--xh-cascader-item-indicator-fg` 配对,避免与触发器 indicator 部件混名。
38
+
39
+ 新增:
40
+
41
+ - 分支条目行尾自动画右向小箭头(`data-branch`,纯 CSS,`--xh-cascader-branch-arrow-size/-fg/-stroke` 可覆写,rtl 自动翻转,禁用同灰)。
42
+ - 触发器箭头与勾选标记的 `:empty` 兜底字形(▾ / ✓),与 select 同约定;级联勾选半选态皮肤自绘横杠。
43
+ - 条目 padding 放宽为 6px / 12px(行内走 `--xh-control-px-md`,紧凑密度自动收窄)、上限宽 25rem、背景与文字色过渡。
44
+ - 搜索候选与列内条目共用同一套行度量槽;搜索视图规则移入 `xihan.components` 层。
45
+ - 三处聚焦环改固定 `--xh-ring-focus`(不再随语气);浮层入场横移 rtl 翻转。
46
+
47
+ - bc65cb7: 首个公开版本:框架无关的 UI 基座。
48
+
49
+ 自研薄 FSM 内核 + headless(anatomy / machine / connect)+ 设计令牌与主题运行时 + 样式层,
50
+ 102 个组件在 Vue 与 Web Components 两套适配器上共用同一份内核,跨适配器一致性套件与
51
+ 真实 Chromium 里的无障碍扫描、浮层定位契约全绿。
52
+
53
+ 浮层定位、虚拟滚动、Web Components 响应式基类、代码着色、流式 Markdown 均为自研,
54
+ 运行时不带第三方依赖。
55
+
56
+ - 84b1aa3: 新增 Icon 原语,`@xihan-ui/icons` 整包重写为首方图标集。
57
+
58
+ 旧的 `@xihan-ui/icons` 是 27 个第三方图标集的聚合(约四万个图标),已整体移除并在
59
+ npm 上弃用。新包只收自研图标,第一批 29 个覆盖组件库自用的全部语义,24×24 单色
60
+ 描边、`stroke-width` 2。
61
+
62
+ 用法:
63
+
64
+ - `@xihan-ui/kernel` 导出 `IconRecord` / `IconNode` / `IconTag` 三个类型
65
+ - `@xihan-ui/headless` 导出 `connectIcon` / `iconAnatomy` / `iconMeta` / `iconKeyboard`
66
+ - `@xihan-ui/vue` 导出 `XhIcon`,`@xihan-ui/web-components` 注册 `<xh-icon>`
67
+ - `@xihan-ui/styles` 新增 `icon.css`,`data-size` 与 `data-weight` 各三档
68
+
69
+ 图标记录是结构化节点数组而不是 SVG 字符串,渲染端逐节点建元素,运行期不经 HTML
70
+ 解析器。图标数据传的是记录本身而不是名字:按名字查表要把全表静态引进来,摇树会
71
+ 整个失效。
72
+
73
+ WC 侧要在 `<svg data-xh-part="root">` 里留一个空的 `<g data-xh-part="glyph"></g>`
74
+ 作为授权点,元素只在它内部铺图元;不留这个空壳就一个节点都不动,手写内联 SVG 与
75
+ `<use>` 引用两种写法因此都还能用。`icon` 是对象,只能走 property 传,属性里写不出来。
76
+
77
+ 可及名字两态互斥:`label` 给了非空白文本就输出 `role="img"` 与 `aria-label`,否则
78
+ 输出 `aria-hidden="true"`。只有图标的按钮请把名字写在按钮上而不是图标上,两处都写
79
+ 读屏会念两遍。
80
+
81
+ - 9d7d703: 下拉/列表族条目度量与高亮档位统一(select / menu / listbox / combobox / popselect / tree / tree-select,向级联选择的两档制看齐)。
82
+
83
+ 破坏性:7 个覆盖槽改名或移除(不留旧名):
84
+
85
+ - `--xh-select-item-bg-highlight` → `--xh-select-item-bg-hover`、`--xh-combobox-item-bg-highlight` → `--xh-combobox-item-bg-hover`、`--xh-popselect-item-bg-highlight` → `--xh-popselect-item-bg-hover`、`--xh-menu-item-bg-highlight` → `--xh-menu-item-bg-hover`:悬停与键盘锚点统一为中性灰轻档(缺省 `--xh-bg-subtle`),不再随语气换色。
86
+ - `--xh-listbox-item-bg-highlight`、`--xh-tree-row-bg-highlight`、`--xh-tree-select-row-bg-highlight` 移除:键盘锚点并入轻档,与悬停共用 `-bg-hover` 一个槽(键盘位置由聚焦环表达,选中仍是文字色 + ✓ 标记,互不挤占)。
87
+
88
+ 新增(menu):
89
+
90
+ - 打开子菜单的触发条目升为强档:品牌淡底(经 `--xh-_tone-subtle` 随语气、缺省 `--xh-bg-brand-subtle`)+ 600 字重,新增 `--xh-menu-item-bg-active` 与 `--xh-menu-item-active-font-weight` 槽;两档靠色相分家,与级联选择的展开路径同一套词汇。
91
+ - 条目行高从 none 抬到 normal,新增 `--xh-menu-item-leading` 槽。
92
+
93
+ 度量:
94
+
95
+ - 七家条目 padding 统一为 6px / 12px(`--xh-space-1_5` / `--xh-control-px-md`,行内随密度轴收窄),底色与文字色补 micro 过渡。
96
+ - menu 尺寸阶梯重排:sm = `--xh-space-1` / `--xh-control-px-sm`,lg = `--xh-space-2` / `--xh-control-px-lg`。
97
+ - listbox / combobox 分组标题行内内缩跟随条目改为 `--xh-control-px-md`,与条目文字保持同一条竖线。
98
+
99
+ - f7d53de: 列表族条目度量与高亮档位统一第二批(context-menu / menubar / mention / time-picker / transfer / table),与 select 族同一套两档词汇。
100
+
101
+ 破坏性:9 个覆盖槽改名或移除(不留旧名):
102
+
103
+ - `--xh-context-menu-item-bg-highlight` → `--xh-context-menu-item-bg-hover`、`--xh-menubar-item-bg-highlight` → `--xh-menubar-item-bg-hover`、`--xh-transfer-item-bg-highlight` → `--xh-transfer-item-bg-hover`、`--xh-table-row-bg-highlight` → `--xh-table-row-bg-hover`、`--xh-time-picker-item-bg-highlight` → `--xh-time-picker-item-bg-hover`:悬停与键盘锚点统一为中性灰轻档(缺省 `--xh-bg-subtle`),不再随语气换色。
104
+ - `--xh-time-picker-item-bg-checked-highlight` → `--xh-time-picker-item-bg-checked-hover`:同一档位词汇;选中格保持品牌实底不变。
105
+ - `--xh-mention-item-bg-highlight` → `--xh-mention-item-bg-hover`,`--xh-mention-item-fg-highlight` 与 `--xh-mention-item-font-weight-highlight` 移除:候选锚点回归纯轻档底色,不再借选中的文字色与字重。
106
+
107
+ 新增:
108
+
109
+ - context-menu 打开子菜单的触发条目升强档:品牌淡底(经 `--xh-_tone-subtle` 随语气、缺省 `--xh-bg-brand-subtle`)+ 600 字重,新增 `--xh-context-menu-item-bg-active` 与 `--xh-context-menu-item-active-font-weight`。
110
+ - menubar 展开着的菜单 trigger 升强档:`--xh-menubar-trigger-bg-active` 槽名不变、缺省从中性灰改为品牌淡底;不加字重(横排加粗会推挤相邻触发器)。悬停新增轻档槽 `--xh-menubar-trigger-bg-hover`。
111
+ - 条目行高从 none 抬到 normal,新增 `--xh-context-menu-item-leading` 与 `--xh-menubar-item-leading`。
112
+
113
+ 度量:
114
+
115
+ - context-menu / menubar / mention / transfer 条目与 time-picker 格 padding 统一 6px / 12px(`--xh-space-1_5` / `--xh-control-px-md`,行内随密度轴收窄),底色文字色补 micro 过渡;context-menu / menubar 尺寸阶梯重排(sm = `--xh-space-1` / `--xh-control-px-sm`,lg = `--xh-space-2` / `--xh-control-px-lg`)。
116
+ - time-picker 格保持紧排行高:居中的单个数字格没有截断层,列内多露几格。
117
+ - transfer 面板头与搜索框行内内缩跟随条目改 `--xh-control-px-md`,勾选列与全选框保持同一条竖线。
118
+ - table 只统一行高亮档位词汇;行选中保持底色表达(宽行扫读依赖底色通道),单元格度量不动。
119
+ - tags-input 不入组:其 data-highlighted 是退格/方向键的操作光标(整颗反白表示即将删除或编辑),语义与列表导航高亮不同,胶囊度量亦非列表行。
120
+
121
+ - d43624c: 把跨组件已经分叉的名字统一回一套。part 名与 prop 名在 1.0 之后就是公开 API——皮肤按
122
+ `data-part` 选择、使用者按 prop 名调用——改名一律是破坏性变更,所以趁 alpha 一次改完。
123
+
124
+ **time-picker 的列表条目由 `option` 改叫 `item`。** 另外 32 个组件的列表条目都叫 `item`,
125
+ 只有它是 `option`。ARIA 角色仍是 `role="option"`(那是角色不是部件名),列里的候选值集合
126
+ `TimePickerColumn.options` 也不动(那是数据不是部件)。
127
+
128
+ 迁移点:
129
+
130
+ - `data-part='option'` 改成 `data-part='item'`;皮肤覆盖槽 `--xh-time-picker-option-*`
131
+ 改成 `--xh-time-picker-item-*`(共 10 个)。
132
+ - Vue 组件 `XhTimePickerOption` 改名 `XhTimePickerItem`。
133
+ - WC 的 `::part(option)` 改成 `::part(item)`。
134
+ - headless 导出:`timePickerOptionQuery` → `timePickerItemQuery`、`findTimePickerOption` →
135
+ `findTimePickerItem`、`timePickerOptionValue` → `timePickerItemValue`、
136
+ `TimePickerOptionProps` → `TimePickerItemProps`。
137
+ - `TimePickerApi` 上:`getOptionProps` → `getItemProps`、`isOptionSelected` → `isItemSelected`、
138
+ `isOptionDisabled` → `isItemDisabled`、`focusedOption` → `focusedItem`。
139
+ - 键盘规格号 `time-picker.kbd.option-*` → `time-picker.kbd.item-*`。
140
+
141
+ **transfer 的数据入口由 `items` 改叫 `collection`。** 另外 17 个集合组件的数据入口都叫
142
+ `collection`。单条的类型名 `TransferItem`、某一侧看得见的条目 `visibleItems`、纯函数
143
+ `transferVisibleItems` 都不动——它们说的是「条目」,不是「数据入口」。
144
+
145
+ 迁移点:
146
+
147
+ - Vue:`<XhTransferRoot :items="…">` 改成 `:collection="…"`。
148
+ - WC:`el.items = […]` 改成 `el.collection = […]`(这个入口表达不成属性,本来就只能走 property)。
149
+ - `TransferApi.items` → `TransferApi.collection`。
150
+
151
+ **checkbox-group 的组内子部件对齐 radio-group。** 同一语义两套名字:checkbox-group 用
152
+ `item-control` / `item-hidden-input`,radio-group 用 `indicator` / `hidden-input`。裸名是全仓
153
+ 多数(`indicator` 13 处、`hidden-input` 10 处),checkbox-group 随大流。`item-text` 不动
154
+ (21 份解剖都这么叫)。
155
+
156
+ 迁移点:
157
+
158
+ - `data-part='item-control'` → `'indicator'`,`data-part='item-hidden-input'` → `'hidden-input'`。
159
+ - 皮肤覆盖槽 `--xh-checkbox-group-control-*` → `--xh-checkbox-group-indicator-*`(10 个),
160
+ 与 radio-group 的 `--xh-radio-group-indicator-*` 对齐。
161
+ - `CheckboxGroupApi.getItemControlProps` → `getIndicatorProps`,
162
+ `getItemHiddenInputProps` → `getHiddenInputProps`(两个名字 radio-group 早就在用)。
163
+ - Vue 组件 `XhCheckboxGroupItemControl` → `XhCheckboxGroupIndicator`。
164
+
165
+ **table 的空态部件由 `empty-state` 改叫 `empty`。** 部件名不该与组件的 scope 名撞车——
166
+ `empty-state` 是一个独立组件的 `data-scope`,再拿它当 table 的部件名,写皮肤时
167
+ `[data-part='empty-state']` 与 `[data-scope='empty-state']` 混在一起读不出谁是谁。
168
+ combobox 早就叫 `empty`。独立的 `empty-state` 组件本身不动。
169
+
170
+ 迁移点:
171
+
172
+ - `data-part='empty-state'` → `'empty'`。
173
+ - `TableApi.getEmptyStateProps` → `getEmptyProps`。
174
+ - Vue 组件 `XhTableEmptyState` → `XhTableEmpty`(`XhEmptyState*` 那一族是另一个组件,不变)。
175
+ - WC 的 `::part(empty-state)` → `::part(empty)`。
176
+
177
+ **transfer 的 `onSelectedChange` 改叫 `onSelectionChange`。** table 与 tree 都叫
178
+ `onSelectionChange`。
179
+
180
+ - `TransferSelectedChangeDetails` → `TransferSelectionChangeDetails`。
181
+ - Vue 事件 `@selected-change` → `@selection-change`;WC 的 `selected-change` 事件同改。
182
+
183
+ **`size` 不再一名两用。** 三轴里的 `size` 是语气枚举,而 qr-code 的 `size` 是像素数值、
184
+ splitter 的 `size` 是百分比数组——两者占着同一个名字却是完全不同的类型,使用者写
185
+ `size="md"` 得到的是静默的错。
186
+
187
+ - qr-code:`size` → `pixelSize`(WC 属性 `size` → `pixel-size`)。中心 logo 挖空区的
188
+ `QrCodeLogoArea.size` 是模块数标量,不动。
189
+ - splitter:数组值的一律改复数——`size` → `sizes`、`defaultSize` → `defaultSizes`、
190
+ `onSizeChange` → `onSizesChange`、`onSizeChangeEnd` → `onSizesChangeEnd`、载荷字段
191
+ `{ size }` → `{ sizes }`、机器事件 `SIZE.SET` → `SIZES.SET`、Vue 的 `v-model:size` →
192
+ `v-model:sizes`、WC 属性 `size` → `sizes`。标量的不动:每块面板的 `collapsedSize`、
193
+ `BOUNDARY.SET` 的 `size`、`setPanelSize`、`SplitterPanelState.size`。
194
+
195
+ **「移除列表里的一项」统一叫 `item-delete-trigger`。** 同一个动作四个组件三个名字:tags-input
196
+ 与 file-upload 已经是 `item-delete-trigger`,select 叫 `tag-remove`、dynamic-input 叫
197
+ `remove-trigger`。tag 的 `close-trigger` 不动——它关的是标签自身,不是列表里的一项。
198
+
199
+ 迁移点:
200
+
201
+ - select:`data-part='tag-remove'` → `'item-delete-trigger'`;皮肤覆盖槽
202
+ `--xh-select-tag-remove-*` → `--xh-select-item-delete-*`(6 个);
203
+ `SelectApi.getTagRemoveProps` → `getItemDeleteTriggerProps`;Vue 组件 `XhSelectTagRemove` →
204
+ `XhSelectItemDeleteTrigger`;WC 的 `::part(tag-remove)` → `::part(item-delete-trigger)`。
205
+ - dynamic-input:`data-part='remove-trigger'` → `'item-delete-trigger'`;皮肤覆盖槽
206
+ `--xh-dynamic-input-remove-fg-hover` → `--xh-dynamic-input-item-delete-fg-hover`;
207
+ `DynamicInputApi.getRemoveTriggerProps` → `getItemDeleteTriggerProps`;Vue 组件
208
+ `XhDynamicInputRemoveTrigger` → `XhDynamicInputItemDeleteTrigger`;WC 的
209
+ `::part(remove-trigger)` → `::part(item-delete-trigger)`。
210
+
211
+ **这枚按钮的文案键统一叫 `deleteItem`。** 四个组件的签名各不相同,统一的是命名形态。
212
+
213
+ - select:`SelectTranslations.removeTag: string` → `deleteItem: (label: string) => string`,
214
+ 由定值串改成接收标签文本的函数,缺省 `Delete ${label}`。
215
+ - tags-input:`deleteTagTrigger` → `deleteItem`。
216
+ - file-upload:`deleteFile` → `deleteItem`;`FileUploadApi.deleteFile` 方法与 `FILE.DELETE`
217
+ 事件名不动——那是动作不是文案。
218
+ - dynamic-input:`removeTrigger` → `deleteItem`。
219
+
220
+ **没有合并的一处,记在这里免得后人重新翻案。** 就绪度审计说 pin-input 的 `onValueComplete`、
221
+ editable 的 `onValueCommit`、slider 的 `onValueChangeEnd` 是「三个名字表达同一语义」,
222
+ 逐条读过源码后判定不成立:`onValueComplete` 是「每格都填满的那一刻」(值的形状谓词),
223
+ `onValueCommit` 是「提交那一刻」(用户显式确认),`onValueChangeEnd` 是「一次操作结束」
224
+ (手势结束,splitter 的 `onSizesChangeEnd` 用的是同一套)。三件不同的事,合并会让 API 更差。
225
+
226
+ - 3c033ca: 通知按卡片重排:左侧类型字形、右上角关闭钮、两列网格。
227
+
228
+ 它的皮肤是从旧的 toast 卡片逐字搬来的,搬完没人按「通知该长什么样」审过一遍,
229
+ 于是留下三处硬伤:
230
+
231
+ - **叉掉到了卡片左下方**。`item` 是竖排 flex,而叉上写着
232
+ `align-self: flex-start` + `margin-inline-start: auto`——交叉轴上的 auto 外边距
233
+ 会让对齐属性整条失效(flexbox §9.6),`align-self` 那行一点作用都没有,
234
+ 叉成了正文下面的第三行。实测它落在距卡片顶 55px 处,卡片因此高出一截。
235
+ 三家参考实现(Ant Design / Element Plus / Naive UI)都是绝对定位钉在右上角内衬处。
236
+ - **组件路径下一个类型指示物都没有**。徽记只由服务档的默认模板画,
237
+ 12 份示例与所有 Web Components 使用者拿到的卡片,语气全靠起始侧那条 4px 色条承载,
238
+ 而它压在卡片底上只有 1.9–2.8:1,`loading` 与 `info` 除颜色外完全同形。
239
+ - **字号比轻提示还小一档**(13px),标题与说明只差 7.7%,两层文字挤成一片。
240
+
241
+ 现在:
242
+
243
+ - 新增 `item-indicator` 部件。作者留空即由皮肤按 `data-type` 画一枚兜底字形
244
+ (info / success / warning / error 各一枚,`loading` 给转圈),
245
+ 颜色取 `--xh-_tone-fg`——与 alert 的状态图标同档,压在卡片底上十二组最低 4.08:1。
246
+ - **两列网格**:左列字形、右列标题与说明;叉绝对定位钉在右上角,标题自动让位
247
+ (写法照 dialog / drawer)。起始侧那条语气色条随之删除——三家都没有,
248
+ 语气改由字形承载。
249
+ - 卡片宽 320 → 384px(`--xh-overlay-max-w-lg`,与 Ant Design 同值),
250
+ 内衬四边 16px,字号回到正文档 14px。
251
+ - 服务档的默认模板改成四个节点平铺(不再套一层皮肤够不着的行容器),
252
+ 说明部件恒渲染——`aria-describedby` 是无条件发的,节点缺席就成了悬空引用。
253
+ - 地标 `role="region"` 从 `root` 搬到 `group`。root 是 `display: contents` 的作用域包装,
254
+ 量出来 0×0,地标挂在它身上跳过去落不到任何看得见的地方;那一摞才是真盒子。
255
+
256
+ 顺带补上三处从来没有门禁看管的地方:`check-elevation-role`、`check-press-feedback`、
257
+ `check-clear-trigger` 三份名单都没登记过 notification,眼下合规纯属巧合。
258
+
259
+ **破坏性**:删掉 `--xh-notification-accent` 与 `--xh-notification-accent-width`
260
+ 两个覆盖槽(色条没了)。另有几个槽的默认值变了:`--xh-notification-w`(20rem → 24rem)、
261
+ `--xh-notification-py` / `-px`(12/16 → 16/16)、`--xh-notification-font-size`(13 → 14)、
262
+ `--xh-notification-gap` 的语义从「行距」改为「图标与正文的列距」(行距另开
263
+ `--xh-notification-row-gap`)。地标从 root 挪到 group,按 `root[role=region]` 写过
264
+ 自动化断言的要跟着改。
265
+
266
+ - 516bd46: 浮层搬进单一落点,层号与背景失活跟着改口。
267
+
268
+ ## 浮层不再原地渲染
269
+
270
+ 此前 20 个带 positioner 的浮层里只有 dialog / drawer / image-viewer 搬走,其余 16 个
271
+ 留在触发器旁边。坐标一直是对的(定位引擎特意处理了「祖先抢走包含块」),坏的是层叠序:
272
+ 宿主应用的祖先只要建了层叠上下文——`transform` / `translate` / `scale` / `filter` /
273
+ `backdrop-filter` / `opacity` 小于 1 / `contain` / `will-change` / `position: sticky` /
274
+ 定位元素带 `z-index` / `isolation`——浮层的层号就退化成那个上下文里的局部序号,被任何
275
+ 上层兄弟盖住。这是库无法从自身约束的:宿主怎么写 DOM 不归库管。
276
+
277
+ kernel 新增 `ensurePortalRoot(doc)`,在 body 末尾维护单一 `#xh-portal-root`,
278
+ `RuntimeConfig.portalContainer` 的默认值指向它。Vue 侧 19 个浮层的 positioner
279
+ (tour 连同 backdrop 与 spotlight)一律 Teleport 过去。落点自身一条样式都不写——
280
+ 子元素全是 `position: fixed`,不占布局,而任何 `position` / `transform` / `contain` /
281
+ `isolation` 都会平白建出新的层叠上下文,正是要躲的东西。
282
+
283
+ WC 适配器是 Light DOM,解剖契约就是「作者写在哪就在哪」,搬不动。改为在浮层展开时
284
+ 沿祖先链探一次层叠上下文,命中就投一条诊断,指名是哪个祖先的哪条属性。
285
+
286
+ **破坏性**:浮层的 DOM 位置变了。按 `wrapper.querySelector` 之类以挂载根为基准取浮层
287
+ 节点的代码要改从 `document` 取。
288
+
289
+ ## 遮罩式浮层并到同一档层号
290
+
291
+ `--xh-z-drawer` 删除,`--xh-layer-drawer` 与 `--xh-layer-modal` 解析到同一个值。
292
+
293
+ 原先抽屉 1000 低于对话框 1100,而两者都在同一个栈上下文里,纯靠数字定序:从对话框里
294
+ 拉出抽屉时,抽屉连同自己的遮罩一起沉在对话框遮罩底下,用户只看到画面又暗一层、什么都
295
+ 没出现,而焦点已经陷进看不见的面板。反方向是对的,所以这是只在一个方向上炸的组合。
296
+ 并档之后先后交给 portal 顺序决定,与对话框套对话框的现有行为一致。
297
+
298
+ **破坏性**:`--xh-z-drawer` 这个名字没有了。改用 `--xh-layer-drawer`。
299
+
300
+ ## 背景失活改走祖先链
301
+
302
+ `hideOutside` 此前只遍历 body 直接子元素,判据是「这个子元素包含 target 就整块放行」。
303
+ WC 适配器的浮层长在作者写它的位置,应用只要有一层根容器(`#app` 之类)就会因包含浮层
304
+ 被整块豁免——模态对话框身后的整个应用对读屏依然完全可遍历,不认外点关闭的
305
+ `alertdialog` 更是完全可点。改成沿每个 target 到 body 的祖先链逐层罩住其余兄弟。
306
+
307
+ `data-xh-inert-exempt` 的语义随之扩大:带标记的元素及其后代不被罩住,**其祖先只递归、
308
+ 不整块罩住**。通知队列因此在任意嵌套深度都能保持可点,外点判定也一并豁免(点通知不再
309
+ 把模态关掉)。
310
+
311
+ ## 其余
312
+
313
+ - `--xh-editable-preview-line-height` 删除,改用 `--xh-editable-preview-min-h`:预览态
314
+ 原先拿行高冒充高度,实测比同组件的编辑态高 2px,切换时跳一下。
315
+ - tooltip 与 navigation-menu 入层栈,Escape 不再连它们下面的对话框一起关掉。
316
+ - 定位引擎新增 size 中间件,回报可用空间与锚点宽度;菜单族补上高度上限与内部滚动。
317
+ - 包含块判定补齐 `translate` / `rotate` / `scale` 独立属性与 `backdrop-filter`。
318
+ - 滚动锁补滚动条补偿与滚动根探测。
319
+
320
+ - 1590d92: Select 的盒不再自带宽度上限,框宽交回布局;视觉行为变更。
321
+
322
+ `[data-part='control']` 上原有一条 `max-inline-size`,兜底取 `--xh-overlay-max-w`(20rem / 320px)。
323
+ 浮层的宽度预算被搬到了在流内排布的表单控件上:格子一旦宽过 320px,select 就停在 320px 不再跟着长——
324
+ 两列栅格的弹窗里,左边的 select 比右边的数字输入框窄一截。硬上限也不是必需的:`value-text` 与
325
+ `trigger` 各有 `min-inline-size: 0` 配省略号,长值撑不破盒。
326
+
327
+ 同族的 cascader / tree-select / popselect / color-picker,以及 text-field / number-field,
328
+ control 上都没有上限,select 是唯一一家。这条删掉之后全族同形。
329
+
330
+ 破坏性变更:**覆盖槽 `--xh-select-control-max-w` 随之移除**。此前写过
331
+ `--xh-select-control-max-w: 24rem` 的,改在自己的布局层给 select 的根或所在格子写宽度
332
+ (`inline-size` / `max-inline-size`),效果一致且对同族其余控件通用。
333
+
334
+ `check-family-parity` 的下拉族 control 名单补上 `max-inline-size`:往后任何一家单独给盒封顶都会被拦下。
335
+ 公开面基线(`tooling/public-surface.json`)需随本次改动跑一次 `pnpm surface:update`。
336
+
337
+ - 934e126: 每份皮肤现在都能单独引入了,动画不再指望别处的文件在场。
338
+
339
+ `styles` 的 exports 逐组件铺了一百多条子入口,`import '@xihan-ui/styles/dialog.css'` 是受支持的用法。但 `xh-fade-in`、`xh-fade-out`、`xh-spin`、`xh-dialog-in/out` 这五支关键帧住在 `motion.css` 里,被 15 份别的皮肤引用——单独引入其中任何一份,动画名都查不到。`@keyframes` 的名字查找只认「文档里有没有这个名字」,查不到既不报错也不降级,看上去就是「这个组件没做动效」。`spinner.css` / `switch.css` / `popconfirm.css` 三处注释早就写明了这条理由,只是这五支没照办。
340
+
341
+ 现在每份皮肤都自带它用到的关键帧。`motion.css` 因此空了,**已删除,`./motion.css` 子入口一并移除**——如果你显式引过它,删掉那行即可,它提供的关键帧已经跟着各组件走了。
342
+
343
+ 新增 `check-keyframe-refs` 门禁盯住三件事:引用的动画名必须在同一份皮肤里定义、同名的多份定义必须逐字一致(名字是全局的,两份不同内容会互相覆盖)、关键帧必须写在 `@layer xihan.motion` 里(使用者按层覆盖时才盖得住)。
344
+
345
+ 产物只大了 60 B:重复的关键帧对 gzip 几乎是免费的。
346
+
347
+ - f4d3708: 轻提示改成短消息的样子:顶部居中、宽度包着内容、一行图标加一句话。
348
+
349
+ 上一版把 toast 从通知卡片收窄成操作反馈时只动了结构,皮肤还是照着卡片那份抄的——
350
+ 定宽 320px、竖排、起始侧一条 4px 语气色条、行尾一颗叉。一句「已保存」于是撑成一个
351
+ 方块,右边留着一大片空白,看着仍然像一则公告。
352
+
353
+ 现在它是这样:
354
+
355
+ ```
356
+ ┌──────────────────┐
357
+ │ ✓ 已保存 │ ← 贴着文字收缩,顶部居中
358
+ └──────────────────┘
359
+ ```
360
+
361
+ - **收缩包裹**:`inline-size` 的默认值从 `--xh-overlay-max-w` 改成 `auto`,
362
+ 上限压在 `min(48rem, 100%)`,长文案在上限处换行、仍然居中。
363
+ - **单行横排**:`flex-direction` 去掉,`align-items: center`;标题吃掉剩余宽度,
364
+ 操作钮与叉自动落到行尾(两者不再 `align-self: flex-start`)。
365
+ - **矮一档**:纵内衬从面档(12px)换成控件档 `--xh-field-py`(8px),条子高 39px,
366
+ 与 Element Plus message 的 39px 齐平、比 Ant Design message 的 40px 矮 1px。
367
+ - **语气走淡底**:底与描边取语气层的 `--xh-_tone-subtle` / `--xh-_tone-border`
368
+ (与 alert 同一套口径),正文留中性——正文也跟着兑成语气色的话,绿字压绿底是整条里
369
+ 对比度最差的一处。起始侧那条 4px 色条随之删除。
370
+ - **字号回到正文档**:13px → 14px;标题不再加粗、不再换行高,一句话的反馈没有主次之分。
371
+ - **状态字形不带圆底**:服务档的默认模板改用新的 `typeGlyph`(16px 裸字形,颜色取
372
+ `--xh-_tone-fg`,与 alert 的状态图标同档),圆底徽记 `typeBadge` 留给对话框那种有余裕的版面(通知的类型字形由皮肤在 `item-indicator` 上画)。
373
+ - **到点自己走的不出关闭按钮**:`createToastService` 的默认模板据此分两档——
374
+ 会自己消失的不出叉(三家参考实现都是这样),`loading` 与 `duration <= 0` 这种走不掉的
375
+ 反过来默认出叉,否则界面上一个可点、可聚焦的节点都没有。两档都能用 `closable` 显式改口。
376
+
377
+ **破坏性**:删掉 `--xh-toast-accent` 与 `--xh-toast-accent-width` 两个覆盖槽(色条没了)。
378
+ 另有四个槽的默认值变了:`--xh-toast-w`(20rem → auto)、`--xh-toast-bg`
379
+ (`--xh-bg-surface-raised` → 语气淡底)、`--xh-toast-border`(中性 → 语气描边)、
380
+ `--xh-toast-title-font-weight`(semibold → regular);`--xh-toast-close-size` 的默认值
381
+ 从 `--xh-control-h-sm`(28px)降到 `--xh-control-action-size`(24px)。
382
+ 靠「轻提示是 320px 定宽」做过对齐、或依赖默认那颗叉关闭常驻提示的用法要跟着改。
383
+
384
+ - 5a1aedd: 轻提示与通知分家:新增 notification,toast 收窄成操作反馈,toaster 删除。
385
+
386
+ 原先 toast 一个组件担了两件事——「用户刚点了一下,告诉他结果」和「系统主动推来一条消息」。
387
+ 两者的信息量、停留时长、落位习惯、谁触发都不一样,混在一起的结果是标题加正文两层文本、
388
+ 九宫格落位、堆叠上限这些只有后者需要的东西全压在轻提示上,而轻提示自己反倒要靠一个
389
+ 额外的容器组件才能用起来。
390
+
391
+ **通知(新增)**
392
+
393
+ ```vue
394
+ <XhNotificationRoot v-slot="{ create, dismiss }">
395
+ <XhNotificationGroup>
396
+ <template #default="{ item }">
397
+ <XhNotificationItem :id="item.id" :title="item.title" :description="item.description">
398
+ <XhNotificationItemIndicator />
399
+ <XhNotificationItemTitle />
400
+ <XhNotificationItemDescription />
401
+ <XhNotificationItemCloseTrigger />
402
+ </XhNotificationItem>
403
+ </template>
404
+ </XhNotificationGroup>
405
+ </XhNotificationRoot>
406
+ ```
407
+
408
+ 队列与卡片是同一个组件的两层:`root`(队列的作用域包装)/ `group`(某个位置上的那一摞,也是 `role=region` 的地标)/ `item` 起是单条卡片。
409
+ 九宫格落位、`max` 上限、同 id 就地改写、逐条计时与暂停都在这里。
410
+ Web Components 侧是 `<xh-notification>` 与 `<xh-notification-item>`。
411
+
412
+ 单条卡片的生命周期复用 toast 那台机器——「会自己消失的卡片」这一行为与消息来源无关。
413
+
414
+ 通知另有命令式的 `createNotificationService`:推送连接的回调、后台任务的收尾、
415
+ 拦截器里的一条系统消息,调用点都在组件之外,让它们各自去找一份队列上下文并不现实。
416
+ 队列要长在页面结构里(通知中心那一栏自己排版)时用组件形态,两者不共享队列。
417
+
418
+ **轻提示(收窄)**
419
+
420
+ - 解剖去掉 `description`:一次操作的结果一句话说得完,说不完的那是通知。
421
+ - 新增 `group` 部件:同时在场的几条叠成一摞。这一摞由全局服务渲染,没有对应的容器组件——
422
+ 反馈落在哪儿是整个服务的口径,不该让每个业务页面各挂一份容器再各自决定。
423
+ - `createToastService` 的队列改为服务内部私有,`info` / `success` / `warning` / `error` /
424
+ `loading` / `create` / `update` / `dismiss` / `dismissAll` 签名不变,调用点零改动。
425
+ 服务选项新增 `placement`(默认 `top`)、`max`(默认 5)、`gap`。
426
+
427
+ **破坏性**
428
+
429
+ - 删除 toaster:`XhToasterRoot` / `XhToasterGroup` / `useToaster` / `<xh-toaster>` /
430
+ `connectToaster` / `toasterMachine` / `toasterAnatomy` / `@xihan-ui/styles/toaster.css` 等
431
+ 一并移除。组件树内的通知队列改用 notification,命令式轻提示继续用 `createToastService`。
432
+ - toast 删掉 `description` 部件与 `getDescriptionProps`;`<xh-toast>` 的 `description` 属性同时移除。
433
+ 机器上的 `description` prop 保留——notification 的卡片复用同一台机器。
434
+ - `ToastOptions` / `ToastRecord` 不再带 `placement`:轻提示的落位归服务,不逐条各去一处。
435
+ - 覆盖槽 `--xh-toaster-inset` / `--xh-toaster-layer` 改名为 `--xh-notification-inset` /
436
+ `--xh-notification-layer`;`--xh-toast-description-*` 随部件一起移除。
437
+
438
+ ### Minor Changes
439
+
440
+ - 906b712: 真机 axe 扫出的无障碍缺陷逐条修,并把三个模态补进扫描名单。
441
+
442
+ **dialog / drawer / image-viewer 此前从没被真机 axe 扫过**:它们的 presence 模型与共享套件对不上,各自单开了一份 WC 规格,因而不在扫描名单里——而焦点陷阱、`aria-modal`、背景 inert 恰恰最该在真浏览器里验。补进名单后三者全绿。
443
+
444
+ 同一次扫描照出四类既有缺陷:
445
+
446
+ - **side-nav 折叠成图标栏后,行按钮与链接没有可及名**(critical + serious,14 条):皮肤把 `branch-text` / `link-text` 整个 `display: none`,可及名随之归零——读屏用户在折叠侧栏里完全不知道每一项是什么。改成仓内既有的视觉隐藏配方(文字仍在无障碍树里),可及名恒等于可见文本,不必再让连接层去猜名字,也不会覆盖作者自己写的 `aria-label`。
447
+ - **side-nav 的 `ul` 直接装 `a`**(serious,19 条):Vue 适配器早就偷偷包了一层没登记的 `<li>`。把它提成正式的 `item` 部件(解剖 / connect / meta / 两个适配器 / 套件 / 示例同步),与同族的 breadcrumb、anchor、navigation-menu 一致。
448
+ - **有值时下拉钮被藏掉**(date-picker / time-picker / combobox):清空钮的互斥契约此前让「清空钮顶替下拉钮」,但这三家的 `trigger` 是打开浮层的那颗按钮而不是装饰箭头——藏掉它,鼠标用户在有值之后没有入口,浮层收起时的焦点归还也会落到隐藏节点上,键盘用户当场丢失位置(真机里 Escape 后焦点掉到 `body`)。改为只有纯装饰的 `indicator` 才让位(select / cascader / tree-select 那三家),这三家的清空钮与下拉钮并排显示。
449
+ - select 的隐藏原生 `select` 在派生用例里被插了两份,第二份没有接线因而没有可及名——套件的 fixture 助手补幂等判断。
450
+
451
+ `data-name` 这类写成常量再当计算键用的属性,此前公开面采集器的正则扫不到,基线漏登记;采集器补上常量形态。新增 `check-release-tag`:标签写的版本号必须与 changesets 的 pre 模式对得上,否则打 `v1.0.0` 却发出 `1.0.0-alpha.N`、或退出 pre 后打 `v1.0.0-rc.1` 直接占掉 `latest`。
452
+
453
+ - c57542d: 补齐 4 处「边框改不动」的覆盖槽。
454
+
455
+ 这几处的边框颜色绑在背景槽上,或者干脆没有槽——想只改边框改不了,一动就连底色一起变:
456
+
457
+ - `form` 的提交按钮三态:`border-color` 直接读 `--xh-form-submit-bg`,没有 `--xh-form-submit-border`。
458
+ - `editable` 的提交按钮 hover / active:静息态有 `--xh-editable-submit-border`,另两态回落到 bg 槽,
459
+ 覆盖被顶掉。
460
+ - `table` 的行选中把手选中态:静息态有 `--xh-table-trigger-border`,选中态改用 bg 槽。
461
+ - `steps` 的禁用态指示器:全皮肤唯一一条没有对外覆盖槽的边框声明,而且拿前景令牌
462
+ `--xh-fg-disabled` 当边框色;同部件的 current / completed 两态都有各自的 border 槽。
463
+
464
+ 新增 8 个槽,都排在既有 bg 槽之前作为第一优先,未设置时求值链回落到原值——**渲染结果逐字不变**,
465
+ 既有的 `--xh-form-submit-bg` 之类覆盖照旧同时改动边框与底色。
466
+
467
+ - e12e337: 日历可以并排展示连续几个月,date-picker 的区间选择默认就是两个。
468
+
469
+ 区间的起止常常跨月,只有一个面板就得「点起点 → 翻页 → 点终点」,翻的时候还看不见起点在哪。
470
+ 两个并排是这类选择器的通行做法,也是这次补上的。
471
+
472
+ - **calendar 新增 `visibleCount`**(默认 1)与 **`panels`**:一个锚点铺出 N 个连续月,
473
+ 翻页只动锚点、整窗一起走一个月,不是各翻各的。跨年自然接上(12 月的下一个面板是次年 1 月)。
474
+ - **`getGridProps` / `getHeadingProps` 收面板下标**,每个面板一份标题 id,网格各由自己那行标题命名。
475
+ 不给下标即首个面板,旧调用一字不改。
476
+ - **`CalendarCellProps` 多一个 `index`**:同一天会同时出现在两个面板里(8 月末那几天也铺在 9 月首行),
477
+ 「是不是本月」只有连着面板一起看才判得出来。
478
+ - **往后翻的边界按整窗算**:新露出来的是窗口末尾再往后一个月。单面板时与从前逐字一致。
479
+ - **date-picker 新增 `visibleCount`**,缺省单选 1、区间 2。
480
+ - 皮肤只在 `content` 直接摆了两张日历时才横排(`:has`),并给第二张起画一道左分隔线——
481
+ `showTime` 那套结构里 content 的直属子节点是作者自己的包裹块与确认行,无条件横排会把它们并到日历旁边去。
482
+
483
+ 旧字段 `weeks` / `visibleMonth` / `headingLabel` 保留,恒指首个面板。
484
+
485
+ - ff84a16: 日历补上按月 / 季度 / 年 / 周挑,并修掉多面板下的两处硬伤。
486
+
487
+ **面板粒度 `view`**(`day` 默认 / `month` / `quarter` / `year`)
488
+
489
+ 格子的值一律是「那段时间的第一天」的 ISO 串,不另立一套值形态——min/max 比较、区间逻辑、
490
+ 不可用判定、表单出口于是全都原样复用。点 Q3 落的就是 `2026-07-01`。
491
+
492
+ - 月面板一年 12 格、季度 4 格、年面板一页十年(两端各带一格邻十年,与日视图带邻月同一套做法)
493
+ - 一页翻多久跟着视图走:日 1 个月、月与季度 12 个月、年 120 个月;翻页边界同样按整页算
494
+ - 标题按 locale 出:`2026年8月` / `2026年` / `2020年-2029年`
495
+ - 网格上多一个 `data-view`,皮肤据此换排布(月与年 3 列、季度 4 列);日视图一个字没动
496
+
497
+ **周选 `weekSelection`**:点任意一天落的是它所在的整整一周(两端一起给),周首日随 locale。
498
+ 只在 `view=day` 且区间模式下生效,其余情形照旧只落这一天。
499
+
500
+ **修:点第二个面板里的日子会整窗往后翻一页**
501
+
502
+ 视窗起点此前直接由聚焦日反推,于是点右边那个面板 → 聚焦日落到下个月 → 整窗跟着走,
503
+ 看着就像「点一下翻一页、根本选不中」。现在视窗是独立的浏览位置,只在聚焦日走出视窗时
504
+ 才挪过去,挪到刚好把它露出来的那一端。
505
+
506
+ **修:浮层展开后指针那条路没有出口**
507
+
508
+ 上一版把触发钮变成可选部件后,点输入行只能展开、不能收起——而段位里敲出来的值又不触发
509
+ 「选完即收」(那时人还在打字),于是浮层关不掉。现在点输入行是开合对称的,段上按 `Enter`
510
+ 也收起(`Alt+ArrowDown` 展开的对偶)。
511
+
512
+ - a19bbaa: 级联选择补空态兜底:新增 empty 部件,搜索无候选或 collection 为空(根列没有条目)时露面,其余时候带 hidden。
513
+
514
+ - headless:`getEmptyProps` 管空态占位的露面与收起;`getSearchListProps` 无候选时带 `data-empty`,`getContentProps` 根列没有条目时带 `data-empty`;新增 `translations` prop(`empty` / `noMatch` 两键,默认英文)与 api 上并入默认后的完整一份。
515
+ - vue:`XhCascaderContent` 自动补渲空态占位,`empty` 插槽可换内容,缺省文案按视图取无匹配或无数据;`translations` prop 接入全局 `provideXhConfig` 注入点(`translations.cascader`)。
516
+ - web-components:新增可缺省的 `empty` 部件,元素代管其 hidden,文案归作者。
517
+ - styles:空态占位居中排版(`--xh-cascader-empty-min-h` / `--xh-cascader-empty-p` / `--xh-cascader-empty-fg` 可覆写);无候选时候选列表不再占位,根列没有条目时空列让位。
518
+
519
+ - 089db90: 清空 / 关闭 / 移除按钮收成四类契约(`开发设计/UI.ClearTrigger.Contract.md`),`check-clear-trigger` 门禁固化。
520
+
521
+ **内嵌清空钮**(cascader · tree-select · combobox · date-picker · time-picker · text-field · tags-input · select,以及新增部件的 popselect · date-field · time-field)统一为:`tabindex=-1` 不占 Tab 位但**不再 aria-hidden**——读屏按 `aria-label` 找得到它,文案统一走 `translations.clearTrigger`(缺省 `'Clear'`;select 的 `clear` 键改名);pointerdown 不夺焦,点完发 `VALUE.CLEAR` 并把焦点送回宿主(trigger / input / 第一段);没值就 `hidden`,不再同时打 `disabled`/`data-disabled`、皮肤也不再留一颗永远看不见的灰钮;尺寸与圆角统一为 `var(--xh-<c>-action-size, var(--xh-control-action-size))` / `var(--xh-<c>-action-radius, var(--xh-shape-control))`——text-field 此前与输入框等高、select / tags-input 按指示符尺寸走 pill,`--xh-text-field-clear-*` / `--xh-tags-input-clear-*` / `--xh-select-clear-*` 槽改名 `action-*`;互斥一律由 connect 在被让位的部件上打 `data-clearable`、皮肤一条 `display: none`——select 去掉了 `:has()` 让位与 `:hover` 才显形(触屏此前根本看不到清空钮),清空钮改为 trigger 的兄弟并排(`--xh-select-control-gap`)。
522
+
523
+ **键盘清空**:select · cascader · tree-select · popselect 此前没有任何键盘清空路径。现在焦点在 trigger、有值且可编辑时 **Delete 清空全部、Backspace 单选清空 / 多选去掉最后一个**,键盘表与一致性套件同步。
524
+
525
+ **select** 补 `readOnly`(浮层照常展开、值改不动、清不掉)与 `VALUE.CLEAR` 事件(`api.clear()` 不再借 `VALUE.SET []`);Vue 的 select / combobox Root 新增 `clearable`(缺省 false)决定 collection 自动渲染树是否带清空钮——combobox 此前无条件渲染,示例已补 `clearable`。
526
+
527
+ **独立动作钮**(file-upload · signature-pad):file-upload 的 `api.clearFiles()` 改名 `clear()`、`translations.clearFiles` 改名 `clearTrigger`;列表为空时不再原生 disabled(清完焦点会掉回 body),只打 `data-empty` 压淡。
528
+
529
+ **浮层关闭钮**(dialog · drawer · popover · tour · toast · alert · floating-panel · image-viewer)统一 `var(--xh-<c>-close-size, var(--xh-control-h-sm))` / `var(--xh-<c>-close-radius, var(--xh-shape-control))`,dialog / drawer / popover / tour 补上使用者槽;image-viewer 保持 `--xh-control-h-lg`(全屏看片的 chrome 钮按触控靶走)但圆角归 control。**标签内移除钮**(tag · tags-input item · select tag)尺寸基准 `--xh-control-indicator-size`、圆角 `--xh-shape-inset`;行级删除钮(file-upload item · dynamic-input)按 `--xh-control-action-size` / `--xh-shape-control`。
530
+
531
+ 四类按钮都补了 `:active` 按压反馈(`--xh-motion-scale-press`),27 处登记进 `check-press-feedback`。
532
+
533
+ `--xh-select-clear-*` / `--xh-tags-input-clear-*` / `--xh-text-field-clear-*` 共 20 个槽名变更是公开面删减,基线已推。
534
+
535
+ - 72dc39c: color-picker 能进 HTML 表单了。
536
+
537
+ 此前它既没有 `name` prop 也没有表单影子——放进 `<form>` 里提交,`FormData` 里没有这个字段。
538
+ 同仓 11 个组件早就做全了这件事,它是缺口之一。
539
+
540
+ 照仓内既成的形状补:新增 `hidden-input` 部件(`type=hidden`,排在解剖末位)、`name?: string` prop、
541
+ `ColorPickerApi.getHiddenInputProps()`。影子产出的属性恰好五条——parts 属性、`type`、`name`、`value`、
542
+ `disabled`——`type` 必须排在 `value` 前(改 type 会重置输入的值),`name` 不给就整条不产出、这份输入
543
+ 不参与提交,禁用时带原生 `disabled` 不提交值,只读照常提交。
544
+
545
+ **这是纯增量**:影子是作者自己写的可选部件(Vue 侧新增 `XhColorPickerHiddenInput`,WC 侧新增
546
+ `::part(hidden-input)`),不写它就不存在,既有 DOM 与皮肤选择器一个字节不变。
547
+
548
+ - 4748212: 控件边界切到 `border.control`,WCAG SC 1.4.11 的 3:1 第一次真的达标。
549
+
550
+ **这是一次观感变更**:输入框、选择器、复选框、单选、开关、步进钮这一类控件的边框会比以前明显一点
551
+ (浅色 1.26 → 3.23,深色 1.91 → 3.59),悬停档 4.73 / 4.18。分隔线、卡片描边、表格行线、浮层外框
552
+ 一律不动——它们不在 SC 1.4.11 的范围内,跟着变重只会毁掉版面。
553
+
554
+ 131 处改动,判定规则是可机械执行的四问,其中决定性的一问是**焦点环画在谁身上**:环向下委派给
555
+ 后代(`outline: none` 且后代另有画环规则)的盒子是取景框不是控件,它的边框不承载「不看清就做不成事」
556
+ 的信息。`table` 的 root 与 `listbox` 的 content 因此同判装饰——两份文件里各自写着的注释就是依据
557
+ (「纯容器:焦点落它身上不画环,高亮永远长在行上」/「落焦不画环:高亮永远长在条目上」)。
558
+
559
+ **三处二阶效应,逐条处理过:**
560
+
561
+ - `splitter` 的分隔条静息走 `background` 取 `border.default`,而它的悬停走的是 `bg-*` 族。只迁静息会让
562
+ 悬停比静息更淡,所以这一处不迁。
563
+ - `text-field` / `number-field` 的**聚焦态描边**兜底仍是 `border.default`。静息提到 3.23 之后,控件一被
564
+ 聚焦边框反而掉回 1.26——比静息淡一大截。8 条聚焦槽一并迁走(含 subtle / ghost 两个变体:它们静息是
565
+ `transparent`,聚焦那道边就是当下唯一的边界)。
566
+ - `transfer` 的搜索框 `border: 0` 只留一条下边线,而它与面板共用 `--xh-transfer-panel-border`。新增
567
+ `--xh-transfer-search-border` 单独承载,默认控件级。
568
+
569
+ 写了一份倒挂检查:13 个私有边框槽族 × 四个主题档位(浅、深、浅+高对比、深+高对比),逐档比对
570
+ 静息 / 悬停 / 聚焦的权重必须单调不减,现在零倒挂。
571
+
572
+ **仍未达标、如实记账的一处**:带语气的 outline 形态(`<XhButton variant="outline" tone="danger">` 这类)
573
+ 走的是 `--xh-_tone-border`,它是语气色兑 40% 底色的结果,六种语气在两套主题下是 1.44–2.18,新令牌够不着。
574
+ 要治得另立一支控件级的语气边框槽,而那一支里 `warning` 对白底只有 2.70,仍需单独裁定——留待下一轮。
575
+
576
+ - 7f8021e: 日期区间的框选改成逐行横杠,面板数按区间跨不跨页现算,面板号写在日历上一处即可。
577
+
578
+ **区间底色画成了一整块实心方块。** 底色铺在格子的背景上,格子上下的内衬也算背景区,
579
+ 而行与行之间没有间距——七月一整月被选中时,五行底色首尾相接连成一个大方块,
580
+ 两端那两枚圆点像是被按在方块上,看不出区间是一天一天连起来的。
581
+
582
+ 底色改由格子的 `::before` 铺:横向铺满格子,相邻两格接成一条;纵向收在格子内衬里,
583
+ 行与行之间留出 4px 空当。每一行的行首与行尾各自收圆,跨周的区间于是是一行一条两头圆的横杠。
584
+ 摆了周序号格的行里,行首那一格排在周序号后面,圆角跟着落到它身上。
585
+
586
+ **两端那一格只铺半格**,另外半格由选中圆片占满:区间收在圆点上而不是收在格子边上。
587
+ 起止落在同一天时两条一起生效,底色宽度归零,只剩那枚圆点。
588
+
589
+ **邻月的日子不再吃区间底色与选中圆片。** 并排两张面板里同一天会各出现一次
590
+ (7 月 31 日既在七月的末行、也在八月的首行),两张都画就成了两个端点、两段底色。
591
+ 邻月的日子回到「压暗的数字」这一档。
592
+
593
+ **粗粒度视图的邻月判定修正。** 月/季度/年三档里格子的值是那一段的第一天,与面板起点比月份恒不相等,
594
+ 于是除首格外整页都被判成邻月、整页压暗。这三档改用网格自报的 `inView`。
595
+
596
+ **区间默认铺几个面板改成现算**:已选的两端落在同一页里就一张,跨页才并排两张;
597
+ 只落了一端(还在挑)时仍按两张算。日历同时恒渲染六行(新 prop `fixedWeeks`,默认开),
598
+ 并排的两张面板等高,翻页时浮层高度也不再跟着月份变。
599
+
600
+ **面板号写在 `XhDatePickerCalendar` 上一处即可**:新增 `index` prop,面板内的
601
+ `Heading` / `HeadingYearTrigger` / `HeadingMonthTrigger` / `Grid` / `Cell` 不写就跟着它走,
602
+ 自己写了仍按自己写的算。此前这五个部件各要写一遍,漏掉任何一个都会静默落到面板 0——
603
+ 两张面板显示同一个月份、第二张面板的邻月判定整片错位,都是这么来的。五个 prop 一并兼收字符串。
604
+
605
+ **快捷选项列的高度由并排的日历给。** 此前这一列按内容收、上限写死一档,
606
+ 右侧那道分隔线只画到最后一条选项,比日历矮一截;它与旁边那张日历之间也补上了与两张日历之间同样的空当。
607
+
608
+ - e2292bf: date-picker 与 time-picker 补上三条视觉轴:`variant` / `tone` / `size`。
609
+
610
+ 这两个组件此前是全仓仅有的两处「有输入行却没有形态轴」——同一张表单里,
611
+ 文本框、数字框、分段日期、分段时间都能换档,唯独这两个换不了,只能靠覆盖令牌硬凑。
612
+ 它们各自内嵌的 `date-field` / 分段时间输入早就有三轴,缺的一直是外层这一份。
613
+
614
+ 轴的落法与全仓一致:三个属性只写在 `root` 上,输入行、日历格与浮层里的列都从那里继承皮肤声明的私有槽,
615
+ 所以换一档不必给每个部件各写一条选择器。
616
+
617
+ 皮肤同步把两份里原先散着的写死值收成私有槽:
618
+
619
+ - 尺寸档换 `control-h` / `control-px` / 两档字号(time-picker 还多一个列表格子的内边距)
620
+ - 形态档换底色与两档描边;输入类照例不做实心档——填满一个要往里打字的框,字与底没法同时读
621
+ - 语气只落在聚焦环、段位反白、时间列选中与确认按钮上,正文与日期数字不归它管
622
+
623
+ 不写这三个属性时一个 `data-*` 都不产出,皮肤走缺省档,观感与之前逐像素一致。
624
+
625
+ - 0be028c: 抽屉可以挂在页面里的某一块区域上了,`portalContainer` 也不再是个死字段。
626
+
627
+ `RuntimeConfig.portalContainer` 自打声明起就没人读过——全部浮层的搬运目标一律写死 `'body'`,
628
+ 所以「局部抽屉」根本做不出来。这次两头一起接:
629
+
630
+ - **drawer 新增 `contained`**:遮罩与定位层从 `fixed` 换成 `absolute`,只罩住最近的定位祖先而不是盖满整屏。
631
+ `data-contained` 同时落在 root / backdrop / positioner / content 上,页面里那半边与被搬走的那半边都能选到。
632
+ - **Vue 新增 `container`**(选择器或元素):浮层搬进那个容器,并**隐含 `contained`**——
633
+ 一处给定、两件事从它派生,不会出现「搬进去了但还画着全屏遮罩」这种两边各说各话。
634
+ 显式写了 `contained` 以显式的为准。
635
+ - **`portalContainer` 真正接上**:`XhConfig` 多一个同名字段,应用级注入一次,
636
+ 没写 `container` 的浮层就落到它给的容器里;都没有才落 `body`。
637
+ - **Web Components** 是 Light DOM,作者写在哪浮层就在哪,因此只需要 `contained` 这一个属性来让皮肤按容器画。
638
+
639
+ 那个容器要自己带 `position`(`relative` 之类),否则 `absolute` 会往上找到别的定位祖先——
640
+ 这一条写进了 props 说明与示例。
641
+
642
+ - f154e07: 组件自带的兜底字形改为真正的图标:勾、半选横杠、展开箭头、清空与关闭的叉、排序方向、加减号、翻页箭头、图片查看器工具条这些,原先要么是皮肤里的 Unicode 字符(`✓ ▾ ✕`,跨字体跨系统长得各不一样),要么由作者在每个部件里手打一个字符。现在统一走 `--xh-glyph-mark-*` 一族二十个令牌,取值是图标包里对应 SVG 的 `url("data:image/svg+xml,…")`,皮肤拿它当 `mask-image`、用 `currentColor` 着色——随语气、悬停、禁用自动变色,与 `<XhIcon>` 画出来的一模一样。令牌的 `$type` 为 `icon`、`$value` 是图标名,构建期从图标包读 SVG 内联,改图标只改一处。
643
+
644
+ 使用者换图标有两条路:在 `:root` 上重声明令牌即全局换,写在任意容器上即只换那一块(任何 SVG 都行,着色一样走 `currentColor`);或者往部件里放自己的节点,皮肤那条 `:empty` 守卫即不命中。兜底覆盖面从 14 份皮肤扩到 39 份:此前 tree / tree-select / table / toast / dialog / drawer / number-field / carousel / transfer / image-viewer 等二十个组件的把手空着就什么都不画,文档示例只好逐个手打字符;现在示例里的 960 处手打字符全部删掉,由皮肤画。命令式 toast / dialog 的类型徽记与 `XhToastCloseTrigger`、`XhImageViewer*Trigger` 的默认内容同样改走这族令牌。
645
+
646
+ 图标包新增 `info` / `rotate-left` / `rotate-right` / `flip-horizontal` / `flip-vertical` 五枚。`check-glyph-slots` 门禁禁止皮肤里再写字面字形,并双向核对令牌与用处(适配器里的 JS 默认模板也算)。
647
+
648
+ - 1e90ce6: 热力图新增 `palette` 色板轴:`green` / `blue` / `orange` / `purple` / `red` / `gray`,直接按颜色点名色阶满档那一端,三种形态与图例一起跟着走。它是装饰性的一条轴,不是第四条语义轴——与 `tone` 同时写时听色板的,两条都压不过作者自己写的 `--xh-heatmap-ink`;不写时行为与之前逐字一致。
649
+
650
+ 令牌层随之补上紫色原语 `--xh-color-purple-600`:明度与彩度照 danger 的 600 档,只把色相换成 302。
651
+
652
+ - 091bbef: 补上动效地基的四个缺口。
653
+
654
+ **减弱动效此前基本是失效的。** `tokens.css` 里一个 `prefers-reduced-motion` 都没有,降级靠 19 份皮肤各写各的 `@media`,而它们只把 `animation-duration` 压到 `0.01ms`——位移与缩放是写死的字面量,压时长压不掉。前庭不适恰恰来自大位移与缩放,所以「减弱动效」的用户看到的是瞬间跳完整段位移。现在幅度走 `--xh-motion-distance-sm/-md` 与 `--xh-motion-scale-enter`,令牌层在 reduce 下把它们归零,皮肤不必自带 `@media`。删掉 8 份已经冗余的降级块(含 8 条 `!important`);marquee / skeleton / spinner 那几处有讲得通的自定义降级,保留。
655
+
656
+ **dialog 与 image-viewer 的退场动画从来没播过。** 皮肤给挂着退场动画的 `content` 补了 `[hidden]{display:none}`,收起时元素当场不生成盒子,动画不启动,退场探测器放弃申领租约、就地卸载。drawer 早就绕开了这个坑,它的注释还写着「与 dialog 一致」——而 dialog 恰恰是反的。现在真的一致了,四条退场动画同时补上 `forwards`。
657
+
658
+ **Web Components 端全域没有退场动画。** 三个浮层元素把收起写死在展开态上,与 `data-state="closed"` 同帧写内联 `display:none`。现在收起跟着 presence 走;Light DOM 下被拉长的不是节点存在的时间,而是可见的时间。
659
+
660
+ **破坏性程度**:进场缩放统一到 `0.96`(此前 0.98 与 0.96 混用),dialog / toast 进场 / color-picker 的起势略明显一点。button 的加载转圈不再被压成 `0.01ms`——转圈是「系统还在做事」的唯一可感知信号,压掉等于把加载态变成假死。
661
+
662
+ 回归测试进了 `tests/browser/`:jsdom 不把样式表里的 animation 算进 `getComputedStyle`,这三件事在 jsdom 里结构性测不到。
663
+
664
+ - 689ed0f: 13 个宿主的滚动层自带自绘滚动条:滚动时或指针在这一片时露出、静止后收起,浮在内容之上不占宽度。
665
+
666
+ **哪些宿主**:12 个浮层族的 `content`(cascader / color-picker / combobox / context-menu / date-picker / hover-card / mention / menu / pagination / popover / popselect / tree-select)与 json-viewer 的 `tree`、`text`,共 14 个滚动容器。条子由库自己建,作者一个部件都不用写:它是滚动层的兄弟,绝对定位贴在组件既有的壳上(浮层族是 `positioner`,json-viewer 是 `root`)。轴按各自的溢出方向摆——cascader 只摆横的,tree-select 与 json-viewer 竖横都摆、两条都溢出时各让出交叉口那一格,其余只摆竖的。
667
+
668
+ 挂上条子的容器带 `data-xh-scrollbar`(挂在它身上的条数),皮肤据此把原生条藏成零宽:容器的可用宽度一点不减,也不再需要为原生条留空道。露面时机、尺寸档、拖动、触屏交给原生滚动这些全是 `scrollbar` 那一套,与手写 `<XhScrollbar>` / `<xh-scrollbar>` 挂上去的完全一致,缺省档是 `scroll-hover`。
669
+
670
+ **json-viewer 换档跟随**:树档与原文档互斥,换档时条子跟到此刻在场的那个容器,节点不重建(换档不会把滚动条闪一下)。
671
+
672
+ **按在 `positioner` 上不再消解浮层**:条子住在 `positioner` 里、是 `content` 的兄弟,浮层的层分支因此把 `positioner` 一并记上——不记的话按住条子拖动那一下会被判成层外交互,面板当场收起。副作用是 `positioner` 的其他子节点也算进了层内:吃指针的只有 combobox 的 `empty` 空态占位,按它不再关闭候选面板(此前会关)。其余 11 个浮层的 `positioner` 除了条子没有吃指针的子节点(`positioner` 自身是 `pointer-events: none`),按在面板之外仍照旧消解。
673
+
674
+ **皮肤侧要跟着改的**:自带皮肤给这 13 个壳补了 `--xh-scrollbar-track-bg: transparent`(浮在内容上的条子不该有实色轨道),json-viewer 的 `root` 补了 `position: relative`(条子贴它的内边距盒)。第三方皮肤若整份接管这些 part,同样要给壳一个定位上下文,并把轨道底色关掉。滚动条自身的 `root` 补了 `pointer-events: auto`,抵消 `positioner` 那句 `none`。
675
+
676
+ - 843e17a: json-viewer 补原文视图:`view="text"` 直接出缩进过的 JSON 原文。
677
+
678
+ 树档是拿来"翻"的——折叠、逐层看结构;而"核对这份报文与后端下发的是不是一字不差"、
679
+ "把它整段拷走"这两件事树档做不到:值受 `maxStringLength` 截断、成员受 `maxItems` 折减,
680
+ 分支摘要与把手还带 `user-select: none`,框选拿到的不是原文。原文档就是补这一件事,
681
+ 因此它刻意不吃那两个折减选项。
682
+
683
+ `api.text` 在两档下都取得到,作者要做"复制原文"按钮时不必自己再序列化一遍。
684
+ 序列化与树同源:同一个 `jsonEntries` 排键(`sortKeys` 一样生效)、同一条祖先链判环
685
+ (环落成 `"[Circular]"`,两条不相干分支共享同一个对象照样摊开),
686
+ `bigint` / `undefined` / 函数这些 JSON 没有写法的值退回树上那份文本并按字符串写出,
687
+ 整份始终解析得动。
688
+
689
+ 新增 headless 出口 `jsonText` 与类型 `JsonViewerView`,解剖新增 `text` 部件。
690
+ 皮肤与树档共用同一套边框、内衬与高度令牌,两档切过去盒子不跳。
691
+
692
+ - ec93d6b: 浮层里的条目之间加 2px 行距,新增语义令牌 `--xh-list-option-gap` 统一这把尺。
693
+
694
+ **下拉里选中项与悬停项贴成一整块。** a11 的选中蓝底与 b22 的悬停灰底之间没有一丝缝,
695
+ 两块底色首尾相接,读起来像一条被涂了两截颜色的长条而不是两个条目。
696
+
697
+ **库内自己就有三种方言**:浮层选项列(time-picker / date-picker 的时间列与预设列)已经是
698
+ 2px,页面导航列(side-nav / navigation-menu)是 4px,下拉、菜单、树这一族是 0。补上 2px
699
+ 是把这一族拉回库内既有的口径。
700
+
701
+ `list` 组的描述原文写着「option-\* 给浮层里的条目——菜单项、下拉选项、树行、时间列」,
702
+ 新令牌落在这一组:`--xh-list-option-gap: 2px`。compact 档不覆盖,2px 已是最小档。
703
+
704
+ 22 个条目的直接父容器接上这把尺:select 的 `list`;combobox / listbox 的 `content` 与
705
+ `item-group`;popselect 与 mention 的 `content`;menu / menubar / context-menu 的 `content`
706
+ 与 `group`;cascader 的 `column` 与 `search-list`;tree 与 tree-select 的 `tree`、
707
+ `branch-content`、`branch`;transfer 的 `list`。装 list 加 footer 的外壳(select /
708
+ tree-select 的 `content`)不接——它不是条目的父层。json-viewer 也不接,只读数据视图与
709
+ table、log 同为紧排一档。
710
+
711
+ `tree` 与 `tree-select` 的 `branch` 此前是块盒,为接这把尺改成纵向 flex,tree-select 同时
712
+ 补上此前缺的 `[hidden]` 兜底。
713
+
714
+ 节奏顺手收一级,加了 gap 之后总量不变:combobox 与 listbox 的组间距 8px → 6px,
715
+ menu / menubar / context-menu 的分隔线外边距 4px → 2px。time-picker 那两处等值的
716
+ `--xh-space-0_5` 改指新令牌,视觉不变。
717
+
718
+ 这把尺打在容器上,所以分组标题与它下面第一条之间同样多出 2px——分组标题是 `group`
719
+ 的第一个子元素,与条目同属一层 flex 子项。
720
+
721
+ - 8d35702: 动效与浮层口径收口(`开发设计/UI.MotionOverlay.Contract.md`)。
722
+
723
+ **减弱动效只剩一条通道。** 此前 kernel 的 `RuntimeConfig.reducedMotion` 只读系统 matchMedia、motion 包的 `setMotionOverride` 只有 animate / 滚动 / 数字动画在听,presence 与 stick-to-bottom 感知不到应用级覆盖;无 matchMedia 的宿主两包还给出相反答案(kernel 直接抛 TypeError、motion 报 reduce)。现在 kernel 依赖 motion,`reducedMotion` 缺省即 `resolveMotionPreference() === 'reduce'`(覆盖 ?? 系统偏好),没有 matchMedia 一律不减弱;glyph 转圈、backgrounds、滚动、数字动画全部走同一函数。CSS 侧 `tokens.css` 新增 `:where([data-motion='reduce'])` 块,与 `@media (prefers-reduced-motion: reduce)` 同源生成、逐条相同——作者把 `data-motion="reduce"` 打在任意容器即局部减弱。全局配置加 `motion?: 'reduce' | 'no-preference'`,Vue `provideXhConfig` / WC `<xh-config motion>` 收到即调 `setMotionOverride`。
724
+
725
+ **缓动与时长的真源是令牌。** motion 包新增 `durations = { fast, normal, slow }`,`animate()` 缺省与 `@xihan-ui/animations` 的缺省时长都引它;`check-motion-source` 比对 primitive.json 与 easing.ts / durations.ts,值不等即红;`check-reduced-motion-channel` 禁止 motion 包之外再出现 `matchMedia('(prefers-reduced-motion')`。
726
+
727
+ **皮肤的 reduce 块归口。** 只在两种情况自写:无限循环动画要整个停掉、有使用者时长槽的过渡要兜住穿透。image-viewer / side-nav / layout 三份纯重复令牌层的块删掉;table 的 `0.01ms !important` 改 `animation: none`;保留的 10 份每块配一份等价的 `[data-motion='reduce']` 规则。animation / transition 不再直引 `--xh-duration-*` 原语:spinner 走 `--xh-spin-duration`,skeleton 走新令牌 `--xh-shimmer-duration`(1600ms)。`check-infinite-motion` / `check-motion-primitives` 守住。
728
+
729
+ **浮层的 placement / offset 默认值只有两种语义。** `OVERLAY_PLACEMENT_ANCHORED = 'bottom'`(气泡类)与 `OVERLAY_PLACEMENT_LIST = 'bottom-start'`(列表类)、`OVERLAY_OFFSET = 8` 从 headless 共享导出,各组件的 `<C>_DEFAULT_PLACEMENT` 改为引用它们(tooltip / hover-card / popover / popconfirm / popselect 新增导出常量),所有机器显式传 offset,不再隐式靠引擎兜底;`check-overlay-defaults` 守住。
730
+
731
+ **层级覆盖槽齐全、后缀统一。** 22 个浮层族的 positioner / backdrop、toaster、navigation-menu 面板都有了 `--xh-<c>-layer` 槽(缺省仍是 `--xh-layer-*`);tour / table / heatmap 的 `-z` 后缀槽改名 `-layer`(7 个,公开面变更,基线已推)。
732
+
733
+ **进退场对称。** toast 退场位移从 distance-sm 改 distance-md(与进场、与 dialog 一致);tour 的气泡改用 pop 族,聚光灯补退场;side-nav 折叠态弹出面板补进退场并在 Vue / WC 接上退场租约。
734
+
735
+ **navigation-menu 的定位登记变成可验证的。** 三道浮层门禁此前按「anatomy 有 positioner」发现族,它从没被检查过;现在 `SKIN_POSITIONED` 名单要求它没有 positioner、不接引擎、面板由皮肤 absolute 排布,任一条不成立即红。`check-arrow-geometry` 增比对 JS 箭头常量(8·√2 / 8)与令牌(8px 边长 / 8px 圆角)。
736
+
737
+ - 239eb5d: 浮层箭头改为指向锚点,不再钉死在浮层中点。
738
+
739
+ 定位结果新增箭头落点:`PositionResult.arrow` 给出箭头中心距浮层起始缘的距离(上下两侧给 x、左右两侧给 y),由调用方在 `PositionOptions.arrow` 里交出箭头的尺寸与让开圆角的余量才计算,不要就缺席。落点算在翻面与挪位之后,两者的位移因此自动带上;锚点落在浮层之外时钳到最近的合法点。
740
+
741
+ 六个带箭头的浮层(popover / tooltip / hover-card / menu / context-menu / tour)接上这条链路:机器把箭头的量交给引擎,连接层把落点写成内联自定义属性,皮肤消费它、引擎没给时退回原来的居中。此前只要 placement 带 `-start` / `-end` 对齐、浮层比锚点宽、或引擎为避让把浮层挪了位,箭头就指向空处。
742
+
743
+ tooltip 的箭头补了 `data-placement`,皮肤的四条侧向规则从挂祖先 positioner 改为挂箭头自己,与其余五个统一。
744
+
745
+ - 8fc5f05: 上一页 / 下一页默认就是两枚箭头。
746
+
747
+ 原先库里一个字都不产出,可见内容全靠作者往插槽里塞——于是每份示例都手写了
748
+ 「上一页 / 下一页」四个字,翻译、宽度与图标风格全归使用者自己操心。
749
+
750
+ 皮肤补上兜底字形:两个把手为空时各画一枚 chevron,走既有的 `--xh-glyph-mark-*` 令牌
751
+ (mask + currentColor,跟着语气、悬停与禁用一起变色)。rtl 下两枚对调,指向行进方向。
752
+ 作者往部件里塞了自己的图形或文字,`:empty` 即不命中,原样让位。
753
+
754
+ 读屏名字一如既往来自 `translations.prevTrigger / nextTrigger`,不受影响——
755
+ 去掉的只是可见文字,不是可及名字。
756
+
757
+ - 1a36b7e: 省略号能摊开了:折进去的那几页现在有路走到。
758
+
759
+ 原先省略位是 `aria-hidden` + `pointer-events: none` 的死占位,而 `pages` 序列
760
+ 只说「这里折了一段」,说不出折的是哪几页——那几页除了手打跳页输入框没有任何入口。
761
+
762
+ 分页因此升级成浮层族,新增 `positioner` 与 `content` 两个部件:
763
+
764
+ ```vue
765
+ <XhPaginationRoot v-slot="{ pageItems }" :count="2000" :page-size="10">
766
+ <template v-for="item in pageItems">
767
+ <XhPaginationEllipsis v-if="item.type === 'ellipsis'" :side="item.side" />
768
+ <XhPaginationItem v-else :value="item.value">{{ item.value }}</XhPaginationItem>
769
+ </template>
770
+ <XhPaginationPositioner>
771
+ <XhPaginationContent v-slot="{ pages }">
772
+ <XhPaginationItem v-for="p in pages" :key="p" :value="p">{{ p }}</XhPaginationItem>
773
+ </XhPaginationContent>
774
+ </XhPaginationPositioner>
775
+ </XhPaginationRoot>
776
+ ```
777
+
778
+ - 新增 `api.pageItems`:与 `pages` 同一串序列,但省略位带着被折叠的那几页。
779
+ `pages` 由它派生,两者的窗口数学只有一份。旧的 `pages` 写法一行不用改。
780
+ - 悬停摊开(`openDelay` / `closeDelay`),**点一下也摊开**——纯悬停会把键盘用户挡在外面。
781
+ Escape 与点外面都能收起(走消解层)。
782
+ - 至多两个省略位,用 `side`(`'start' | 'end'`)区分;同时只开一个,一份定位层就够。
783
+ Web Components 侧由作者在节点上写 `side="end"`,与页码按钮自报 `value` 同一套写法。
784
+ - 浮层 portal 到统一落点,三视觉轴在 `positioner` 上重打一遍。
785
+
786
+ **破坏性**:`getEllipsisProps()` 改为收 `{ side }`;省略位从 `<span>` 变 `<button>`、
787
+ 不再带 `aria-hidden`。
788
+
789
+ - 911d0b7: 每页条数控制器随分页一起给了。
790
+
791
+ ```vue
792
+ <XhPaginationPageSizeSelect v-slot="{ options }">
793
+ <option v-for="o in options" :key="o" :value="String(o)">{{ o }} 条 / 页</option>
794
+ </XhPaginationPageSizeSelect>
795
+ ```
796
+
797
+ 用**原生 `<select>`** 而不是再造一个浮层:档位就那么几档,浮层带不来什么,
798
+ 却要多接一层定位、消解与键盘;原生控件在 Web Components 侧也一样能用,键盘天然可达。
799
+ 不给插槽时按 `pageSizeOptions` 渲染默认档位。
800
+
801
+ 受控时会把 DOM 的选中项同步回填:宿主不写回的话,用户改过的原生 select 与真正生效的
802
+ 档位会对不上,而 vdom 那边没有变化就不会打补丁——这一条两个适配器共用。
803
+
804
+ - d738f78: `date-picker` 与 `time-picker` 新增快捷选项:给 `presets` 数据就在浮层里多排一列(「今天」「近 7 天」「此刻」这类),点一条整份写进值。新增 `presets` / `preset` 两个部件、`getPresetsProps` / `getPresetProps` 两个产出与两条键盘行;这一列自成一套 listbox 键盘,与日历网格、时分秒那几列互不抢键。
805
+
806
+ 单日的值就是一条 ISO 日期串,区间用 ISO 8601 的区间写法把两端拼起来(`2026-08-15/2026-08-21`),一个串同时充当这一项的身份。日子由使用者算好传进来——连接层每帧求值,`today()` 放进渲染期会跨零点算出两个答案;headless 备了 `datePickerPresetDay` / `-Range` / `-Month` / `-Year` 与 `timePickerPresetNow` 五个纯函数。
807
+
808
+ date-picker 的收起沿用 `closeOnSelect` 那条守卫(区间要两端齐、showTime 仍由确认按钮收口);time-picker 的快捷选项给的是整份时间,写完即收。
809
+
810
+ - a41b931: 进度条新增环形与仪表盘两种形态。
811
+
812
+ - 新增 `variant` 轴:`line`(缺省,行为逐字不变)/ `circle` / `dashboard`,以及 `canvas`(承载环的 svg)与 `label`(环心那一块)两个可缺省部件。
813
+ - 新增 props:`strokeWidth`(环的线宽,viewBox 单位,缺省 6)、`gapDegree` 与 `gapPosition`(仪表盘的缺口,缺省 75 度朝下)、`valueText`(进度不是百分比时给读屏念的那句话)。线宽是 prop 不是令牌——它改的是几何,半径要跟着往里收;线形的厚度仍走 `--xh-progress-thickness`。
814
+ - 环的直径、底槽色、进度色与端点形状走令牌(`--xh-progress-size` / `-track` / `-range` / `-linecap`),几何由连接层算好写进标记,皮肤只上色。
815
+
816
+ 顺带两处修正:
817
+
818
+ - 退化输入不再算成满进度:`max` 不为正或不是数时回落 100,`value` 不是数时按 0 处理(此前 `max=0` 会让进度算成满格)。
819
+ - 线形的长度不再取整:`value=3 / max=8` 由 38% 改为 37.5%,相邻两档不会再看起来一样长。
820
+
821
+ - 9548330: 新增 `scrollbar` 组件:自绘滚动条,挂在**任意一个**滚动容器上——表格的滚动盒、虚拟滚动的视口、随手一个 `overflow: auto` 的 div 都行,不必是本组件的后代。此前这套东西焊在 `scroll-area` 里,只有连视口带内容一起交出去的场景用得上。
822
+
823
+ 解剖 `root` / `track` / `thumb` 三层必需、`corner` 可选(横竖两条同时摆着时写在其中一条里补交叉口,配合 `gutter` 让两条各自让出那一格);四种露面时机(`auto` / `always` / `scroll` / `hover`)带收起延时;拖滑块、点轨道跳转、RTL 双向换算、滑块像素下限、成段的 `scroll-start` / `scroll-end` 与 `drag-start` / `drag-end` 都在库里。`focusable` 打开后滑块进 Tab 序、报 `role="scrollbar"` 与三个 `aria-value*`,方向键 / 翻页键 / Home / End 可用;缺省不进 Tab 序也对读屏隐藏——滚动本身由滚动容器报,同一件事没必要报两遍。触屏(粗指针)上默认交给原生滚动,整条不画并带 `data-native`,`forceVisible` 打开才画。收起不再打 `hidden`,而是 `data-state=hidden` 由皮肤淡出(`visibility` 随退场播完才收),露出同样淡入;根上另有 `data-hover` 标指针在不在这一片。
824
+
825
+ **`scroll-area` 改由 `scrollbar` 组装。** 滚动区不再有自己的机器:它是视口加两条 scrollbar——`scrollbar` 角色节点是那条滚动条的挂载点、同时充当它的根,里面照 scrollbar 的写法摆 `track` / `thumb` / `corner`(戴 `data-scope="scrollbar"`),显隐、拖动、键盘、几何、触屏原生、淡入淡出全是 scrollbar 那一套,两个组件共用一份滚动条。Vue 新增 `XhScrollAreaTrack`;交叉口 `corner` 改写在竖条的挂载点里,两条都显形时才露;`scroll-area` 新增 `size` / `forceVisible`;视口的占道改打在视口自己身上(`data-lane-vertical` / `data-lane-horizontal`),不再依赖 `:has()`。原 `--xh-scroll-area-thumb-*` / `-bar-*` / `-corner-bg` 那几个槽随之归到 `--xh-scrollbar-*` 名下;`scrollAreaMachine` / `ScrollAreaSchema` / `SCROLL_AREA_*` 导出不再有,连接层改收两台 scrollbar 机器与 props(`scrollAreaScrollbarProps` 给出每台的 props)。挂了自绘滚动条的容器带 `data-xh-scrollbar`(挂在它身上的条数),皮肤据此藏掉原生滚动条的外观——表格放进滚动区即可滚(吸顶表头与吸附列钉在视口上),虚拟滚动的视口给个 id 用 `controls` 挂上即可。
826
+
827
+ 滚动容器换了会自动把监听挪过去(`scrollable` / `controls` 指向另一个节点、或条件渲染的容器重建);查不到时投一条 `scrollbar.missing-scrollable` 诊断,不静默,容器后到时调一次 `api.measure()` 即接上。容器里内容长短变了会自动重量(`MutationObserver` 盯着子树,一拍内合并成一次),量不到的场合另有 `api.measure()`。
828
+
829
+ - abe790b: 滚动条新增 `scroll-hover` 档,并把它定为缺省档。
830
+
831
+ **新增 `'scroll-hover'`**:滚动时露出,指针进入滚动容器或滚动条时也露出;指针占着容器时滚动只重画滑块、不起收起倒计时,指针离开或停手满 `hideDelay` 才收起。它是 `hover` 与 `scroll` 两档显形条件的并集,与那两档一样浮在内容之上——`data-lane-*` 的判据只认 `auto` / `always`,视口宽度一点不减(横条同理不占高度)。
832
+
833
+ **缺省档由 `'hover'` 改为 `'scroll-hover'`**:`scrollbar` 与 `scroll-area` 不写 `type` 时都走新档。显形集合是原缺省档的严格超集,没有一条本来看得见的滚动条会消失;占道与否、触屏交给原生滚动那一路都不变。
834
+
835
+ **需要跟着改的代码**:对 `ScrollbarType` 做穷尽 `switch` / 映射表的地方要补 `'scroll-hover'` 分支;读 `ScrollbarApi.type` 或 `data-type` 并按值分派的代码会收到这个新值。
836
+
837
+ 状态机的两个判据改了名:`isHoverType` → `showsOnHover`、`isScrollType` → `showsOnScroll`(原名在新档下会读成谎话)。判据名只在机器内部与文档的「状态机」小节露面,不进公开 API。
838
+
839
+ 日志的视口那条 `scrollbar-gutter` 收窄到「还在用原生条」的情形:带 `data-xh-scrollbar` 的容器原生条已被藏成零宽,空道对它没有布局作用。没挂自绘条时空道照留,原生滚动条出现与消失仍不推动文字。
840
+
841
+ - 35c9b65: 四家分段控件(date-field · time-field · date-picker · time-picker)的盒内布局统一。
842
+
843
+ **解剖新增 `segment-group`**:包住全部段位与作者写在段间的分隔符。date-field / time-field /
844
+ time-picker 三家新增这个部件,date-picker 已有的分段容器 `input` 改名为它——四家从此同名同职。
845
+ time-picker 的 `input` 仍是段位本身(多实例),语义不动。
846
+
847
+ 破坏性改动:
848
+
849
+ - `date-picker` 的 `input` 部件改名 `segment-group`,不留别名。
850
+ - `getInputProps` → `getSegmentGroupProps`;`DatePickerInputProps` → `DatePickerSegmentGroupProps`。
851
+ - Vue `XhDatePickerInput` → `XhDatePickerSegmentGroup`。
852
+ - WC `@csspart input` → `@csspart segment-group`(作者标记写 `data-xh-part="segment-group"`)。
853
+ - `--xh-time-field-segment-fg-placeholder` → `--xh-time-field-placeholder-fg`;
854
+ `--xh-time-picker-segment-fg-placeholder` → `--xh-time-picker-placeholder-fg`。
855
+ - `--xh-time-picker-column-max-h` → `--xh-time-picker-column-h`(列改定高)。
856
+ - `--xh-date-picker-content-p` → `--xh-date-picker-content-py` / `-px`;
857
+ `--xh-time-picker-content-p` → `--xh-time-picker-content-py` / `-px`。
858
+
859
+ 作者要把段位与分隔符挪进 `segment-group` 里,清空钮与展开钮留在 `control` 直属:
860
+
861
+ ```html
862
+ <div data-xh-part="control">
863
+ <div data-xh-part="segment-group">
864
+ <span data-xh-part="segment"></span>
865
+ <span>:</span>
866
+ <span data-xh-part="segment"></span>
867
+ </div>
868
+ <button data-xh-part="clear-trigger"></button>
869
+ </div>
870
+ ```
871
+
872
+ 行为与外观:
873
+
874
+ - 尾部按钮一律靠框内末端,靠 `segment-group` 的 `flex: 1 1 auto` 顶;
875
+ time-field 清空钮与 time-picker 展开钮的 `margin-inline-start: auto` 删掉。
876
+ - 四家 `control` 的 `gap` / `block-size` / `padding-inline` / `min-inline-size` 逐条同值,
877
+ `gap` 随尺寸档走 `--xh-control-gap-sm/md/lg`。
878
+ - 时间列定高:`time-picker` 的 `column` 与 `date-picker` 的 `time-column` 走 `--xh-viewport-h-sm`,
879
+ 两家的快捷选项列同档;两家浮层补上最大高度。
880
+ - 段位内衬统一 `--xh-space-1`;标题不再写 `cursor`;`:focus-within` 一律带 `:not([data-disabled])`;
881
+ time-picker 聚焦时补画聚焦环;图标尺寸随尺寸档走 `--xh-glyph-size-sm/md/lg`。
882
+
883
+ - bbc3431: select 浮层多出一个底部操作区:「新建」「全选」这类按钮终于有地方放了。
884
+
885
+ 原来放不进去有两条硬理由,都不是样式能绕的:`content` 既是 `role="listbox"`
886
+ (而 listbox 只许拥有 option 与 group,塞按钮进去是违规),又是那个 `overflow-y: auto` 的滚动容器
887
+ (放进去的按钮会跟着条目滚走)。所以这次把两件事拆开:
888
+
889
+ - **`content` 退成浮层外壳** —— 描边、底色、阴影、整体尺寸与键盘收口归它,它自己不滚。
890
+ - **新增 `list` 部件** —— `role="listbox"`、条目的拥有关系、滚动与那个「无锚点时兜底的 Tab 位」全在它身上。
891
+ - **新增 `footer` 部件** —— `list` 的兄弟。因此它既不进列表框的拥有关系,方向键与连打检索也走不到它,
892
+ 条目多到要滚时它仍贴在下沿不动。
893
+
894
+ **破坏性变更(alpha 期)**:条目现在要写在 `list` 里。
895
+
896
+ - Vue:`<XhSelectContent>` 与条目之间加一层 `<XhSelectList>`;底部操作区用新增的 `<XhSelectFooter>`。
897
+ 只传 `collection`、不写插槽的那条路由组件自己铺好,一个字都不用改。
898
+ - Web Components:`<div data-xh-part="content">` 里加一层 `<div data-xh-part="list">` 包住条目。
899
+ `list` 已列进 `requiredParts`,忘了写会在诊断通道上报 `wc.missing-part`,不会静默丢掉列表框语义。
900
+ - `trigger` 的 `aria-controls` 随之改指 `list`(它才是那个列表框)。
901
+
902
+ - 309feb2: DOM 状态属性收成一套词汇(`tooling/scripts/state-vocabulary.json` 是真源,`check-state-vocabulary` 七条判据守住)。皮肤靠这些 `data-*` 选中状态,使用者的全局规则同样靠它们,所以同一含义只留一个名字:
903
+
904
+ - **当前项**:`aria-current` 在 data 侧一律配 `data-current`。anchor 的 `data-active`、carousel 指示点 / pagination 页码 / side-nav 链接的 `data-selected` 都改过来;steps 保持 `data-state=current`(步骤族)。
905
+ - **`data-active` 一名三义退役**:展开 / 选中路径上的祖先改 `data-in-path`(cascader 列项、side-nav 分支),滑杆刻度已被越过改 `data-passed`(slider mark / mark-label)。
906
+ - **混合态一个词**:checkbox-group / table 表头 / transfer 列头的组级汇总 `data-state` 从 `all | some | none` 改为 `checked | unchecked | indeterminate`,与 checkbox 同词(`CheckboxGroupCheckedState` / `TableSelectionState` / `TransferCheckState` 的取值随之改)。
907
+ - **显隐**:有开合交互的 tag,`data-state` 从 `visible | hidden` 改 `open | closed`(机器状态名同改,`onOpenChange` 不变);派生显隐的 back-top 从布尔 `data-visible` 改 `data-state: visible | hidden`。
908
+ - **折叠**:layout 侧栏从 `data-state=collapsed|expanded` 改布尔 `data-collapsed`,与 side-nav / splitter 同写法。
909
+ - **死属性删除**:button 与 infinite-scroll 根上皮肤零引用的 `data-state`;scroll-area / scrollbar 的 `data-hover`(悬停走 `:hover`)。calendar 格子的 `data-focused` 改 `data-focus`。
910
+
911
+ 退役的四个属性名(`data-active` / `data-focused` / `data-hover` / `data-visible`)与四个 `data-state` 取值(`all` / `some` / `none` / `collapsed`)是公开面删减,基线已推。
912
+
913
+ - 8d6e450: 整洁度归队(统一性审计的最后一批)。
914
+
915
+ **令牌**:dialog / drawer 的宽度档提为 `--xh-overlay-sheet-w-sm/md/lg`(24/32/48rem)与 `--xh-overlay-drawer-w-sm/md/lg`(16/20/28rem),empty-state / result 的图标档提为 `--xh-glyph-size-xl/2xl/3xl/4xl`;`--xh-control-gap-lg` 此前与 md 恒等,改为 space-3(compact space-2);补 `--xh-fg-warning` / `--xh-fg-info`(与 success 同构)。tokens README 写明 px 与 rem 的口径,以及「单行控件本体的槽一律叫 control」。
916
+
917
+ **皮肤**:number-field 的 `--xh-number-field-input-h` 在 control 上用错部件名,改 `--xh-number-field-control-h`;spinner 三档归 glyph 尺寸族、anchor / pagination / steps / composer / menubar 的内衬对齐 control-px 阶梯;back-top / card / float-button / switch / dynamic-input 的阴影补使用者槽;timeline / typography / field / slider 的字面残留改令牌;30 处与令牌同值却不引令牌的兜底改引(15 处登记理由);checkbox-group / transfer 的指示符字形与 checkbox 同一配方。菜单与列表族的条目高亮只认 `[data-highlighted]`(菜单族此前还并挂 `:focus` / `:focus-visible`)。
918
+
919
+ **无障碍**:select 的触发器按 APG select-only combobox 打 `role=combobox` + `aria-haspopup=listbox` + `aria-controls`(popselect 是按钮式弹出保持 button);image-viewer 触发器补 `aria-controls`;83 处 `aria-hidden` 统一写布尔;iconOnly 按钮没有 `aria-label` / `aria-labelledby` 时开发模式提醒一次(Vue / WC 把作者写在根节点上的可及名转告连接层)。
920
+
921
+ **共享配方**:visually-hidden 的 9 条声明收成 headless 的 `VISUALLY_HIDDEN_STYLE`,六份 connect 引它;七份皮肤各自那份必须与 `visually-hidden.css` 逐条一致。
922
+
923
+ **门禁**:`check-literal-fallbacks`(兜底字面量与令牌同值即红)、`check-visually-hidden`、`check-tone-contrast`(自算 oklch → WCAG 对比度,六族 × 两主题 26 组配对,1 组已知例外登记理由)、`check-aria-shapes`(aria-hidden 字符串写法 / listbox 触发器角色);`check-elevation-role` 增「阴影必须带使用者槽」。
924
+
925
+ - 032f3fd: 带语气的 outline 控件边框补到 3:1,并修掉上一版留下的一处断链。
926
+
927
+ 上一版把控件边界迁到 `border.control` 时,如实记了一笔「带语气的 outline 形态够不着 3:1」——
928
+ 它走的是 `--xh-_tone-border`(语气色兑 40% 底色),六族在两套主题下是 1.44–2.18。这一版补上。
929
+
930
+ 新增 `--xh-_tone-border-control`:直接取语气主色本体,不再兑底色。两族在各自的底上仍不够,
931
+ 按主题各兜一次——语气色是固定原语、不随主题翻,这是唯一能表达的地方:
932
+
933
+ - 黄在白底上只有 2.70,浅色态改取新增的 `--xh-color-warning-700`(3.75);深色态 600 档就有 7.32,不动。
934
+ - 中性在深色底上只有 2.54,深色态改取 `--xh-color-neutral-550`(3.59);浅色态 600 档就有 7.80,不动。
935
+
936
+ 六族 × 两套主题现在最低 3.04(浅色 success),全部达标。
937
+
938
+ **调色板新增一档** `--xh-color-warning-700 = oklch(0.62 0.15 70)`。步距 ΔL 0.085,落在同族 700 档的
939
+ 区间中间(brand 0.058 / danger 0.077 / success 0.138),色度按同族惯例微降,色相与 600 一致。
940
+ 黄族此前只有 500/600 两档,没有更深的档可取,所以必须新增。
941
+
942
+ **顺带修掉一处断链**:上一版把 `toggle.css` 的 outline 边框改指了 `--xh-_tone-border-control`,
943
+ 而那个槽当时并不存在——`<XhToggle variant="outline" tone="danger">` 的边框一直退到中性色,语气丢了。
944
+ 这一版把槽真正建起来,`button` 与 `button-group` 一并接上。
945
+
946
+ **新增门禁 `check-private-slots`**:皮肤里消费的每个 `--xh-_*` 私有槽都必须在某份皮肤里声明过,
947
+ 声明了没人用的也要删。上面那条断链正是它该拦下的——CSS 不报错、TS 不报错,
948
+ 而既有的 `check-token-refs` 整体放行 `--xh-_` 前缀,谁都看不见。拿改动前的仓库实跑过:它红在
949
+ `toggle.css:71`,改完转绿。
950
+
951
+ - 918b870: 实心底上的前景色与交互态挪动方向改由语气色现推,换肤下配对自动成立。
952
+
953
+ `--xh-_tone-on` 此前按语气族写死:brand / neutral / danger 配白字,success / warning / info 配深字。这个分派是按本仓这套 600 档实测出来的,但 `--xh-_tone` 是可换肤的——使用者把各族的 600 改写成自己的调色板,配对的前提就没了,而门禁只验本仓的色,失配一路绿灯。
954
+
955
+ 改成从 `--xh-_tone` 现推:把语气色转成线性 sRGB 分量、按 WCAG 的权重算出相对亮度,低于 0.179 配白字、高于配深字。0.179 是白字与黑字对比度相等的那一点,解析解,不是估的。
956
+
957
+ `--xh-_tone-shift`(交互态往哪个方向挪)一并现推,取前景的反面。只推前景不推方向会出净回归:静态态救回来了,悬停与按下却把底往前景那一侧挪——某消费方调色板上实测 danger 静态 4.70 而悬停掉到 3.78、按下 3.18。
958
+
959
+ 用相对颜色语法 `color(from … srgb-linear …)`,高于本仓的浏览器地板(Chrome 119 / Firefox 128 / Safari 16.4 起),所以整块包在 `@supports` 里,逐族写死的那一份留着当兜底;探针按实际用到的整个形态测,只测得起半截语法的引擎不会落进坏值。alpha 显式写 1,不然会连语气色的透明度一起继承。
960
+
961
+ 某消费方调色板上的实测(浅色 / 深色,静态 · 悬停 · 按下):
962
+
963
+ 改前 info 3.94 · danger 4.47 不达标
964
+ 改后 六族最低 4.70,全部达标
965
+
966
+ 本仓自己这套色只升不降:success 6.51→6.90、warning 7.36→7.80、info 5.71→6.05、深色 brand 5.32→5.64。
967
+
968
+ 三条门禁跟着改:
969
+
970
+ - `check-tone-contrast` 此前只验写死的那一份。现在把 `@supports` 块单独解析,现推档与兜底档**各验一遍**(158 组配对,原 122 组),并按 tone.css 里那两条式子的形态逐字对账——式子改了形态对不上就失败。
971
+ - `check-css-floor` 新增「受 `@supports` 守卫的增强」这一档并登记相对颜色语法:块外出现即失败。此前它对这条语法是静默的,等于谁都能悄悄抬底线。
972
+ - `check-token-refs` 不再把注释里提到的颜色、`@supports` 的探测条件、以及 `fn(from var(--xh-…) …)` 当成颜色字面量——前两者不落到任何元素上,后者的源色本身就是令牌。
973
+
974
+ - a69cead: 树补一条 `leafOrientation`:末端那一层可以横排。
975
+
976
+ 只作用于「子节点全是叶子」的那一层——菜单授权里就是按钮那层。一个菜单下十几个按钮,
977
+ 横排一行铺完,省掉大量纵向翻找:
978
+
979
+ ```vue
980
+ <XhTreeRoot :collection="menus" leaf-orientation="horizontal" />
981
+ ```
982
+
983
+ **中间层与整棵树恒是竖排,不提供开关。** 它们承载的是层级本身,横过来层级就读没了。
984
+ 判据是「这一层不再往下分」而不是「深度等于几」:同一棵树里各枝深浅不一,
985
+ 按深度判会把浅枝的中间层也横过来。
986
+
987
+ **方向键不跟着改。** 树上左右是层级操作(收起 / 展开、回父层 / 进子层)、上下走可见行,
988
+ 这是 treeview 的规范语义,横排只是排布。
989
+
990
+ 顺带修一处:叶子行在竖排下会自己补出「箭头那一格」与同级分支对齐,横排下补出来的
991
+ 是节点之间的空隙而不是层级,那条规则因此按行盒**所在的那层容器**判定方向。
992
+
993
+ - 4abe899: 统一性收口的头两批:先立门禁让跑偏能红,再补语义令牌把皮肤里的原语引用与互异的字面量收成一处。
994
+
995
+ **海拔改按角色走。** `--xh-elevation-0…4` 五档删掉,换成三个角色:`raised`(静态抬起面:卡片的 elevated 变体、分段控制器的滑块、滑杆拇指)、`floating`(锚定浮层:下拉、菜单、popover、hover-card、tooltip)、`sheet`(遮罩式与通知:dialog / drawer / toast / tour / floating-panel / float-button / back-top)。深色主题的三档更重、外加一圈 1px 浅描边,暗底上浮层才分得出层。34 份皮肤全部迁过去,`check-elevation-role` 校验每处阴影都走角色、且 27 个浮层/遮罩面的角色与部件对得上。这是公开面的删减,基线已推。
996
+
997
+ **字号不再下探原语。** 新增 `--xh-control-font-sm/md/lg`(控件主文字,与 `--xh-control-h-*` 同构按档走)、`--xh-control-caption-sm/md/lg`(控件里的次级文字:提示、计数、快捷键、清空钮,比同档主文字低一级)、`--xh-text-heading-1/2-*`、`--xh-text-caption-size`、`--xh-text-secondary-size`。皮肤里两百三十处 `--xh-font-size-*` 引用全部换成语义档;typography 的六级标题与 rating 的星标是字号阶梯本身,登记为例外。`check-text-scale` 守住。
998
+
999
+ **默认宽度、内衬、轨道、折叠面的共享字面量收成令牌。** `--xh-control-min-w`(12rem)统一了 select / combobox / tree-select / cascader / color-picker / date-picker 六个触发器此前的六个值,time-picker / text-field / date-field / time-field / password-input 五家此前没有任何宽度声明,现在同样接上;`--xh-surface-py/px-sm/md` 统一了 dialog / drawer / tour / floating-panel / toast 的内衬;`--xh-track-thickness` / `--xh-track-thumb-size` 给滑杆与进度条;`--xh-nav-link-max-w`、`--xh-viewport-max-h`、`--xh-motion-scale-drag`(减弱动效归 1)、`--xh-glyph-size-text`(跟文字走的字形尺寸)、`--xh-control-box-sm/md/lg`(pin-input 的方格,随 compact 收)、`--xh-switch-track-h-*`、`--xh-syntax-string/number/keyword`(code-block 与 json-viewer 的语法色,随主题明暗切换,皮肤里不再有 hex 字面量)。`check-shared-slots` 新增「同后缀跨组件字面量互异也报」。
1000
+
1001
+ **聚焦态描边统一成一派。** 此前三派:描边不变只画环、描边跟着环色走(语气轴在这一派整个失效)、只画环不管描边。现在 21 份输入类皮肤都写 `border-color: var(--xh-<c>-<part>-border-focus, var(--xh-_tone, var(--xh-border-control-focus)))`,新令牌 `--xh-border-control-focus` 缺省等于 `--xh-border-control`;time-field 聚焦补上了此前缺的环。`check-focus-ring` 加校验。
1002
+
1003
+ **图标尺寸接线。** 38 份画兜底字形的皮肤在 root(浮层族在 content)上声明 `--xh-icon-size: var(--xh-<c>-icon-size, var(--xh-glyph-size-text))`,兜底字形的盒同样按它量——作者往指示符槽塞 `<XhIcon>` 时不再从 1em 跳到 20px。`check-icon-size` 守住。
1004
+
1005
+ **几何修正。** pin-input 的方格此前缺省引的是 lg 档高度、sm 档引 md;segmented 横排外盒此前 38px(item 32 + 轨道内衬 + 描边),现在外盒本身即一档控件高、段撑满轨道内侧;checkbox 的方框锚在 `--xh-control-indicator-size` 上随 compact 收;checkbox-group 的指示符不再是 16px 字面量。radio-group / checkbox-group / composer 的禁用态去掉叠加的不透明度(与容器一起变淡会把对比度压穿)。
1006
+
1007
+ **门禁。** 新增 `check-stroke-scale`(描边宽度只走 `--xh-stroke-*` / ring)、`check-keyboard-suites`(键盘表非空 ⇒ 一致性套件存在且两个适配器都登记);`check-control-height` 按「组件 → 控件本体部件」显式管辖(button / toggle / segmented / pagination 等此前在门禁外)并校验 sm/md/lg 档位与 `data-size` 对应;`check-disabled-contrast` 改正则并加跨块判定;`check-shape-scale` 扩到逻辑角与私有槽;`check-keyframe-refs` 增扫适配器源码里的内联动画名(反馈服务的加载徽记改用 Web Animations,不再依赖某份皮肤在场);`check-state-vocabulary` 接上 `state-vocabulary.json` 真源(`data-state` 的 43 个取值分 9 个族,connect 字面量与皮肤选择器两头对表,并报告「发射但零引用」的属性);`check-token-refs` 禁皮肤里的颜色字面量。
1008
+
1009
+ **套件。** 补 image-viewer(8 行键盘表,Tab 循环两行 jsdom 豁免)与 side-nav(10 行含折叠态弹出)的一致性套件,Vue 与 WC 两侧登记。
1010
+
1011
+ - 35c9b65: 相似组件与组合组件的视觉、动效、行为收成一套口径(`开发设计/UI.VisualConsistency.Contract.md`)。
1012
+
1013
+ **盒的定义统一了。** 此前 16 个输入 / 选择控件有三种「盒」:9 家由 `control` 画描边与底、5 家由 `trigger`(一个 `<button>`)当盒、2 家由 `input` 自画。盒是 button 的那 5 家(select · cascader · tree-select · popselect · color-picker)没法把清空钮放进框里,只能贴在框外——这就是「清空钮位置不统一」的总根因。现在判据只有一条:**解剖里有 `control` 就是盒**,`trigger` 退化成盒内那颗 `flex: 1 1 auto; border: 0; background: transparent` 的按钮,聚焦环改画在 `control:focus-within` 上。cascader / tree-select / popselect / color-picker / text-field 的解剖新增 `control` 部件。
1014
+
1015
+ **尾部按钮一律在框内最右。** 盒内布局恒为「内容区 `flex: 1` → 尾钮组 `flex: none`」。段位并排、没有单一容器的四家(date-field · time-field · date-picker · time-picker)新增 `segment-group` 部件把段位与分隔符包起来当内容区(date-picker 原有的 `input` 分段容器改名 `segment-group`,四家从此同名同职),`margin-inline-start: auto` 那套 hack 删掉。行内动作钮(清空 / 展开 / 明暗切换 / 加减)一律 `--xh-control-action-size` 方钮——number-field 的加减钮与 password-input 的明暗钮此前是「贴边的控件高钮」。
1016
+
1017
+ **并排成对的面板定高。** 新增 `--xh-viewport-h-sm/md/lg`(12/16/24rem,compact 同比例收)。transfer 两侧列表此前是 `min 8rem / max 16rem`,条目搬走后整个组件跟着变矮——现在定高 `--xh-viewport-h-md`,左右等高、空侧也占满。cascader 的列、date-picker / time-picker 的时间列同样定高;单个浮层面板仍内容驱动,但补上了此前缺失的高度上限。
1018
+
1019
+ **菜单族三家逐条同值。** `menu` / `menubar` / `context-menu` 共用同一台机器,皮肤却各写各的:menubar 根本没有 `item[data-state='open']` 这条规则,所以「发送到…」展开时不像 menu 那样加粗高亮。现在条目内衬 / 字号 / 圆角 / 行高 / 展开态 / 高亮态 / `content` 外观 / `separator` / `group-label` 全族同值,menu 补齐 `group` / `group-label` / `separator` 部件,子菜单箭头走字形令牌。navigation-menu 与 side-nav 的弹出面板按同族口径归队。
1020
+
1021
+ **浮层面板与输入族小件归队。** `content` 一律双槽内衬 + 族档 min-w / max-w;cascader 的 48rem、color-picker 的 15rem、tour 的 22rem 等裸值改令牌(新增 `--xh-overlay-max-w-xl`);label 颜色与间距、图标尺寸随档、聚焦环私有槽(invalid 时变红)、`:focus-within` 的禁用守卫、disabled / readonly 的三样齐——逐条统一。password-input 的明暗钮用上了新的 `--xh-glyph-mark-eye` / `-eye-off` 字形令牌。
1022
+
1023
+ **门禁**:`check-control-box`(盒结构 12 条判据)、`check-panel-height`(面板高度只走滚动面令牌、并排面板必须定高)、`check-family-parity`(菜单族 / 分段族 / 下拉族 / 气泡族逐条同值)。
1024
+
1025
+ 公开面:五家 `--xh-<c>-trigger-*` → `--xh-<c>-control-*` 槽改名、date-picker 的 `input` 部件与 `XhDatePickerInput` 组件改名 `segment-group` / `XhDatePickerSegmentGroup`、`--xh-hover-card-font-size` 与 transfer 的 `-list-min-h` / `-list-max-h` 删除,共 43 项,基线已推。
1026
+
1027
+ - 520b847: 周序号成为一等部件 `week-number`,不再由使用者自己拼一列出来。
1028
+
1029
+ 上一版只把数字算出来(`panel.weekNumbers`),列宽得作者用行内 `grid-template-columns` 自己撑,
1030
+ 库不管它的皮——同一份东西在不同项目里会长得不一样,这不是组件库该留的样子。
1031
+
1032
+ - 解剖新增 `week-number`(可选部件,不写即不渲染),语义是这一行的表头(`role=rowheader`):
1033
+ 在 `role=grid` 里,一行的标号本就该是 rowheader,而不是又一个可选的格子
1034
+ - `getWeekNumberProps` / `getWeekNumberText` 两条,文字由两个适配器各自填,保证同构;
1035
+ 表头那一格是占位、不带值,解析不了不抛、给空串占住列宽
1036
+ - 皮肤接管列宽与字样:摆了周序号格的行自动让出行首一列
1037
+ (`--xh-calendar-week-number-w`,默认 2.25rem),数字比日子小一号、颜色压下去、不跟着选中态走
1038
+ - 新增 `XhCalendarWeekNumber` / `XhDatePickerWeekNumber`;WC 侧写
1039
+ `<span data-xh-part="week-number" value="行首那天">` 即可
1040
+
1041
+ 选择器那条列宽规则写的是 `:not([hidden]):has(...)`——同特指度的规则谁在后面谁赢,
1042
+ 不带这一道的话收起态会被这条 `display` 掀开(上一轮刚栽过一次,已有门禁拦着)。
1043
+
1044
+ ### Patch Changes
1045
+
1046
+ - 33af800: 角标的三档尺寸:每一档的盒都抬到大于字号。
1047
+
1048
+ 原先 sm 档的盒高与字号都是 12px,两位数会把角标撑破。字号刻度最小就是 12px
1049
+ (`--xh-font-size-xs`),所以往上抬盒子而不是往下压字:盒 16 / 20 / 24,
1050
+ 字 12 / 13 / 14,圆点 6 / 8 / 10,三条阶梯各自严格递增。
1051
+
1052
+ - a55c76e: 日历补上快速翻年、周选整周预览,日期示例按粒度重整。
1053
+
1054
+ **« / » 快速翻**:新增 `prev-year-trigger` / `next-year-trigger` 两个可选部件(不写即不渲染),
1055
+ 步长跟着视图走——日视图一年,月与季度十年,年视图一百年(它的 `‹ ›` 本来就走十年,
1056
+ 大步得更大才有用)。边界与 `‹ ›` 各判各的:上界卡在今年之内时,下一页还翻得动、整年跳出去就按不动了。
1057
+
1058
+ **周选悬停整周亮**:`weekSelection` 下指针扫过哪一行哪一行整整七天一起亮,与点下去的结果对得上。
1059
+ 此前沿用的是「起点 → 悬停点」那一段,一格一格拉出来的区间在周选里讲不通。不开周选时照旧。
1060
+
1061
+ **示例重整**
1062
+
1063
+ - 天 / 周 / 月 / 季度 / 年归拢成一个「五种粒度」示例,一套结构走完
1064
+ - 「区间选择」补齐五种粒度,都是并排两页
1065
+ - 删掉旧的「按月选择」——它是 `view` 出现之前手搓的一版面板(拿 `XhButton` 拼的),
1066
+ 与新的 `view="month"` 长相不一致;它想演的「输入行只留年月两段」并进新示例,
1067
+ 按年挑就只留年那一段
1068
+
1069
+ - 9294172: 日历的「大步翻」两颗钮不再被无条件收掉,« » 与 ‹ › 同一副长相。
1070
+
1071
+ 四条规则的选择器列表里,限定只跟在后两条上:`[data-part='prev-year-trigger'], [data-part='prev-trigger'][hidden], [data-part='next-year-trigger'], [data-part='next-trigger'][hidden]` —— 年那两颗是裸的,于是无条件命中 `display: none`,翻年的钮从来就没画出来过。行为层一直是通的(`canGoPrevYear` / `stepYear` / `getPrevYearTriggerProps` 都在),只是看不见也点不着。
1072
+
1073
+ 同样的漏写还在悬停、禁用、聚焦环三条上:那三条把月钮的状态样式无条件加在了年钮身上。四条一并补齐限定。
1074
+
1075
+ 新增 `tests/browser/calendar-nav-skin.spec.ts` 钉住:四颗翻页钮都画得出来、同一副尺寸,打上 `hidden` 才收起,禁用态四颗同一副长相。这类「被一条 display 悄悄收掉」只有在真实浏览器里按级联算才验得出来。
1076
+
1077
+ - bae3231: combobox 展开按钮翻面改为只转箭头字形(或作者塞的图形),不再旋转按钮本体:按钮自带悬停底色,整体旋转会带着底色一起转出一个歪斜的方块。
1078
+ - 9da2444: 修:区间选择器的浮层恒亮、糊在视口左上角、怎么点都关不掉。
1079
+
1080
+ 多面板那一版给 `content` 加了条 `:has(> calendar + calendar)` 的横排规则。它与上面那条
1081
+ `[data-part='content'][hidden] { display: none }` 特指度相同(都是 0-3-0),却排在它后面,
1082
+ 于是收起态被它掀开:浮层一直显示,又因为定位引擎只在展开时跑、坐标恒为 0,就糊在视口左上角。
1083
+ 只有区间那一个示例中招——它是唯一摆了两张日历的。
1084
+
1085
+ 选择器补上 `:not([hidden])`,与顺序、特指度都无关了。
1086
+
1087
+ 同时新增门禁 `check-hidden-override`:某个 part 已经有 `[hidden]` 兜底,其后又有规则把
1088
+ display 改回非 none 且没带 `[hidden]` / `:not([hidden])` 的,一律拦下。
1089
+ 拿这次的坏规则反向验证过:去掉 `:not([hidden])` 当场报错并指到行号。
1090
+ 全仓 109 份皮肤 · 314 条兜底扫下来,此前只有这一处。
1091
+
1092
+ - 8a44b64: 四处被祖先 `overflow` 裁掉的聚焦环改成往内收。
1093
+
1094
+ `outline` + 正的 `outline-offset` 把环画在元素盒外面,祖先只要是
1095
+ `overflow: hidden / auto / scroll`,环就会被裁掉一截——键盘用户看到的是
1096
+ 三条边或者两侧缺口的半圈蓝环。四处改为 `outline-offset: calc(-1 * var(--xh-ring-width))`,
1097
+ 环整圈落在盒内:
1098
+
1099
+ - image-cropper 的 `crop-area` 与 `crop-handle`:两者都长在 `viewport` 里,
1100
+ 那层 `overflow: hidden` 同时还替暗遮罩(`box-shadow: 0 0 0 9999px`)收边。
1101
+ - heatmap 的 `grid`:`root` 为一整年五十几列备了 `overflow-x: auto`。
1102
+ - table 与 transfer 的 `select-all-trigger`:同文件的邻居
1103
+ (table 的 `row` / `sort-trigger`、transfer 的 `search` / `item`)本就是内收写法,
1104
+ 这两处是仅剩的外扩。
1105
+
1106
+ `--xh-ring-offset` 令牌本身不动:库里另有 9 条规则写着
1107
+ `calc(-1 * var(--xh-ring-offset))`,翻令牌的符号会把它们一起翻成外扩。
1108
+
1109
+ - 1b7a5f1: 统一性审计收口后的六条遗留项。
1110
+
1111
+ **px 与 rem 按口径归位。** 字号七档 `--xh-font-size-xs…3xl` 从 px 改为 rem(0.75 / 0.8125 / 0.875 / 1 / 1.125 / 1.375 / 1.75rem,根字号 16 时像素不变,使用者改根字号时整套排版随之缩放);字形与控件几何改为 px:`--xh-glyph-size-sm/md/lg` 16 / 20 / 24px、`--xh-glyph-size-xl…4xl` 32 / 40 / 56 / 72px、`--xh-control-action-size` 24px(compact 20px)、`--xh-control-indicator-size` 16px(compact 14px);color-picker 的动作钮与色块同样归 px。
1112
+
1113
+ **side-nav 折叠态换枝播退场。** 机器里弹出面板的坐标改为按分支记账(`popoutPlacements`),换枝时旧面板保留坐标、`data-state=closed` 播 `xh-pop-out`,新面板同帧 `open` 播 `xh-pop-in`;此前旧面板的坐标在新枝 OPEN 那一拍被作废,退场瞬时。
1114
+
1115
+ **tree-select 的 Vue Root 补 collection 自动渲染树。** 没给默认插槽且传了 `collection` 时自动铺 label? / trigger / clear-trigger? / positioner / content / tree(分支与叶子递归),新增 `label` prop 与插槽、`clearable` prop(缺省 false);自动树与手写树 DOM 逐字同构,与 select / combobox 同口径。
1116
+
1117
+ **门禁与测试整洁。** 三道浮层门禁共用 `tooling/scripts/lib/overlay-families.mjs`(名单与核实逻辑一份,各门禁的子集差异写明);27 处测试里为旧 kernel 缺省桩的 `matchMedia` 删掉(减弱动效探测无 matchMedia 时已一律不减弱)。
1118
+
1119
+ - 177b3c3: 三道新门禁把版本政策里「只靠自觉」的条款焊成机器检查,`pnpm gate` 由二十项变二十三项。
1120
+
1121
+ - **`check-css-floor`**:`.browserslistrc` 书面记录浏览器硬底线,拒绝名单拦住 `@container`
1122
+ 这类无兜底的抬底线特性(`@scope`、`@starting-style`、`view-transition`、滚动驱动动画、
1123
+ CSS 嵌套等),`light-dark()` / `dvh` 必须同级联兜底;`field-sizing` 的退化路径在 HTML 侧,
1124
+ 按文件白名单放行。
1125
+ - **`check-version-lock`**:17 个库包的 `package.json` 必须同版本。此前改一个包的 version
1126
+ 而不动其余 16 个没有任何门禁会响,锁步发版只靠自觉。
1127
+ - **`check-wiring`**:`tooling/scripts` 里每个检查脚本都必须接进某个 pnpm script——写了不接线
1128
+ 等于没写,死引用同样被拦下。
1129
+
1130
+ 同时 `check-slot-types` 补上第四条判据:写进 `SlotsType` 却从不渲染的插槽(消费方合法传进来的
1131
+ `#slot` 会被静默吞掉),裸引用 `slots.item` 整体传给 helper 的 collection 族用法计入「用过」。
1132
+
1133
+ - ed51531: 菜单族的勾选标记跟着语气走。
1134
+
1135
+ `context-menu` 与 `menubar` 都有 `tone` 轴、也都在根上发 `data-tone`,但 `item-indicator`
1136
+ 的颜色写死在 `--xh-fg-brand`:把菜单标成 `tone="danger"`,整条菜单换了族,勾选标记还是品牌蓝。
1137
+ 同族的 `select` / `popselect` / `combobox` / `cascader` / `tree-select` 五家早就是跟着语气走的,
1138
+ 只有这两家掉队。
1139
+
1140
+ 两家的颜色链改成 `var(--xh-<组件>-item-indicator-fg, var(--xh-_tone, var(--xh-fg-brand)))`:
1141
+ 写了语气跟语气,没写落回 `--xh-fg-brand`——**没写 `tone` 的用法一个像素都不变**。
1142
+ `listbox` 不动,它没有语气轴,链尾就是全部。
1143
+
1144
+ - ac885c9: number-field 新增可选 `control` 部件:加减按钮叠进输入框内,与输入框成为视觉一体。
1145
+
1146
+ 此前加减钮与输入框是兄弟节点,受 HTML 约束进不了框内,只能三件并排。现在把输入框与两个按钮
1147
+ 放进 `control` 部件,皮肤把描边、底色、聚焦环(改为 `:focus-within`)整体画在 control 上:
1148
+ 框内 input 退成透明,减钮在左、加钮在右、输入框居中(顺序由作者模板决定),前后缀图标/文字
1149
+ 直接流式插在 input 两侧,不用绝对定位;悬停/按下/贴边禁用沿用原有语义色。
1150
+
1151
+ - **Vue**:新增 `XhNumberFieldControl`;`data-disabled` / `data-readonly` / `data-invalid`
1152
+ 三个状态属性由 connect 落到 control 上。
1153
+ - **Web Components**:作者写 `<div data-xh-part="control">` 包裹即得同样的一体式。
1154
+ - **不写 control 时完全退回旧观感**:control 是可选部件,旧模板一行不改照常渲染,三档
1155
+ variant / tone / size 与旧式并排布局一致。
1156
+
1157
+ 一致性测试的 fixture 改成一体的 control 结构,两个适配器的 conformance 同步通过。
1158
+
1159
+ - 0a056e6: number-field 的 `control` 收成一枚整件:一道描边、一个圆角、一枚聚焦环,盒里再无第二条边界。
1160
+
1161
+ 此前盒内的加减钮仍带着独立版的灰底与自己的圆角,白底的框上贴着两块灰、圆角还比框的内角大一档
1162
+ 顶到描边外面,一枚控件被读成三块拼起来的。现在盒内三段一律透明,底色、描边、圆角全部只由
1163
+ control 画一次,符号取次级前景、悬停与按下才浮出底色,贴住 min / max 的那一侧只压灰符号而不再铺灰底。
1164
+
1165
+ - 两端圆角取盒子的内圆角(`圆角 - 描边`),并按 `:first-child` / `:last-child` 认位置——
1166
+ 三件的先后由作者模板定,减钮不一定在最前面(库内 `12-precision` / `13-change-timing` 两例即是输入框在前)。
1167
+ - 盒内输入框默认居中、默认宽 `5em`,不写行内样式也是一枚齐整的步进器;
1168
+ 可用 `--xh-number-field-input-align` 与 `--xh-number-field-input-w` 改。
1169
+ - control 由 `align-items: stretch` 改成 `center`:作者插在框里的前后缀文字此前被拉满整框高度,
1170
+ 字贴着框顶;输入框与加减钮各自明写撑满,不受影响。
1171
+ - 不写 `control` 的三件并排布局一行未动。
1172
+
1173
+ - 7a5d898: 漏引皮肤不再静默:新增 `startSkinCheck()` 开发期探测与 `styles.missing-skin` 诊断码。
1174
+
1175
+ 按需引皮肤时漏掉一行原本是这个库最难查的失效:那个组件的 `data-scope` / `data-part` 照常都在、
1176
+ 别的皮肤也确实加载了,只有它渲染成没有内边距、没有底色的裸元素,看起来像组件坏了而不是少引了一行。
1177
+ 这一条正是「按组件挑」在真实项目里立不住的根本原因。
1178
+
1179
+ 每份组件皮肤现在在自己的 `[data-scope='X']` 上落一个 `--xh-X-skin` 标记(104 份)。
1180
+ `startSkinCheck()` 扫页面上出现过的每个 scope,取不到标记就报诊断:
1181
+
1182
+ ```ts
1183
+ if (import.meta.env.DEV) {
1184
+ const { startSkinCheck } = await import("@xihan-ui/kernel/skin-check");
1185
+ startSkinCheck();
1186
+ }
1187
+ ```
1188
+
1189
+ ```
1190
+ [xh][button] [styles] button 的皮肤没引:import '@xihan-ui/styles/button.css',或改引全量的 '@xihan-ui/styles'
1191
+ ```
1192
+
1193
+ 两处刻意的取舍:
1194
+
1195
+ - **每个 scope 只探一次。** 探测要读计算样式,逐实例探是真实的强制样式重算;一个 scope 的皮肤
1196
+ 在不在场与实例数无关,探一次就够。
1197
+ - **标记落在 `[data-scope='X']` 而不是 root 部件上。** 浮层族的 `content` 被 portal 到 body,
1198
+ 不在 root 的子树里,只在 root 上声明的话自定义属性继承不过去,这些部件会误报。
1199
+
1200
+ 探测器走 `@xihan-ui/kernel/skin-check` 子路径而不是主入口:它是开发期工具,不该躺在每个消费方都会打包的那条入口里(放主入口会让 kernel 的体积棘轮超 118 B,那条棘轮量的正是整包)。
1201
+
1202
+ 新增 `check-skin-markers` 门禁守住 104 份皮肤的标记齐全——漏一份,那个组件就退回静默失效,
1203
+ 而且探测器还一声不吭。`pnpm gate` 十九项 → 二十项。
1204
+
1205
+ - 93fe061: `light-dark()` 与 `dvh` 补上级联兜底,旧引擎不再靠解析失效退化。
1206
+
1207
+ `code-block` 的三种语法色原本只有 `light-dark(...)` 一条声明,不认它的引擎里整条声明被丢弃,
1208
+ 变量取不到、靠消费点的 `var()` 失效继承出单色——退化是碰巧成立的,不是写出来的。
1209
+ `layout` 侧栏的 `100dvh` 上限同理。现在两条都按「先旧后新」的级联兜底写法:旧引擎保留前一条。
1210
+
1211
+ 改动由新增的 `check-css-floor` 门禁保证不会再回潮(见同一批提交)。
1212
+
1213
+ - 59c86fa: 新增 `check-style-entries` 门禁:每份皮肤都必须进得了全量入口、也够得着按需入口。
1214
+
1215
+ `index.css` 的 `@import` 清单是手工维护的,`package.json` 的子路径导出也是。两处任何一处漏了,
1216
+ 结果都是静默的:漏进 `index.css`,全量引入的人拿不到那份皮肤,组件渲染成裸元素;
1217
+ 漏了子路径导出,按需引入的人根本 import 不到它。今天 109 份皮肤两处齐全,但没有任何东西守着。
1218
+
1219
+ 门禁同时把「按需产物的顺序只能由 `index.css` 过滤得来」钉在这里。同一个 `@layer xihan.components`
1220
+ 内,等特异性的规则靠源序定胜负;另起一套排序(按字母、按目录读取序)今天看不出差别——
1221
+ 当前仅有的 3 处跨 scope 规则在两种排序下相对次序恰好一致——但那正是它危险的地方:
1222
+ 将来加进第四处,按需引入的人就会与全量引入的人渲染不同,而且全绿。
1223
+
1224
+ `installation.md` 的「样式的三种接法」如实补上第二种要自己扛的两条风险,并给出体积口径
1225
+ (全量 51 kB gzip,含令牌与 109 份皮肤),建议没有明确体积压力就用全量。
1226
+
1227
+ - c8c7c18: 修复 `index.css` 的级联层序:`layers.css` 的层序声明挪到入口最顶。此前 tokens 与部分组件皮肤抢先立层,实际层序成了 `tokens < components < reset < motion < overrides`,`reset` 的 `font: inherit` 会压掉表单控件皮肤的 `font-size`。date-picker 的 showTime 皮肤同步从 motion 层归位 components 层。仅分层入口受影响,`index.unlayered.css` 行为不变。
1228
+ - 3ed6b9f: 描边档的标签改用「可操作区边界」那一档语气色,轮廓看得清了。
1229
+
1230
+ `outline` 的边就是这枚标签的全部轮廓——它没有底色,边没了就只剩一行字。此前取的是 `--xh-_tone-border`(语气色兑四成面色),而 tone.css 自己就注明这一支六族都落在 1.44–2.18,当轮廓根本分不出边界。
1231
+
1232
+ 改取 `--xh-_tone-border-control`:那一支是为「可操作区边界要 3:1」准备的,取的是语气本体。在某消费方的调色板上实测,边相对面从 1.59–1.76 抬到 4.56–7.83(浅色)、2.13–3.42(深色),文字那一档没动(5.14–9.35)。
1233
+
1234
+ `--xh-tag-border` 这个使用者槽仍排在最前,要另配一档边照旧写它。
1235
+
1236
+ - 87f5b73: 装饰档 `--xh-_tone-soft` 从 500 提到与控件边界同一档
1237
+
1238
+ 色条、指示条、锚点高亮这些用装饰档画的东西是非文字图形,按 WCAG 1.4.11 要 3:1。
1239
+ 500 档压在浅色画布上,success 2.29、warning 1.92、info 2.75 都够不到。
1240
+
1241
+ `--xh-_tone-border-control` 早就为同一个阈值兜过底(warning 在浅色下取 700、
1242
+ neutral 在深色下取 550),要求完全一样,装饰档就直接跟着那一支走,六族十二组全部达标。
1243
+ `check-tone-contrast` 补上这条断言,覆盖从 110 组扩到 122 组。
1244
+
1245
+ 观感上所有色条、时间线指示点、锚点高亮会比原来重一档。
1246
+
1247
+ - b6fb182: 叶子行补上箭头那一格的缩进,不再比同级分支往行首缩。
1248
+
1249
+ 分支行的首格是展开箭头,叶子行没有这一格。作者摆了 `item-indicator` 时由它顶着,
1250
+ 可勾选档的首位直接是 `item-checkbox`——没有东西占位,叶子的文字就比所在分支的文字
1251
+ 往行首缩了一个间隙,第三级与第二级挤在同一条竖线上,层级关系读不出来。
1252
+
1253
+ 行盒改为在没有 `item-indicator` 时自己补出这一格(`:has()` 判定,摆了指示符的那档
1254
+ 不受影响、不会被重复缩进)。宽度取的是箭头与指示符共用的那两个令牌,
1255
+ 覆盖 `--xh-tree-indicator-size` / `--xh-tree-row-gap` 时跟着走。
1256
+
1257
+ - Updated dependencies [9ea57f6]
1258
+ - Updated dependencies [f72664d]
1259
+ - Updated dependencies [3469066]
1260
+ - Updated dependencies [bc65cb7]
1261
+ - Updated dependencies [1b7a5f1]
1262
+ - Updated dependencies [f154e07]
1263
+ - Updated dependencies [1e90ce6]
1264
+ - Updated dependencies [091bbef]
1265
+ - Updated dependencies [ec93d6b]
1266
+ - Updated dependencies [8d35702]
1267
+ - Updated dependencies [89d8c54]
1268
+ - Updated dependencies [516bd46]
1269
+ - Updated dependencies [9548330]
1270
+ - Updated dependencies [8d6e450]
1271
+ - Updated dependencies [032f3fd]
1272
+ - Updated dependencies [4abe899]
1273
+ - Updated dependencies [35c9b65]
1274
+ - @xihan-ui/tokens@1.0.0
1275
+
1276
+ ## 1.0.0-preview.0
1277
+
1278
+ ### Major Changes
1279
+
1280
+ - bc7eeed: 徽标收窄成「只做角标」,并补齐角标该有的能力。
1281
+
1282
+ 原先 badge 与 tag 是一对孪生:`variant` 三形态、`size` 三档、默认插槽放任意内容,
1283
+ 连档位取值都逐个相同。两个组件做同一件事,使用者只能靠猜。
1284
+
1285
+ 现在 badge 只做一件事——挂在别的元素角上的一枚标记:
1286
+
1287
+ ```vue
1288
+ <XhBadge :count="5" tone="danger" label="5 条未读">
1289
+ <XhButton>收件箱</XhButton>
1290
+ </XhBadge>
1291
+ ```
1292
+
1293
+ - 解剖从单层 `root` 变成 `root`(锚点)+ `indicator`(角标),定位归组件自己管,
1294
+ 不再要宿主手写 `position: relative` 与负偏移。
1295
+ - 新增 `placement`:`top-end`(默认)/ `top-start` / `bottom-end` / `bottom-start`,
1296
+ 用逻辑属性写,rtl 下自动落到另一侧。
1297
+ - `size` 换的是圆点直径、两位数时的最小宽度与字号,不再是药丸那套内衬与行高。
1298
+ - Vue 侧另出 `XhBadgeRoot` / `XhBadgeIndicator`,要往角标里塞自定义内容时用它们。
1299
+
1300
+ **破坏性**:删掉 `variant`;行内的状态药丸请改用 `tag`(`XhTagRoot` + `XhTagLabel`)。
1301
+ `data-size` 与 `data-tone` 从 `root` 挪到 `indicator`。
1302
+
1303
+ - 3c033ca: 通知按卡片重排:左侧类型字形、右上角关闭钮、两列网格。
1304
+
1305
+ 它的皮肤是从旧的 toast 卡片逐字搬来的,搬完没人按「通知该长什么样」审过一遍,
1306
+ 于是留下三处硬伤:
1307
+
1308
+ - **叉掉到了卡片左下方**。`item` 是竖排 flex,而叉上写着
1309
+ `align-self: flex-start` + `margin-inline-start: auto`——交叉轴上的 auto 外边距
1310
+ 会让对齐属性整条失效(flexbox §9.6),`align-self` 那行一点作用都没有,
1311
+ 叉成了正文下面的第三行。实测它落在距卡片顶 55px 处,卡片因此高出一截。
1312
+ 三家参考实现(Ant Design / Element Plus / Naive UI)都是绝对定位钉在右上角内衬处。
1313
+ - **组件路径下一个类型指示物都没有**。徽记只由服务档的默认模板画,
1314
+ 12 份示例与所有 Web Components 使用者拿到的卡片,语气全靠起始侧那条 4px 色条承载,
1315
+ 而它压在卡片底上只有 1.9–2.8:1,`loading` 与 `info` 除颜色外完全同形。
1316
+ - **字号比轻提示还小一档**(13px),标题与说明只差 7.7%,两层文字挤成一片。
1317
+
1318
+ 现在:
1319
+
1320
+ - 新增 `item-indicator` 部件。作者留空即由皮肤按 `data-type` 画一枚兜底字形
1321
+ (info / success / warning / error 各一枚,`loading` 给转圈),
1322
+ 颜色取 `--xh-_tone-fg`——与 alert 的状态图标同档,压在卡片底上十二组最低 4.08:1。
1323
+ - **两列网格**:左列字形、右列标题与说明;叉绝对定位钉在右上角,标题自动让位
1324
+ (写法照 dialog / drawer)。起始侧那条语气色条随之删除——三家都没有,
1325
+ 语气改由字形承载。
1326
+ - 卡片宽 320 → 384px(`--xh-overlay-max-w-lg`,与 Ant Design 同值),
1327
+ 内衬四边 16px,字号回到正文档 14px。
1328
+ - 服务档的默认模板改成四个节点平铺(不再套一层皮肤够不着的行容器),
1329
+ 说明部件恒渲染——`aria-describedby` 是无条件发的,节点缺席就成了悬空引用。
1330
+ - 地标 `role="region"` 从 `root` 搬到 `group`。root 是 `display: contents` 的作用域包装,
1331
+ 量出来 0×0,地标挂在它身上跳过去落不到任何看得见的地方;那一摞才是真盒子。
1332
+
1333
+ 顺带补上三处从来没有门禁看管的地方:`check-elevation-role`、`check-press-feedback`、
1334
+ `check-clear-trigger` 三份名单都没登记过 notification,眼下合规纯属巧合。
1335
+
1336
+ **破坏性**:删掉 `--xh-notification-accent` 与 `--xh-notification-accent-width`
1337
+ 两个覆盖槽(色条没了)。另有几个槽的默认值变了:`--xh-notification-w`(20rem → 24rem)、
1338
+ `--xh-notification-py` / `-px`(12/16 → 16/16)、`--xh-notification-font-size`(13 → 14)、
1339
+ `--xh-notification-gap` 的语义从「行距」改为「图标与正文的列距」(行距另开
1340
+ `--xh-notification-row-gap`)。地标从 root 挪到 group,按 `root[role=region]` 写过
1341
+ 自动化断言的要跟着改。
1342
+
1343
+ - 1590d92: Select 的盒不再自带宽度上限,框宽交回布局;视觉行为变更。
1344
+
1345
+ `[data-part='control']` 上原有一条 `max-inline-size`,兜底取 `--xh-overlay-max-w`(20rem / 320px)。
1346
+ 浮层的宽度预算被搬到了在流内排布的表单控件上:格子一旦宽过 320px,select 就停在 320px 不再跟着长——
1347
+ 两列栅格的弹窗里,左边的 select 比右边的数字输入框窄一截。硬上限也不是必需的:`value-text` 与
1348
+ `trigger` 各有 `min-inline-size: 0` 配省略号,长值撑不破盒。
1349
+
1350
+ 同族的 cascader / tree-select / popselect / color-picker,以及 text-field / number-field,
1351
+ control 上都没有上限,select 是唯一一家。这条删掉之后全族同形。
1352
+
1353
+ 破坏性变更:**覆盖槽 `--xh-select-control-max-w` 随之移除**。此前写过
1354
+ `--xh-select-control-max-w: 24rem` 的,改在自己的布局层给 select 的根或所在格子写宽度
1355
+ (`inline-size` / `max-inline-size`),效果一致且对同族其余控件通用。
1356
+
1357
+ `check-family-parity` 的下拉族 control 名单补上 `max-inline-size`:往后任何一家单独给盒封顶都会被拦下。
1358
+ 公开面基线(`tooling/public-surface.json`)需随本次改动跑一次 `pnpm surface:update`。
1359
+
1360
+ - f4d3708: 轻提示改成短消息的样子:顶部居中、宽度包着内容、一行图标加一句话。
1361
+
1362
+ 上一版把 toast 从通知卡片收窄成操作反馈时只动了结构,皮肤还是照着卡片那份抄的——
1363
+ 定宽 320px、竖排、起始侧一条 4px 语气色条、行尾一颗叉。一句「已保存」于是撑成一个
1364
+ 方块,右边留着一大片空白,看着仍然像一则公告。
1365
+
1366
+ 现在它是这样:
1367
+
1368
+ ```
1369
+ ┌──────────────────┐
1370
+ │ ✓ 已保存 │ ← 贴着文字收缩,顶部居中
1371
+ └──────────────────┘
1372
+ ```
1373
+
1374
+ - **收缩包裹**:`inline-size` 的默认值从 `--xh-overlay-max-w` 改成 `auto`,
1375
+ 上限压在 `min(48rem, 100%)`,长文案在上限处换行、仍然居中。
1376
+ - **单行横排**:`flex-direction` 去掉,`align-items: center`;标题吃掉剩余宽度,
1377
+ 操作钮与叉自动落到行尾(两者不再 `align-self: flex-start`)。
1378
+ - **矮一档**:纵内衬从面档(12px)换成控件档 `--xh-field-py`(8px),条子高 39px,
1379
+ 与 Element Plus message 的 39px 齐平、比 Ant Design message 的 40px 矮 1px。
1380
+ - **语气走淡底**:底与描边取语气层的 `--xh-_tone-subtle` / `--xh-_tone-border`
1381
+ (与 alert 同一套口径),正文留中性——正文也跟着兑成语气色的话,绿字压绿底是整条里
1382
+ 对比度最差的一处。起始侧那条 4px 色条随之删除。
1383
+ - **字号回到正文档**:13px → 14px;标题不再加粗、不再换行高,一句话的反馈没有主次之分。
1384
+ - **状态字形不带圆底**:服务档的默认模板改用新的 `typeGlyph`(16px 裸字形,颜色取
1385
+ `--xh-_tone-fg`,与 alert 的状态图标同档),圆底徽记 `typeBadge` 留给对话框那种有余裕的版面(通知的类型字形由皮肤在 `item-indicator` 上画)。
1386
+ - **到点自己走的不出关闭按钮**:`createToastService` 的默认模板据此分两档——
1387
+ 会自己消失的不出叉(三家参考实现都是这样),`loading` 与 `duration <= 0` 这种走不掉的
1388
+ 反过来默认出叉,否则界面上一个可点、可聚焦的节点都没有。两档都能用 `closable` 显式改口。
1389
+
1390
+ **破坏性**:删掉 `--xh-toast-accent` 与 `--xh-toast-accent-width` 两个覆盖槽(色条没了)。
1391
+ 另有四个槽的默认值变了:`--xh-toast-w`(20rem → auto)、`--xh-toast-bg`
1392
+ (`--xh-bg-surface-raised` → 语气淡底)、`--xh-toast-border`(中性 → 语气描边)、
1393
+ `--xh-toast-title-font-weight`(semibold → regular);`--xh-toast-close-size` 的默认值
1394
+ 从 `--xh-control-h-sm`(28px)降到 `--xh-control-action-size`(24px)。
1395
+ 靠「轻提示是 320px 定宽」做过对齐、或依赖默认那颗叉关闭常驻提示的用法要跟着改。
1396
+
1397
+ - 5a1aedd: 轻提示与通知分家:新增 notification,toast 收窄成操作反馈,toaster 删除。
1398
+
1399
+ 原先 toast 一个组件担了两件事——「用户刚点了一下,告诉他结果」和「系统主动推来一条消息」。
1400
+ 两者的信息量、停留时长、落位习惯、谁触发都不一样,混在一起的结果是标题加正文两层文本、
1401
+ 九宫格落位、堆叠上限这些只有后者需要的东西全压在轻提示上,而轻提示自己反倒要靠一个
1402
+ 额外的容器组件才能用起来。
1403
+
1404
+ **通知(新增)**
1405
+
1406
+ ```vue
1407
+ <XhNotificationRoot v-slot="{ create, dismiss }">
1408
+ <XhNotificationGroup>
1409
+ <template #default="{ item }">
1410
+ <XhNotificationItem :id="item.id" :title="item.title" :description="item.description">
1411
+ <XhNotificationItemIndicator />
1412
+ <XhNotificationItemTitle />
1413
+ <XhNotificationItemDescription />
1414
+ <XhNotificationItemCloseTrigger />
1415
+ </XhNotificationItem>
1416
+ </template>
1417
+ </XhNotificationGroup>
1418
+ </XhNotificationRoot>
1419
+ ```
1420
+
1421
+ 队列与卡片是同一个组件的两层:`root`(队列的作用域包装)/ `group`(某个位置上的那一摞,也是 `role=region` 的地标)/ `item` 起是单条卡片。
1422
+ 九宫格落位、`max` 上限、同 id 就地改写、逐条计时与暂停都在这里。
1423
+ Web Components 侧是 `<xh-notification>` 与 `<xh-notification-item>`。
1424
+
1425
+ 单条卡片的生命周期复用 toast 那台机器——「会自己消失的卡片」这一行为与消息来源无关。
1426
+
1427
+ 通知另有命令式的 `createNotificationService`:推送连接的回调、后台任务的收尾、
1428
+ 拦截器里的一条系统消息,调用点都在组件之外,让它们各自去找一份队列上下文并不现实。
1429
+ 队列要长在页面结构里(通知中心那一栏自己排版)时用组件形态,两者不共享队列。
1430
+
1431
+ **轻提示(收窄)**
1432
+
1433
+ - 解剖去掉 `description`:一次操作的结果一句话说得完,说不完的那是通知。
1434
+ - 新增 `group` 部件:同时在场的几条叠成一摞。这一摞由全局服务渲染,没有对应的容器组件——
1435
+ 反馈落在哪儿是整个服务的口径,不该让每个业务页面各挂一份容器再各自决定。
1436
+ - `createToastService` 的队列改为服务内部私有,`info` / `success` / `warning` / `error` /
1437
+ `loading` / `create` / `update` / `dismiss` / `dismissAll` 签名不变,调用点零改动。
1438
+ 服务选项新增 `placement`(默认 `top`)、`max`(默认 5)、`gap`。
1439
+
1440
+ **破坏性**
1441
+
1442
+ - 删除 toaster:`XhToasterRoot` / `XhToasterGroup` / `useToaster` / `<xh-toaster>` /
1443
+ `connectToaster` / `toasterMachine` / `toasterAnatomy` / `@xihan-ui/styles/toaster.css` 等
1444
+ 一并移除。组件树内的通知队列改用 notification,命令式轻提示继续用 `createToastService`。
1445
+ - toast 删掉 `description` 部件与 `getDescriptionProps`;`<xh-toast>` 的 `description` 属性同时移除。
1446
+ 机器上的 `description` prop 保留——notification 的卡片复用同一台机器。
1447
+ - `ToastOptions` / `ToastRecord` 不再带 `placement`:轻提示的落位归服务,不逐条各去一处。
1448
+ - 覆盖槽 `--xh-toaster-inset` / `--xh-toaster-layer` 改名为 `--xh-notification-inset` /
1449
+ `--xh-notification-layer`;`--xh-toast-description-*` 随部件一起移除。
1450
+
1451
+ ### Minor Changes
1452
+
1453
+ - 7f8021e: 日期区间的框选改成逐行横杠,面板数按区间跨不跨页现算,面板号写在日历上一处即可。
1454
+
1455
+ **区间底色画成了一整块实心方块。** 底色铺在格子的背景上,格子上下的内衬也算背景区,
1456
+ 而行与行之间没有间距——七月一整月被选中时,五行底色首尾相接连成一个大方块,
1457
+ 两端那两枚圆点像是被按在方块上,看不出区间是一天一天连起来的。
1458
+
1459
+ 底色改由格子的 `::before` 铺:横向铺满格子,相邻两格接成一条;纵向收在格子内衬里,
1460
+ 行与行之间留出 4px 空当。每一行的行首与行尾各自收圆,跨周的区间于是是一行一条两头圆的横杠。
1461
+ 摆了周序号格的行里,行首那一格排在周序号后面,圆角跟着落到它身上。
1462
+
1463
+ **两端那一格只铺半格**,另外半格由选中圆片占满:区间收在圆点上而不是收在格子边上。
1464
+ 起止落在同一天时两条一起生效,底色宽度归零,只剩那枚圆点。
1465
+
1466
+ **邻月的日子不再吃区间底色与选中圆片。** 并排两张面板里同一天会各出现一次
1467
+ (7 月 31 日既在七月的末行、也在八月的首行),两张都画就成了两个端点、两段底色。
1468
+ 邻月的日子回到「压暗的数字」这一档。
1469
+
1470
+ **粗粒度视图的邻月判定修正。** 月/季度/年三档里格子的值是那一段的第一天,与面板起点比月份恒不相等,
1471
+ 于是除首格外整页都被判成邻月、整页压暗。这三档改用网格自报的 `inView`。
1472
+
1473
+ **区间默认铺几个面板改成现算**:已选的两端落在同一页里就一张,跨页才并排两张;
1474
+ 只落了一端(还在挑)时仍按两张算。日历同时恒渲染六行(新 prop `fixedWeeks`,默认开),
1475
+ 并排的两张面板等高,翻页时浮层高度也不再跟着月份变。
1476
+
1477
+ **面板号写在 `XhDatePickerCalendar` 上一处即可**:新增 `index` prop,面板内的
1478
+ `Heading` / `HeadingYearTrigger` / `HeadingMonthTrigger` / `Grid` / `Cell` 不写就跟着它走,
1479
+ 自己写了仍按自己写的算。此前这五个部件各要写一遍,漏掉任何一个都会静默落到面板 0——
1480
+ 两张面板显示同一个月份、第二张面板的邻月判定整片错位,都是这么来的。五个 prop 一并兼收字符串。
1481
+
1482
+ **快捷选项列的高度由并排的日历给。** 此前这一列按内容收、上限写死一档,
1483
+ 右侧那道分隔线只画到最后一条选项,比日历矮一截;它与旁边那张日历之间也补上了与两张日历之间同样的空当。
1484
+
1485
+ - 689ed0f: 13 个宿主的滚动层自带自绘滚动条:滚动时或指针在这一片时露出、静止后收起,浮在内容之上不占宽度。
1486
+
1487
+ **哪些宿主**:12 个浮层族的 `content`(cascader / color-picker / combobox / context-menu / date-picker / hover-card / mention / menu / pagination / popover / popselect / tree-select)与 json-viewer 的 `tree`、`text`,共 14 个滚动容器。条子由库自己建,作者一个部件都不用写:它是滚动层的兄弟,绝对定位贴在组件既有的壳上(浮层族是 `positioner`,json-viewer 是 `root`)。轴按各自的溢出方向摆——cascader 只摆横的,tree-select 与 json-viewer 竖横都摆、两条都溢出时各让出交叉口那一格,其余只摆竖的。
1488
+
1489
+ 挂上条子的容器带 `data-xh-scrollbar`(挂在它身上的条数),皮肤据此把原生条藏成零宽:容器的可用宽度一点不减,也不再需要为原生条留空道。露面时机、尺寸档、拖动、触屏交给原生滚动这些全是 `scrollbar` 那一套,与手写 `<XhScrollbar>` / `<xh-scrollbar>` 挂上去的完全一致,缺省档是 `scroll-hover`。
1490
+
1491
+ **json-viewer 换档跟随**:树档与原文档互斥,换档时条子跟到此刻在场的那个容器,节点不重建(换档不会把滚动条闪一下)。
1492
+
1493
+ **按在 `positioner` 上不再消解浮层**:条子住在 `positioner` 里、是 `content` 的兄弟,浮层的层分支因此把 `positioner` 一并记上——不记的话按住条子拖动那一下会被判成层外交互,面板当场收起。副作用是 `positioner` 的其他子节点也算进了层内:吃指针的只有 combobox 的 `empty` 空态占位,按它不再关闭候选面板(此前会关)。其余 11 个浮层的 `positioner` 除了条子没有吃指针的子节点(`positioner` 自身是 `pointer-events: none`),按在面板之外仍照旧消解。
1494
+
1495
+ **皮肤侧要跟着改的**:自带皮肤给这 13 个壳补了 `--xh-scrollbar-track-bg: transparent`(浮在内容上的条子不该有实色轨道),json-viewer 的 `root` 补了 `position: relative`(条子贴它的内边距盒)。第三方皮肤若整份接管这些 part,同样要给壳一个定位上下文,并把轨道底色关掉。滚动条自身的 `root` 补了 `pointer-events: auto`,抵消 `positioner` 那句 `none`。
1496
+
1497
+ - 843e17a: json-viewer 补原文视图:`view="text"` 直接出缩进过的 JSON 原文。
1498
+
1499
+ 树档是拿来"翻"的——折叠、逐层看结构;而"核对这份报文与后端下发的是不是一字不差"、
1500
+ "把它整段拷走"这两件事树档做不到:值受 `maxStringLength` 截断、成员受 `maxItems` 折减,
1501
+ 分支摘要与把手还带 `user-select: none`,框选拿到的不是原文。原文档就是补这一件事,
1502
+ 因此它刻意不吃那两个折减选项。
1503
+
1504
+ `api.text` 在两档下都取得到,作者要做"复制原文"按钮时不必自己再序列化一遍。
1505
+ 序列化与树同源:同一个 `jsonEntries` 排键(`sortKeys` 一样生效)、同一条祖先链判环
1506
+ (环落成 `"[Circular]"`,两条不相干分支共享同一个对象照样摊开),
1507
+ `bigint` / `undefined` / 函数这些 JSON 没有写法的值退回树上那份文本并按字符串写出,
1508
+ 整份始终解析得动。
1509
+
1510
+ 新增 headless 出口 `jsonText` 与类型 `JsonViewerView`,解剖新增 `text` 部件。
1511
+ 皮肤与树档共用同一套边框、内衬与高度令牌,两档切过去盒子不跳。
1512
+
1513
+ - ec93d6b: 浮层里的条目之间加 2px 行距,新增语义令牌 `--xh-list-option-gap` 统一这把尺。
1514
+
1515
+ **下拉里选中项与悬停项贴成一整块。** a11 的选中蓝底与 b22 的悬停灰底之间没有一丝缝,
1516
+ 两块底色首尾相接,读起来像一条被涂了两截颜色的长条而不是两个条目。
1517
+
1518
+ **库内自己就有三种方言**:浮层选项列(time-picker / date-picker 的时间列与预设列)已经是
1519
+ 2px,页面导航列(side-nav / navigation-menu)是 4px,下拉、菜单、树这一族是 0。补上 2px
1520
+ 是把这一族拉回库内既有的口径。
1521
+
1522
+ `list` 组的描述原文写着「option-\* 给浮层里的条目——菜单项、下拉选项、树行、时间列」,
1523
+ 新令牌落在这一组:`--xh-list-option-gap: 2px`。compact 档不覆盖,2px 已是最小档。
1524
+
1525
+ 22 个条目的直接父容器接上这把尺:select 的 `list`;combobox / listbox 的 `content` 与
1526
+ `item-group`;popselect 与 mention 的 `content`;menu / menubar / context-menu 的 `content`
1527
+ 与 `group`;cascader 的 `column` 与 `search-list`;tree 与 tree-select 的 `tree`、
1528
+ `branch-content`、`branch`;transfer 的 `list`。装 list 加 footer 的外壳(select /
1529
+ tree-select 的 `content`)不接——它不是条目的父层。json-viewer 也不接,只读数据视图与
1530
+ table、log 同为紧排一档。
1531
+
1532
+ `tree` 与 `tree-select` 的 `branch` 此前是块盒,为接这把尺改成纵向 flex,tree-select 同时
1533
+ 补上此前缺的 `[hidden]` 兜底。
1534
+
1535
+ 节奏顺手收一级,加了 gap 之后总量不变:combobox 与 listbox 的组间距 8px → 6px,
1536
+ menu / menubar / context-menu 的分隔线外边距 4px → 2px。time-picker 那两处等值的
1537
+ `--xh-space-0_5` 改指新令牌,视觉不变。
1538
+
1539
+ 这把尺打在容器上,所以分组标题与它下面第一条之间同样多出 2px——分组标题是 `group`
1540
+ 的第一个子元素,与条目同属一层 flex 子项。
1541
+
1542
+ - 8fc5f05: 上一页 / 下一页默认就是两枚箭头。
1543
+
1544
+ 原先库里一个字都不产出,可见内容全靠作者往插槽里塞——于是每份示例都手写了
1545
+ 「上一页 / 下一页」四个字,翻译、宽度与图标风格全归使用者自己操心。
1546
+
1547
+ 皮肤补上兜底字形:两个把手为空时各画一枚 chevron,走既有的 `--xh-glyph-mark-*` 令牌
1548
+ (mask + currentColor,跟着语气、悬停与禁用一起变色)。rtl 下两枚对调,指向行进方向。
1549
+ 作者往部件里塞了自己的图形或文字,`:empty` 即不命中,原样让位。
1550
+
1551
+ 读屏名字一如既往来自 `translations.prevTrigger / nextTrigger`,不受影响——
1552
+ 去掉的只是可见文字,不是可及名字。
1553
+
1554
+ - 1a36b7e: 省略号能摊开了:折进去的那几页现在有路走到。
1555
+
1556
+ 原先省略位是 `aria-hidden` + `pointer-events: none` 的死占位,而 `pages` 序列
1557
+ 只说「这里折了一段」,说不出折的是哪几页——那几页除了手打跳页输入框没有任何入口。
1558
+
1559
+ 分页因此升级成浮层族,新增 `positioner` 与 `content` 两个部件:
1560
+
1561
+ ```vue
1562
+ <XhPaginationRoot v-slot="{ pageItems }" :count="2000" :page-size="10">
1563
+ <template v-for="item in pageItems">
1564
+ <XhPaginationEllipsis v-if="item.type === 'ellipsis'" :side="item.side" />
1565
+ <XhPaginationItem v-else :value="item.value">{{ item.value }}</XhPaginationItem>
1566
+ </template>
1567
+ <XhPaginationPositioner>
1568
+ <XhPaginationContent v-slot="{ pages }">
1569
+ <XhPaginationItem v-for="p in pages" :key="p" :value="p">{{ p }}</XhPaginationItem>
1570
+ </XhPaginationContent>
1571
+ </XhPaginationPositioner>
1572
+ </XhPaginationRoot>
1573
+ ```
1574
+
1575
+ - 新增 `api.pageItems`:与 `pages` 同一串序列,但省略位带着被折叠的那几页。
1576
+ `pages` 由它派生,两者的窗口数学只有一份。旧的 `pages` 写法一行不用改。
1577
+ - 悬停摊开(`openDelay` / `closeDelay`),**点一下也摊开**——纯悬停会把键盘用户挡在外面。
1578
+ Escape 与点外面都能收起(走消解层)。
1579
+ - 至多两个省略位,用 `side`(`'start' | 'end'`)区分;同时只开一个,一份定位层就够。
1580
+ Web Components 侧由作者在节点上写 `side="end"`,与页码按钮自报 `value` 同一套写法。
1581
+ - 浮层 portal 到统一落点,三视觉轴在 `positioner` 上重打一遍。
1582
+
1583
+ **破坏性**:`getEllipsisProps()` 改为收 `{ side }`;省略位从 `<span>` 变 `<button>`、
1584
+ 不再带 `aria-hidden`。
1585
+
1586
+ - 911d0b7: 每页条数控制器随分页一起给了。
1587
+
1588
+ ```vue
1589
+ <XhPaginationPageSizeSelect v-slot="{ options }">
1590
+ <option v-for="o in options" :key="o" :value="String(o)">{{ o }} 条 / 页</option>
1591
+ </XhPaginationPageSizeSelect>
1592
+ ```
1593
+
1594
+ 用**原生 `<select>`** 而不是再造一个浮层:档位就那么几档,浮层带不来什么,
1595
+ 却要多接一层定位、消解与键盘;原生控件在 Web Components 侧也一样能用,键盘天然可达。
1596
+ 不给插槽时按 `pageSizeOptions` 渲染默认档位。
1597
+
1598
+ 受控时会把 DOM 的选中项同步回填:宿主不写回的话,用户改过的原生 select 与真正生效的
1599
+ 档位会对不上,而 vdom 那边没有变化就不会打补丁——这一条两个适配器共用。
1600
+
1601
+ - abe790b: 滚动条新增 `scroll-hover` 档,并把它定为缺省档。
1602
+
1603
+ **新增 `'scroll-hover'`**:滚动时露出,指针进入滚动容器或滚动条时也露出;指针占着容器时滚动只重画滑块、不起收起倒计时,指针离开或停手满 `hideDelay` 才收起。它是 `hover` 与 `scroll` 两档显形条件的并集,与那两档一样浮在内容之上——`data-lane-*` 的判据只认 `auto` / `always`,视口宽度一点不减(横条同理不占高度)。
1604
+
1605
+ **缺省档由 `'hover'` 改为 `'scroll-hover'`**:`scrollbar` 与 `scroll-area` 不写 `type` 时都走新档。显形集合是原缺省档的严格超集,没有一条本来看得见的滚动条会消失;占道与否、触屏交给原生滚动那一路都不变。
1606
+
1607
+ **需要跟着改的代码**:对 `ScrollbarType` 做穷尽 `switch` / 映射表的地方要补 `'scroll-hover'` 分支;读 `ScrollbarApi.type` 或 `data-type` 并按值分派的代码会收到这个新值。
1608
+
1609
+ 状态机的两个判据改了名:`isHoverType` → `showsOnHover`、`isScrollType` → `showsOnScroll`(原名在新档下会读成谎话)。判据名只在机器内部与文档的「状态机」小节露面,不进公开 API。
1610
+
1611
+ 日志的视口那条 `scrollbar-gutter` 收窄到「还在用原生条」的情形:带 `data-xh-scrollbar` 的容器原生条已被藏成零宽,空道对它没有布局作用。没挂自绘条时空道照留,原生滚动条出现与消失仍不推动文字。
1612
+
1613
+ - 918b870: 实心底上的前景色与交互态挪动方向改由语气色现推,换肤下配对自动成立。
1614
+
1615
+ `--xh-_tone-on` 此前按语气族写死:brand / neutral / danger 配白字,success / warning / info 配深字。这个分派是按本仓这套 600 档实测出来的,但 `--xh-_tone` 是可换肤的——使用者把各族的 600 改写成自己的调色板,配对的前提就没了,而门禁只验本仓的色,失配一路绿灯。
1616
+
1617
+ 改成从 `--xh-_tone` 现推:把语气色转成线性 sRGB 分量、按 WCAG 的权重算出相对亮度,低于 0.179 配白字、高于配深字。0.179 是白字与黑字对比度相等的那一点,解析解,不是估的。
1618
+
1619
+ `--xh-_tone-shift`(交互态往哪个方向挪)一并现推,取前景的反面。只推前景不推方向会出净回归:静态态救回来了,悬停与按下却把底往前景那一侧挪——某消费方调色板上实测 danger 静态 4.70 而悬停掉到 3.78、按下 3.18。
1620
+
1621
+ 用相对颜色语法 `color(from … srgb-linear …)`,高于本仓的浏览器地板(Chrome 119 / Firefox 128 / Safari 16.4 起),所以整块包在 `@supports` 里,逐族写死的那一份留着当兜底;探针按实际用到的整个形态测,只测得起半截语法的引擎不会落进坏值。alpha 显式写 1,不然会连语气色的透明度一起继承。
1622
+
1623
+ 某消费方调色板上的实测(浅色 / 深色,静态 · 悬停 · 按下):
1624
+
1625
+ 改前 info 3.94 · danger 4.47 不达标
1626
+ 改后 六族最低 4.70,全部达标
1627
+
1628
+ 本仓自己这套色只升不降:success 6.51→6.90、warning 7.36→7.80、info 5.71→6.05、深色 brand 5.32→5.64。
1629
+
1630
+ 三条门禁跟着改:
1631
+
1632
+ - `check-tone-contrast` 此前只验写死的那一份。现在把 `@supports` 块单独解析,现推档与兜底档**各验一遍**(158 组配对,原 122 组),并按 tone.css 里那两条式子的形态逐字对账——式子改了形态对不上就失败。
1633
+ - `check-css-floor` 新增「受 `@supports` 守卫的增强」这一档并登记相对颜色语法:块外出现即失败。此前它对这条语法是静默的,等于谁都能悄悄抬底线。
1634
+ - `check-token-refs` 不再把注释里提到的颜色、`@supports` 的探测条件、以及 `fn(from var(--xh-…) …)` 当成颜色字面量——前两者不落到任何元素上,后者的源色本身就是令牌。
1635
+
1636
+ - a69cead: 树补一条 `leafOrientation`:末端那一层可以横排。
1637
+
1638
+ 只作用于「子节点全是叶子」的那一层——菜单授权里就是按钮那层。一个菜单下十几个按钮,
1639
+ 横排一行铺完,省掉大量纵向翻找:
1640
+
1641
+ ```vue
1642
+ <XhTreeRoot :collection="menus" leaf-orientation="horizontal" />
1643
+ ```
1644
+
1645
+ **中间层与整棵树恒是竖排,不提供开关。** 它们承载的是层级本身,横过来层级就读没了。
1646
+ 判据是「这一层不再往下分」而不是「深度等于几」:同一棵树里各枝深浅不一,
1647
+ 按深度判会把浅枝的中间层也横过来。
1648
+
1649
+ **方向键不跟着改。** 树上左右是层级操作(收起 / 展开、回父层 / 进子层)、上下走可见行,
1650
+ 这是 treeview 的规范语义,横排只是排布。
1651
+
1652
+ 顺带修一处:叶子行在竖排下会自己补出「箭头那一格」与同级分支对齐,横排下补出来的
1653
+ 是节点之间的空隙而不是层级,那条规则因此按行盒**所在的那层容器**判定方向。
1654
+
1655
+ ### Patch Changes
1656
+
1657
+ - 33af800: 角标的三档尺寸:每一档的盒都抬到大于字号。
1658
+
1659
+ 原先 sm 档的盒高与字号都是 12px,两位数会把角标撑破。字号刻度最小就是 12px
1660
+ (`--xh-font-size-xs`),所以往上抬盒子而不是往下压字:盒 16 / 20 / 24,
1661
+ 字 12 / 13 / 14,圆点 6 / 8 / 10,三条阶梯各自严格递增。
1662
+
1663
+ - 9294172: 日历的「大步翻」两颗钮不再被无条件收掉,« » 与 ‹ › 同一副长相。
1664
+
1665
+ 四条规则的选择器列表里,限定只跟在后两条上:`[data-part='prev-year-trigger'], [data-part='prev-trigger'][hidden], [data-part='next-year-trigger'], [data-part='next-trigger'][hidden]` —— 年那两颗是裸的,于是无条件命中 `display: none`,翻年的钮从来就没画出来过。行为层一直是通的(`canGoPrevYear` / `stepYear` / `getPrevYearTriggerProps` 都在),只是看不见也点不着。
1666
+
1667
+ 同样的漏写还在悬停、禁用、聚焦环三条上:那三条把月钮的状态样式无条件加在了年钮身上。四条一并补齐限定。
1668
+
1669
+ 新增 `tests/browser/calendar-nav-skin.spec.ts` 钉住:四颗翻页钮都画得出来、同一副尺寸,打上 `hidden` 才收起,禁用态四颗同一副长相。这类「被一条 display 悄悄收掉」只有在真实浏览器里按级联算才验得出来。
1670
+
1671
+ - 8a44b64: 四处被祖先 `overflow` 裁掉的聚焦环改成往内收。
1672
+
1673
+ `outline` + 正的 `outline-offset` 把环画在元素盒外面,祖先只要是
1674
+ `overflow: hidden / auto / scroll`,环就会被裁掉一截——键盘用户看到的是
1675
+ 三条边或者两侧缺口的半圈蓝环。四处改为 `outline-offset: calc(-1 * var(--xh-ring-width))`,
1676
+ 环整圈落在盒内:
1677
+
1678
+ - image-cropper 的 `crop-area` 与 `crop-handle`:两者都长在 `viewport` 里,
1679
+ 那层 `overflow: hidden` 同时还替暗遮罩(`box-shadow: 0 0 0 9999px`)收边。
1680
+ - heatmap 的 `grid`:`root` 为一整年五十几列备了 `overflow-x: auto`。
1681
+ - table 与 transfer 的 `select-all-trigger`:同文件的邻居
1682
+ (table 的 `row` / `sort-trigger`、transfer 的 `search` / `item`)本就是内收写法,
1683
+ 这两处是仅剩的外扩。
1684
+
1685
+ `--xh-ring-offset` 令牌本身不动:库里另有 9 条规则写着
1686
+ `calc(-1 * var(--xh-ring-offset))`,翻令牌的符号会把它们一起翻成外扩。
1687
+
1688
+ - ed51531: 菜单族的勾选标记跟着语气走。
1689
+
1690
+ `context-menu` 与 `menubar` 都有 `tone` 轴、也都在根上发 `data-tone`,但 `item-indicator`
1691
+ 的颜色写死在 `--xh-fg-brand`:把菜单标成 `tone="danger"`,整条菜单换了族,勾选标记还是品牌蓝。
1692
+ 同族的 `select` / `popselect` / `combobox` / `cascader` / `tree-select` 五家早就是跟着语气走的,
1693
+ 只有这两家掉队。
1694
+
1695
+ 两家的颜色链改成 `var(--xh-<组件>-item-indicator-fg, var(--xh-_tone, var(--xh-fg-brand)))`:
1696
+ 写了语气跟语气,没写落回 `--xh-fg-brand`——**没写 `tone` 的用法一个像素都不变**。
1697
+ `listbox` 不动,它没有语气轴,链尾就是全部。
1698
+
1699
+ - 3ed6b9f: 描边档的标签改用「可操作区边界」那一档语气色,轮廓看得清了。
1700
+
1701
+ `outline` 的边就是这枚标签的全部轮廓——它没有底色,边没了就只剩一行字。此前取的是 `--xh-_tone-border`(语气色兑四成面色),而 tone.css 自己就注明这一支六族都落在 1.44–2.18,当轮廓根本分不出边界。
1702
+
1703
+ 改取 `--xh-_tone-border-control`:那一支是为「可操作区边界要 3:1」准备的,取的是语气本体。在某消费方的调色板上实测,边相对面从 1.59–1.76 抬到 4.56–7.83(浅色)、2.13–3.42(深色),文字那一档没动(5.14–9.35)。
1704
+
1705
+ `--xh-tag-border` 这个使用者槽仍排在最前,要另配一档边照旧写它。
1706
+
1707
+ - 87f5b73: 装饰档 `--xh-_tone-soft` 从 500 提到与控件边界同一档
1708
+
1709
+ 色条、指示条、锚点高亮这些用装饰档画的东西是非文字图形,按 WCAG 1.4.11 要 3:1。
1710
+ 500 档压在浅色画布上,success 2.29、warning 1.92、info 2.75 都够不到。
1711
+
1712
+ `--xh-_tone-border-control` 早就为同一个阈值兜过底(warning 在浅色下取 700、
1713
+ neutral 在深色下取 550),要求完全一样,装饰档就直接跟着那一支走,六族十二组全部达标。
1714
+ `check-tone-contrast` 补上这条断言,覆盖从 110 组扩到 122 组。
1715
+
1716
+ 观感上所有色条、时间线指示点、锚点高亮会比原来重一档。
1717
+
1718
+ - b6fb182: 叶子行补上箭头那一格的缩进,不再比同级分支往行首缩。
1719
+
1720
+ 分支行的首格是展开箭头,叶子行没有这一格。作者摆了 `item-indicator` 时由它顶着,
1721
+ 可勾选档的首位直接是 `item-checkbox`——没有东西占位,叶子的文字就比所在分支的文字
1722
+ 往行首缩了一个间隙,第三级与第二级挤在同一条竖线上,层级关系读不出来。
1723
+
1724
+ 行盒改为在没有 `item-indicator` 时自己补出这一格(`:has()` 判定,摆了指示符的那档
1725
+ 不受影响、不会被重复缩进)。宽度取的是箭头与指示符共用的那两个令牌,
1726
+ 覆盖 `--xh-tree-indicator-size` / `--xh-tree-row-gap` 时跟着走。
1727
+
1728
+ - Updated dependencies [9ea57f6]
1729
+ - Updated dependencies [ec93d6b]
1730
+ - @xihan-ui/tokens@1.0.0-preview.0
1731
+
3
1732
  ## 1.0.0-alpha.3
4
1733
 
5
1734
  ### Major Changes
@@ -50,6 +1779,7 @@
50
1779
  把模态关掉)。
51
1780
 
52
1781
  ## 其余
1782
+
53
1783
  - `--xh-editable-preview-line-height` 删除,改用 `--xh-editable-preview-min-h`:预览态
54
1784
  原先拿行高冒充高度,实测比同组件的编辑态高 2px,切换时跳一下。
55
1785
  - tooltip 与 navigation-menu 入层栈,Escape 不再连它们下面的对话框一起关掉。