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

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 (110) hide show
  1. package/CHANGELOG.md +341 -0
  2. package/css/accordion.css +5 -0
  3. package/css/affix.css +5 -0
  4. package/css/alert.css +5 -0
  5. package/css/anchor.css +5 -0
  6. package/css/avatar-group.css +5 -0
  7. package/css/avatar.css +5 -0
  8. package/css/back-top.css +7 -2
  9. package/css/badge.css +5 -0
  10. package/css/breadcrumb.css +5 -0
  11. package/css/button-group.css +6 -1
  12. package/css/button.css +16 -1
  13. package/css/calendar.css +6 -1
  14. package/css/card.css +5 -0
  15. package/css/carousel.css +7 -2
  16. package/css/cascader.css +204 -24
  17. package/css/checkbox-group.css +25 -20
  18. package/css/checkbox.css +6 -1
  19. package/css/clipboard.css +8 -3
  20. package/css/code-block.css +5 -0
  21. package/css/collapsible.css +5 -0
  22. package/css/color-picker.css +27 -6
  23. package/css/combobox.css +42 -20
  24. package/css/composer.css +6 -1
  25. package/css/context-menu.css +62 -34
  26. package/css/countdown.css +5 -0
  27. package/css/date-field.css +10 -5
  28. package/css/date-picker.css +72 -3
  29. package/css/descriptions.css +5 -0
  30. package/css/dialog.css +69 -9
  31. package/css/drawer.css +33 -12
  32. package/css/dynamic-input.css +7 -2
  33. package/css/editable.css +11 -6
  34. package/css/ellipsis.css +5 -0
  35. package/css/empty-state.css +5 -0
  36. package/css/field.css +37 -1
  37. package/css/file-upload.css +8 -3
  38. package/css/flex.css +5 -0
  39. package/css/float-button.css +7 -2
  40. package/css/form.css +23 -6
  41. package/css/gradient-text.css +5 -0
  42. package/css/grid.css +5 -0
  43. package/css/highlight.css +5 -0
  44. package/css/hover-card.css +34 -17
  45. package/css/icon-wrapper.css +5 -0
  46. package/css/icon.css +5 -0
  47. package/css/image-viewer.css +196 -0
  48. package/css/image.css +5 -0
  49. package/css/infinite-scroll.css +5 -0
  50. package/css/layout.css +5 -0
  51. package/css/list.css +5 -0
  52. package/css/listbox.css +14 -6
  53. package/css/loading-bar.css +5 -0
  54. package/css/log.css +5 -0
  55. package/css/marquee.css +5 -0
  56. package/css/mention.css +33 -22
  57. package/css/menu.css +57 -23
  58. package/css/menubar.css +34 -25
  59. package/css/navigation-menu.css +6 -1
  60. package/css/number-animation.css +5 -0
  61. package/css/number-field.css +21 -16
  62. package/css/page-header.css +5 -0
  63. package/css/pagination.css +5 -0
  64. package/css/pin-input.css +11 -6
  65. package/css/popconfirm.css +44 -0
  66. package/css/popover.css +34 -11
  67. package/css/popselect.css +50 -16
  68. package/css/progress.css +73 -1
  69. package/css/qr-code.css +5 -0
  70. package/css/radio-group.css +6 -1
  71. package/css/rating.css +5 -0
  72. package/css/result.css +5 -0
  73. package/css/scroll-area.css +5 -6
  74. package/css/select.css +167 -17
  75. package/css/separator.css +5 -0
  76. package/css/side-nav.css +168 -0
  77. package/css/skeleton.css +5 -0
  78. package/css/slider.css +60 -0
  79. package/css/spinner.css +9 -1
  80. package/css/splitter.css +5 -0
  81. package/css/statistic.css +5 -0
  82. package/css/steps.css +7 -2
  83. package/css/switch.css +34 -0
  84. package/css/table.css +11 -6
  85. package/css/tabs.css +5 -0
  86. package/css/tags-input.css +12 -7
  87. package/css/text-field.css +28 -10
  88. package/css/thread.css +6 -1
  89. package/css/time-field.css +10 -5
  90. package/css/time-picker.css +42 -27
  91. package/css/time.css +5 -0
  92. package/css/timeline.css +5 -0
  93. package/css/toast.css +13 -12
  94. package/css/toaster.css +5 -0
  95. package/css/toggle-group.css +6 -1
  96. package/css/toggle.css +6 -1
  97. package/css/tone.css +22 -0
  98. package/css/toolbar.css +5 -0
  99. package/css/tooltip.css +39 -21
  100. package/css/tour.css +45 -21
  101. package/css/transfer.css +16 -9
  102. package/css/tree-select.css +45 -18
  103. package/css/tree.css +13 -5
  104. package/css/typography.css +5 -0
  105. package/css/virtualizer.css +5 -0
  106. package/css/watermark.css +5 -0
  107. package/index.css +6 -3
  108. package/index.unlayered.css +2325 -588
  109. package/package.json +4 -3
  110. package/css/motion.css +0 -58
@@ -0,0 +1,196 @@
1
+ @layer xihan.motion {
2
+ /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
3
+ 动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
4
+ @keyframes xh-fade-in {
5
+ from {
6
+ opacity: 0;
7
+ }
8
+
9
+ to {
10
+ opacity: 1;
11
+ }
12
+ }
13
+
14
+ @keyframes xh-fade-out {
15
+ from {
16
+ opacity: 1;
17
+ }
18
+
19
+ to {
20
+ opacity: 0;
21
+ }
22
+ }
23
+ }
24
+
25
+ @layer xihan.components {
26
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
27
+ [data-scope='image-viewer'] {
28
+ --xh-image-viewer-skin: 1;
29
+ }
30
+
31
+ [data-scope='image-viewer'][data-part='backdrop'] {
32
+ position: fixed;
33
+ inset: 0;
34
+ z-index: var(--xh-layer-modal);
35
+ /* 看片要压住页面底色,遮罩比普通对话框更深 */
36
+ background: var(--xh-image-viewer-backdrop-bg, color-mix(in oklab, var(--xh-color-neutral-950) 78%, transparent));
37
+ }
38
+
39
+ [data-scope='image-viewer'][data-part='backdrop'][data-state='open'] {
40
+ animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
41
+ }
42
+
43
+ [data-scope='image-viewer'][data-part='backdrop'][data-state='closed'] {
44
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
45
+ }
46
+
47
+ [data-scope='image-viewer'][data-part='positioner'] {
48
+ position: fixed;
49
+ inset: 0;
50
+ z-index: var(--xh-layer-modal);
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: center;
54
+ }
55
+
56
+ [data-scope='image-viewer'][data-part='content'] {
57
+ position: relative;
58
+ inline-size: 100%;
59
+ block-size: 100%;
60
+ display: flex;
61
+ align-items: center;
62
+ justify-content: center;
63
+ outline: none;
64
+ /* 深色底上的工具条与计数固定用浅字,不随主题翻 */
65
+ color: var(--xh-image-viewer-fg, var(--xh-color-neutral-0));
66
+ }
67
+
68
+ [data-scope='image-viewer'][data-part='content'][data-state='open'] {
69
+ animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
70
+ }
71
+
72
+ [data-scope='image-viewer'][data-part='content'][data-state='closed'] {
73
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
74
+ }
75
+
76
+ [data-scope='image-viewer'][data-part='viewport'] {
77
+ position: absolute;
78
+ inset: 0;
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ overflow: hidden;
83
+ cursor: grab;
84
+ /* 平移与滚轮都归指针会话,浏览器手势不抢 */
85
+ touch-action: none;
86
+ }
87
+
88
+ [data-scope='image-viewer'][data-part='viewport'][data-panning] {
89
+ cursor: grabbing;
90
+ }
91
+
92
+ [data-scope='image-viewer'][data-part='image'] {
93
+ max-inline-size: 90%;
94
+ max-block-size: 85%;
95
+ user-select: none;
96
+ /* 变换是内联 style 写的,只给缩放旋转一点缓动;拖拽平移要跟手,不缓 */
97
+ transition: transform var(--xh-duration-fast) var(--xh-ease-out);
98
+ }
99
+
100
+ [data-scope='image-viewer'][data-part='image'][data-panning] {
101
+ transition: none;
102
+ }
103
+
104
+ /* —— 悬浮件共用:半透明深底加浅字 —— */
105
+ [data-scope='image-viewer'][data-part='toolbar'],
106
+ [data-scope='image-viewer'][data-part='counter'],
107
+ [data-scope='image-viewer'][data-part='prev-trigger'],
108
+ [data-scope='image-viewer'][data-part='next-trigger'],
109
+ [data-scope='image-viewer'][data-part='close-trigger'] {
110
+ position: absolute;
111
+ z-index: 1;
112
+ background: var(--xh-image-viewer-chrome-bg, color-mix(in oklab, var(--xh-color-neutral-950) 55%, transparent));
113
+ color: inherit;
114
+ border: none;
115
+ border-radius: var(--xh-shape-pill);
116
+ }
117
+
118
+ [data-scope='image-viewer'][data-part='toolbar'] {
119
+ inset-block-end: var(--xh-space-6);
120
+ inset-inline-start: 50%;
121
+ translate: -50% 0;
122
+ display: flex;
123
+ align-items: center;
124
+ gap: var(--xh-space-1);
125
+ padding: var(--xh-space-1_5) var(--xh-space-3);
126
+ }
127
+
128
+ /* 工具条里的按钮:底色交给工具条,本体只管命中区 */
129
+ [data-scope='image-viewer'][data-part='toolbar'] > [data-part] {
130
+ position: static;
131
+ display: inline-flex;
132
+ align-items: center;
133
+ justify-content: center;
134
+ inline-size: var(--xh-control-action-size);
135
+ block-size: var(--xh-control-action-size);
136
+ background: none;
137
+ border: none;
138
+ border-radius: var(--xh-shape-control);
139
+ color: inherit;
140
+ font-size: var(--xh-glyph-size-sm);
141
+ cursor: pointer;
142
+ }
143
+
144
+ [data-scope='image-viewer'][data-part='counter'] {
145
+ inset-block-start: var(--xh-space-6);
146
+ inset-inline-start: 50%;
147
+ translate: -50% 0;
148
+ padding: var(--xh-space-1) var(--xh-space-3);
149
+ font-size: var(--xh-font-size-sm);
150
+ }
151
+
152
+ [data-scope='image-viewer'][data-part='prev-trigger'],
153
+ [data-scope='image-viewer'][data-part='next-trigger'],
154
+ [data-scope='image-viewer'][data-part='close-trigger'] {
155
+ display: inline-flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ inline-size: var(--xh-control-h-lg);
159
+ block-size: var(--xh-control-h-lg);
160
+ font-size: var(--xh-glyph-size-md);
161
+ cursor: pointer;
162
+ }
163
+
164
+ [data-scope='image-viewer'][data-part='prev-trigger'] {
165
+ inset-inline-start: var(--xh-space-6);
166
+ inset-block-start: 50%;
167
+ translate: 0 -50%;
168
+ }
169
+
170
+ [data-scope='image-viewer'][data-part='next-trigger'] {
171
+ inset-inline-end: var(--xh-space-6);
172
+ inset-block-start: 50%;
173
+ translate: 0 -50%;
174
+ }
175
+
176
+ [data-scope='image-viewer'][data-part='close-trigger'] {
177
+ inset-block-start: var(--xh-space-6);
178
+ inset-inline-end: var(--xh-space-6);
179
+ }
180
+
181
+ [data-scope='image-viewer'] button[data-part]:focus-visible {
182
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
183
+ outline-offset: var(--xh-ring-offset);
184
+ }
185
+
186
+ [data-scope='image-viewer'] button[data-part]:disabled {
187
+ opacity: var(--xh-state-disabled-opacity);
188
+ cursor: not-allowed;
189
+ }
190
+
191
+ @media (prefers-reduced-motion: reduce) {
192
+ [data-scope='image-viewer'][data-part='image'] {
193
+ transition: none;
194
+ }
195
+ }
196
+ }
package/css/image.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='image'] {
4
+ --xh-image-skin: 1;
5
+ }
6
+
2
7
  /* 尺寸交给三个变量而不是写死:这个组件既要能当封面图,也要能当头图与缩略图。
3
8
  给了 --xh-image-ratio 才有确定高度,回退内容与图片在同一个盒子里换人时不会跳。 */
4
9
  [data-scope='image'][data-part='root'] {
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='infinite-scroll'] {
4
+ --xh-infinite-scroll-skin: 1;
5
+ }
6
+
2
7
  /*
3
8
  * 列表外壳这一层刻意不给盒模型:滚的是页面还是作者自己的 overflow 容器、列表长什么样,
4
9
  * 都归作者的布局管,组件只往它身上挂 data-state / data-loading / data-disabled 供作者取用。
package/css/layout.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='layout'] {
4
+ --xh-layout-skin: 1;
5
+ }
6
+
2
7
  /* 页面骨架排成三行两列:头与脚各横贯一整行,侧栏与内容并排占中间那一行。
3
8
  缺哪一段,对应的行/列就没有条目,宽高自动收成 0,不必为"少写一段"另立规则。
4
9
 
package/css/list.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='list'] {
4
+ --xh-list-skin: 1;
5
+ }
6
+
2
7
  [data-scope='list'][data-part='root'] {
3
8
  /* 一个尺寸档只换这几个私有槽,条目与两行文字都从根上继承它们,
4
9
  换档不必给每个部件各写一条选择器 */
package/css/listbox.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='listbox'] {
4
+ --xh-listbox-skin: 1;
5
+ }
6
+
2
7
  [data-scope='listbox'][data-part='root'] {
3
8
  display: flex;
4
9
  flex-direction: column;
@@ -74,7 +79,7 @@
74
79
  [data-scope='listbox'][data-part='item-group-label'] {
75
80
  display: block;
76
81
  padding-block: var(--xh-listbox-group-label-py, var(--xh-space-1));
77
- padding-inline: var(--xh-listbox-group-label-px, var(--xh-control-px-sm));
82
+ padding-inline: var(--xh-listbox-group-label-px, var(--xh-control-px-md));
78
83
  color: var(--xh-listbox-group-label-fg, var(--xh-fg-subtle));
79
84
  font-size: var(--xh-listbox-group-label-font-size, var(--xh-font-size-xs));
80
85
  font-weight: var(--xh-listbox-group-label-font-weight, var(--xh-font-weight-medium));
@@ -94,8 +99,8 @@
94
99
  display: flex;
95
100
  align-items: center;
96
101
  gap: var(--xh-listbox-item-gap, var(--xh-control-gap-md));
97
- padding-block: var(--xh-listbox-item-py, var(--xh-space-1));
98
- padding-inline: var(--xh-listbox-item-px, var(--xh-control-px-sm));
102
+ padding-block: var(--xh-listbox-item-py, var(--xh-space-1_5));
103
+ padding-inline: var(--xh-listbox-item-px, var(--xh-control-px-md));
99
104
  border-radius: var(--xh-listbox-item-radius, var(--xh-shape-control));
100
105
  color: var(--xh-listbox-item-fg, var(--xh-fg-default));
101
106
  font-size: var(--xh-listbox-item-font-size, var(--xh-text-body-size));
@@ -104,6 +109,9 @@
104
109
  white-space: nowrap;
105
110
  cursor: pointer;
106
111
  user-select: none;
112
+ transition:
113
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
114
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
107
115
  }
108
116
 
109
117
  /* 条目可以按需收起(过滤掉的选项),有 display 就得补这条 */
@@ -111,10 +119,10 @@
111
119
  display: none;
112
120
  }
113
121
 
114
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边。
115
- data-highlighted 跟的是连接层记的焦点条目,与选中互相独立 */
122
+ /* 悬停与键盘锚点共用中性灰轻档:data-highlighted 跟的是连接层记的焦点条目,
123
+ 与选中互相独立 */
116
124
  [data-scope='listbox'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]) {
117
- background: var(--xh-listbox-item-bg-highlight, var(--xh-bg-subtle-hover));
125
+ background: var(--xh-listbox-item-bg-hover, var(--xh-bg-subtle));
118
126
  }
119
127
 
120
128
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='loading-bar'] {
4
+ --xh-loading-bar-skin: 1;
5
+ }
6
+
2
7
  /*
3
8
  * 贴在视口顶边的一条细带。厚度那条轴(block-size)由连接层写进内联样式——
4
9
  * height 这个 prop 的缺省值在 connect 里收口,这里再声明一次只会被内联盖掉,
package/css/log.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='log'] {
4
+ --xh-log-skin: 1;
5
+ }
6
+
2
7
  /* 行高是本组件的定尺基准:视口按行数定高、每行排版,都从这一个槽位来。
3
8
  它的兜底值须与连接层内联高度里用的那份逐字一致,否则 rows 算出来的高度对不上行。 */
4
9
  [data-scope='log'][data-part='root'] {
package/css/marquee.css CHANGED
@@ -25,6 +25,11 @@
25
25
  }
26
26
 
27
27
  @layer xihan.components {
28
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
29
+ [data-scope='marquee'] {
30
+ --xh-marquee-skin: 1;
31
+ }
32
+
28
33
  /*
29
34
  * 窗口:只露出轨道的一段。
30
35
  *
package/css/mention.css CHANGED
@@ -1,4 +1,23 @@
1
+ @layer xihan.motion {
2
+ /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
3
+ 动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
4
+ @keyframes xh-fade-in {
5
+ from {
6
+ opacity: 0;
7
+ }
8
+
9
+ to {
10
+ opacity: 1;
11
+ }
12
+ }
13
+ }
14
+
1
15
  @layer xihan.components {
16
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
17
+ [data-scope='mention'] {
18
+ --xh-mention-skin: 1;
19
+ }
20
+
2
21
  [data-scope='mention'][data-part='root'] {
3
22
  /* 尺寸档只换这四个私有槽,输入框与候选都从 root 继承它们,换档不必给每个部件各写一条选择器。
4
23
  浮层在 positioner 里、也是 root 的后代,一并继承 */
@@ -9,14 +28,11 @@
9
28
 
10
29
  /* 形态档只换这三个槽:缺省即 outline(描边 + 画布底) */
11
30
  --xh-_mention-input-bg: var(--xh-bg-canvas);
12
- --xh-_mention-input-border: var(--xh-border-default);
13
- --xh-_mention-input-border-hover: var(--xh-border-strong);
31
+ --xh-_mention-input-border: var(--xh-border-control);
32
+ --xh-_mention-input-border-hover: var(--xh-border-control-hover);
14
33
 
15
- /* 强调色随语气走,只落在聚焦与高亮上,正文文字色不动。
16
- 没写 data-tone 时 --xh-_tone 没被声明,三处都退回原来的取值 */
34
+ /* 聚焦环恒用固定环色,不随语气走 */
17
35
  --xh-_mention-ring: var(--xh-ring-focus);
18
- --xh-_mention-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
19
- --xh-_mention-item-highlight-fg: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
20
36
 
21
37
  display: block;
22
38
  }
@@ -47,21 +63,21 @@
47
63
  形态只决定描边与底色怎么用,用哪族颜色由 data-tone 决定 */
48
64
  [data-scope='mention'][data-part='root'][data-variant='outline'] {
49
65
  --xh-_mention-input-bg: var(--xh-bg-canvas);
50
- --xh-_mention-input-border: var(--xh-border-default);
51
- --xh-_mention-input-border-hover: var(--xh-border-strong);
66
+ --xh-_mention-input-border: var(--xh-border-control);
67
+ --xh-_mention-input-border-hover: var(--xh-border-control-hover);
52
68
  }
53
69
 
54
70
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
55
71
  [data-scope='mention'][data-part='root'][data-variant='subtle'] {
56
72
  --xh-_mention-input-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
57
73
  --xh-_mention-input-border: transparent;
58
- --xh-_mention-input-border-hover: var(--xh-_tone-border, var(--xh-border-default));
74
+ --xh-_mention-input-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
59
75
  }
60
76
 
61
77
  [data-scope='mention'][data-part='root'][data-variant='ghost'] {
62
78
  --xh-_mention-input-bg: transparent;
63
79
  --xh-_mention-input-border: transparent;
64
- --xh-_mention-input-border-hover: var(--xh-_tone-border, var(--xh-border-default));
80
+ --xh-_mention-input-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
65
81
  }
66
82
 
67
83
  /* 框与聚焦环都长在输入框自己身上:这里没有把输入框和别的控件裹成一体的输入行,
@@ -166,8 +182,8 @@
166
182
  display: flex;
167
183
  align-items: center;
168
184
  gap: var(--xh-mention-item-gap, var(--xh-_mention-gap));
169
- padding-block: var(--xh-mention-item-py, var(--xh-space-1));
170
- padding-inline: var(--xh-mention-item-px, var(--xh-control-px-sm));
185
+ padding-block: var(--xh-mention-item-py, var(--xh-space-1_5));
186
+ padding-inline: var(--xh-mention-item-px, var(--xh-control-px-md));
171
187
  border-radius: var(--xh-mention-item-radius, var(--xh-shape-control));
172
188
  color: var(--xh-mention-item-fg, var(--xh-fg-default));
173
189
  font-size: var(--xh-mention-item-font-size, var(--xh-_mention-font-size));
@@ -176,18 +192,19 @@
176
192
  white-space: nowrap;
177
193
  cursor: pointer;
178
194
  user-select: none;
195
+ transition:
196
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
197
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
179
198
  }
180
199
 
181
200
  [data-scope='mention'][data-part='item'][hidden] {
182
201
  display: none;
183
202
  }
184
203
 
185
- /* 只认 data-highlighted,不写 :hover:指针划过时连接层已经把高亮搬过来了,
204
+ /* 键盘锚点走中性灰轻档。只认 data-highlighted,不写 :hover:指针划过时连接层已经把高亮搬过来了,
186
205
  两套底色各画各的会出现"鼠标停在 A 上、回车却插进了 B" */
187
206
  [data-scope='mention'][data-part='item'][data-highlighted]:not([data-disabled]) {
188
- background: var(--xh-mention-item-bg-highlight, var(--xh-_mention-item-highlight-bg));
189
- color: var(--xh-mention-item-fg-highlight, var(--xh-_mention-item-highlight-fg));
190
- font-weight: var(--xh-mention-item-font-weight-highlight, var(--xh-font-weight-medium));
207
+ background: var(--xh-mention-item-bg-hover, var(--xh-bg-subtle));
191
208
  }
192
209
 
193
210
  /* 禁用走 aria-disabled 而非原生 disabled(原生 disabled 不派 click,禁用守卫就走不到),
@@ -210,12 +227,6 @@
210
227
  display: none;
211
228
  }
212
229
 
213
- /* 候选行里除文本以外的部分(作者摆的头像、副标题)由作者自己画;
214
- 这里只把强调色透出去,作者要用就取它 */
215
- [data-scope='mention'][data-part='item'][data-highlighted] [data-part='item-text'] {
216
- color: var(--xh-mention-item-fg-highlight, var(--xh-_mention-item-highlight-fg));
217
- }
218
-
219
230
  [data-scope='mention'][data-part='item'][data-disabled] [data-part='item-text'] {
220
231
  color: var(--xh-fg-disabled);
221
232
  }
package/css/menu.css CHANGED
@@ -1,4 +1,23 @@
1
+ @layer xihan.motion {
2
+ /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
3
+ 动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
4
+ @keyframes xh-fade-in {
5
+ from {
6
+ opacity: 0;
7
+ }
8
+
9
+ to {
10
+ opacity: 1;
11
+ }
12
+ }
13
+ }
14
+
1
15
  @layer xihan.components {
16
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
17
+ [data-scope='menu'] {
18
+ --xh-menu-skin: 1;
19
+ }
20
+
2
21
  /* 坐标由定位引擎写进 style(inset-inline-start / inset-block-start),这里只定位模式与层级 */
3
22
  [data-scope='menu'][data-part='positioner'] {
4
23
  position: fixed;
@@ -15,13 +34,14 @@
15
34
  [data-scope='menu'][data-part='content'] {
16
35
  /* 尺寸档只换这四个私有槽,条目从 content 继承它们,换档不必给每个部件各写一条选择器。
17
36
  菜单没有 root 部件(浮层树的最外层就是 content),三轴属性都落在这里 */
18
- --xh-_menu-item-py: var(--xh-space-2);
19
- --xh-_menu-item-px: var(--xh-control-px-sm);
37
+ --xh-_menu-item-py: var(--xh-space-1_5);
38
+ --xh-_menu-item-px: var(--xh-control-px-md);
20
39
  --xh-_menu-item-gap: var(--xh-control-gap-md);
21
40
  --xh-_menu-font-size: var(--xh-text-body-size);
22
41
 
23
- /* 条目高亮底随语气走。没写 data-tone 时 --xh-_tone-subtle-hover 没被声明,退回原来的取值 */
24
- --xh-_menu-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
42
+ /* 打开子菜单的触发条目走品牌淡底强档,随语气换色相;没写 data-tone 时退回品牌淡底。
43
+ 悬停与键盘焦点恒中性灰轻档、不接语气槽——两档同色相的话打开路径就认不出来了 */
44
+ --xh-_menu-item-active-bg: var(--xh-_tone-subtle, var(--xh-bg-brand-subtle));
25
45
 
26
46
  display: flex;
27
47
  flex-direction: column;
@@ -52,18 +72,18 @@
52
72
  }
53
73
 
54
74
  /* —— 尺寸 —— */
55
- /* 缺省档写在 content 的基础规则里。条目内外边距缺省档本就不在 control-h/px 那把尺上
56
- (高度由 padding 撑出来、行内内缩取的是 sm 档),故按 space 档上下各挪一级 */
75
+ /* 缺省档写在 content 的基础规则里。条目高度由 padding 撑出来:
76
+ 纵向走 space 档、行内内缩走 control-px 档(随密度轴),换档各挪一级 */
57
77
  [data-scope='menu'][data-part='content'][data-size='sm'] {
58
78
  --xh-_menu-item-py: var(--xh-space-1);
59
- --xh-_menu-item-px: var(--xh-space-1);
79
+ --xh-_menu-item-px: var(--xh-control-px-sm);
60
80
  --xh-_menu-item-gap: var(--xh-control-gap-sm);
61
81
  --xh-_menu-font-size: var(--xh-font-size-sm);
62
82
  }
63
83
 
64
84
  [data-scope='menu'][data-part='content'][data-size='lg'] {
65
- --xh-_menu-item-py: var(--xh-space-3);
66
- --xh-_menu-item-px: var(--xh-space-3);
85
+ --xh-_menu-item-py: var(--xh-space-2);
86
+ --xh-_menu-item-px: var(--xh-control-px-lg);
67
87
  --xh-_menu-item-gap: var(--xh-control-gap-lg);
68
88
  --xh-_menu-font-size: var(--xh-font-size-lg);
69
89
  }
@@ -77,15 +97,25 @@
77
97
  border-radius: var(--xh-menu-item-radius, var(--xh-shape-control));
78
98
  color: var(--xh-menu-item-fg, var(--xh-fg-default));
79
99
  font-size: var(--xh-menu-item-font-size, var(--xh-_menu-font-size));
80
- line-height: var(--xh-leading-none);
100
+ line-height: var(--xh-menu-item-leading, var(--xh-leading-normal));
81
101
  white-space: nowrap;
82
102
  cursor: pointer;
83
103
  user-select: none;
104
+ transition:
105
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
106
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
84
107
  }
85
108
 
86
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边 */
109
+ /* 悬停与键盘焦点共用中性灰轻档:条目本身承载焦点,不另画描边 */
87
110
  [data-scope='menu'][data-part='item']:not([data-disabled]):is(:hover, :focus) {
88
- background: var(--xh-menu-item-bg-highlight, var(--xh-_menu-item-highlight-bg));
111
+ background: var(--xh-menu-item-bg-hover, var(--xh-bg-subtle));
112
+ }
113
+
114
+ /* 打开子菜单的触发条目是展开路径,走品牌淡底加粗强档。
115
+ 必须排在轻档之后:悬停或焦点回到它身上时同特指度靠书写顺序压过轻档 */
116
+ [data-scope='menu'][data-part='item']:not([data-disabled])[data-state='open'] {
117
+ background: var(--xh-menu-item-bg-active, var(--xh-_menu-item-active-bg));
118
+ font-weight: var(--xh-menu-item-active-font-weight, var(--xh-font-weight-semibold));
89
119
  }
90
120
 
91
121
  [data-scope='menu'][data-part='item']:focus {
@@ -104,40 +134,44 @@
104
134
  }
105
135
 
106
136
  /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
107
- 引擎不产出箭头偏移,因此沿交叉轴固定居中 */
137
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
108
138
  [data-scope='menu'][data-part='arrow'] {
139
+ --xh-_menu-arrow-size: var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size));
140
+ --xh-_menu-arrow-x: 50%;
141
+ --xh-_menu-arrow-y: 50%;
142
+
109
143
  position: absolute;
110
- inline-size: var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size));
111
- block-size: var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size));
144
+ inline-size: var(--xh-_menu-arrow-size);
145
+ block-size: var(--xh-_menu-arrow-size);
112
146
  background: var(--xh-menu-bg, var(--xh-bg-surface));
113
147
  border: var(--xh-stroke-thin) solid var(--xh-menu-border, var(--xh-border-default));
114
148
  rotate: 45deg;
115
149
  }
116
150
 
117
151
  [data-scope='menu'][data-part='arrow'][data-placement^='bottom'] {
118
- inset-block-start: calc(var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
119
- inset-inline-start: calc(50% - var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
152
+ inset-block-start: calc(var(--xh-_menu-arrow-size) / -2);
153
+ inset-inline-start: calc(var(--xh-_menu-arrow-x) - var(--xh-_menu-arrow-size) / 2);
120
154
  border-block-end: 0;
121
155
  border-inline-end: 0;
122
156
  }
123
157
 
124
158
  [data-scope='menu'][data-part='arrow'][data-placement^='top'] {
125
- inset-block-end: calc(var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
126
- inset-inline-start: calc(50% - var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
159
+ inset-block-end: calc(var(--xh-_menu-arrow-size) / -2);
160
+ inset-inline-start: calc(var(--xh-_menu-arrow-x) - var(--xh-_menu-arrow-size) / 2);
127
161
  border-block-start: 0;
128
162
  border-inline-start: 0;
129
163
  }
130
164
 
131
165
  [data-scope='menu'][data-part='arrow'][data-placement^='right'] {
132
- inset-inline-start: calc(var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
133
- inset-block-start: calc(50% - var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
166
+ inset-inline-start: calc(var(--xh-_menu-arrow-size) / -2);
167
+ inset-block-start: calc(var(--xh-_menu-arrow-y) - var(--xh-_menu-arrow-size) / 2);
134
168
  border-block-start: 0;
135
169
  border-inline-end: 0;
136
170
  }
137
171
 
138
172
  [data-scope='menu'][data-part='arrow'][data-placement^='left'] {
139
- inset-inline-end: calc(var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
140
- inset-block-start: calc(50% - var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
173
+ inset-inline-end: calc(var(--xh-_menu-arrow-size) / -2);
174
+ inset-block-start: calc(var(--xh-_menu-arrow-y) - var(--xh-_menu-arrow-size) / 2);
141
175
  border-block-end: 0;
142
176
  border-inline-start: 0;
143
177
  }