@xihan-ui/styles 1.0.0-alpha.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 (114) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE +21 -0
  3. package/README.md +27 -0
  4. package/css/accordion.css +108 -0
  5. package/css/affix.css +27 -0
  6. package/css/alert.css +108 -0
  7. package/css/anchor.css +137 -0
  8. package/css/avatar-group.css +82 -0
  9. package/css/avatar.css +55 -0
  10. package/css/back-top.css +61 -0
  11. package/css/badge.css +50 -0
  12. package/css/breadcrumb.css +127 -0
  13. package/css/button-group.css +131 -0
  14. package/css/button.css +98 -0
  15. package/css/calendar.css +219 -0
  16. package/css/card.css +116 -0
  17. package/css/carousel.css +193 -0
  18. package/css/cascader.css +397 -0
  19. package/css/checkbox-group.css +185 -0
  20. package/css/checkbox.css +91 -0
  21. package/css/clipboard.css +133 -0
  22. package/css/code-block.css +81 -0
  23. package/css/collapsible.css +63 -0
  24. package/css/color-picker.css +424 -0
  25. package/css/combobox.css +433 -0
  26. package/css/composer.css +127 -0
  27. package/css/context-menu.css +263 -0
  28. package/css/countdown.css +21 -0
  29. package/css/date-field.css +200 -0
  30. package/css/date-picker.css +231 -0
  31. package/css/descriptions.css +200 -0
  32. package/css/dialog.css +127 -0
  33. package/css/drawer.css +372 -0
  34. package/css/dynamic-input.css +157 -0
  35. package/css/editable.css +249 -0
  36. package/css/ellipsis.css +41 -0
  37. package/css/empty-state.css +78 -0
  38. package/css/field.css +65 -0
  39. package/css/file-upload.css +298 -0
  40. package/css/flex.css +95 -0
  41. package/css/float-button.css +121 -0
  42. package/css/form.css +185 -0
  43. package/css/gradient-text.css +62 -0
  44. package/css/grid.css +428 -0
  45. package/css/highlight.css +21 -0
  46. package/css/hover-card.css +146 -0
  47. package/css/icon-wrapper.css +64 -0
  48. package/css/icon.css +54 -0
  49. package/css/image.css +45 -0
  50. package/css/infinite-scroll.css +22 -0
  51. package/css/layers.css +2 -0
  52. package/css/layout.css +169 -0
  53. package/css/list.css +117 -0
  54. package/css/listbox.css +174 -0
  55. package/css/loading-bar.css +66 -0
  56. package/css/log.css +66 -0
  57. package/css/marquee.css +182 -0
  58. package/css/mention.css +222 -0
  59. package/css/menu.css +144 -0
  60. package/css/menubar.css +271 -0
  61. package/css/motion.css +58 -0
  62. package/css/navigation-menu.css +270 -0
  63. package/css/number-animation.css +32 -0
  64. package/css/number-field.css +219 -0
  65. package/css/page-header.css +85 -0
  66. package/css/pagination.css +148 -0
  67. package/css/pin-input.css +157 -0
  68. package/css/popconfirm.css +150 -0
  69. package/css/popover.css +150 -0
  70. package/css/popselect.css +207 -0
  71. package/css/progress.css +37 -0
  72. package/css/qr-code.css +50 -0
  73. package/css/radio-group.css +101 -0
  74. package/css/rating.css +141 -0
  75. package/css/reset.css +22 -0
  76. package/css/result.css +104 -0
  77. package/css/scroll-area.css +176 -0
  78. package/css/select.css +264 -0
  79. package/css/separator.css +17 -0
  80. package/css/skeleton.css +78 -0
  81. package/css/slider.css +197 -0
  82. package/css/spinner.css +74 -0
  83. package/css/splitter.css +127 -0
  84. package/css/statistic.css +67 -0
  85. package/css/steps.css +252 -0
  86. package/css/switch.css +61 -0
  87. package/css/table.css +404 -0
  88. package/css/tabs.css +157 -0
  89. package/css/tags-input.css +360 -0
  90. package/css/text-field.css +195 -0
  91. package/css/thread.css +107 -0
  92. package/css/time-field.css +200 -0
  93. package/css/time-picker.css +347 -0
  94. package/css/time.css +20 -0
  95. package/css/timeline.css +284 -0
  96. package/css/toast.css +195 -0
  97. package/css/toaster.css +69 -0
  98. package/css/toggle-group.css +160 -0
  99. package/css/toggle.css +103 -0
  100. package/css/tone.css +85 -0
  101. package/css/toolbar.css +114 -0
  102. package/css/tooltip.css +109 -0
  103. package/css/tour.css +305 -0
  104. package/css/transfer.css +318 -0
  105. package/css/tree-select.css +441 -0
  106. package/css/tree.css +204 -0
  107. package/css/typography.css +151 -0
  108. package/css/undefined.css +38 -0
  109. package/css/virtualizer.css +104 -0
  110. package/css/visually-hidden.css +13 -0
  111. package/css/watermark.css +48 -0
  112. package/index.css +113 -0
  113. package/index.unlayered.css +16164 -0
  114. package/package.json +163 -0
@@ -0,0 +1,174 @@
1
+ @layer xihan.components {
2
+ [data-scope='listbox'][data-part='root'] {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: var(--xh-listbox-gap, var(--xh-space-2));
6
+ }
7
+
8
+ [data-scope='listbox'][data-part='root'][hidden] {
9
+ display: none;
10
+ }
11
+
12
+ [data-scope='listbox'][data-part='label'] {
13
+ color: var(--xh-listbox-label-fg, var(--xh-fg-muted));
14
+ font-size: var(--xh-listbox-label-font-size, var(--xh-text-label-size));
15
+ font-weight: var(--xh-listbox-label-font-weight, var(--xh-text-label-weight));
16
+ line-height: var(--xh-leading-none);
17
+ }
18
+
19
+ /* 整组禁用会把 data-disabled 一路打到 label / content / item-group 上,各处观感保持一致 */
20
+ [data-scope='listbox'][data-part='label'][data-disabled] {
21
+ color: var(--xh-fg-subtle);
22
+ }
23
+
24
+ /* 常驻列表不是浮层:容器留边框与底色,但不投影、不脱离文档流,
25
+ 因此这里既不写 position 也不写 z-index,滚动发生在容器自身 */
26
+ [data-scope='listbox'][data-part='content'] {
27
+ display: flex;
28
+ flex-direction: column;
29
+ max-block-size: var(--xh-listbox-content-max-h, var(--xh-overlay-max-h));
30
+ padding-block: var(--xh-listbox-content-py, var(--xh-space-1));
31
+ padding-inline: var(--xh-listbox-content-px, var(--xh-space-1));
32
+ border: var(--xh-stroke-thin) solid var(--xh-listbox-content-border, var(--xh-border-default));
33
+ border-radius: var(--xh-listbox-content-radius, var(--xh-shape-surface));
34
+ background: var(--xh-listbox-content-bg, var(--xh-bg-surface));
35
+ color: var(--xh-listbox-content-fg, var(--xh-fg-default));
36
+ /* 两轴一起给:条目文字用省略号收口,正常不会横向溢出,
37
+ 但作者塞进宽内容时至少还够得着,而不是被裁掉 */
38
+ overflow: auto;
39
+ }
40
+
41
+ /* display 会盖掉 hidden 属性的 UA 规则,作者手动收起时要显式让位 */
42
+ [data-scope='listbox'][data-part='content'][hidden] {
43
+ display: none;
44
+ }
45
+
46
+ /* 横向列表沿主轴排,此时高度由内容决定,再压最大高度只会白留一条滚动条 */
47
+ [data-scope='listbox'][data-part='content'][data-orientation='horizontal'] {
48
+ flex-direction: row;
49
+ align-items: center;
50
+ max-block-size: none;
51
+ }
52
+
53
+ /* 焦点在列表外时容器自己进 Tab 序列兜底,落焦不画环:高亮永远长在条目上 */
54
+ [data-scope='listbox'][data-part='content']:focus-visible {
55
+ outline: none;
56
+ }
57
+
58
+ [data-scope='listbox'][data-part='item-group'] {
59
+ display: flex;
60
+ flex-direction: column;
61
+ }
62
+
63
+ [data-scope='listbox'][data-part='item-group'][hidden] {
64
+ display: none;
65
+ }
66
+
67
+ /* 组间留白只加在后继分组上:首组顶上已经有 content 的内边距,再加一层会显得头重 */
68
+ [data-scope='listbox'][data-part='item-group'] + [data-scope='listbox'][data-part='item-group'] {
69
+ margin-block-start: var(--xh-listbox-group-gap, var(--xh-space-2));
70
+ }
71
+
72
+ /* 分组标题不是选项、进不了导航,画成小号次要文字,
73
+ 内缩与条目对齐(同一个 padding-inline),标题与条目文字才在一条竖线上 */
74
+ [data-scope='listbox'][data-part='item-group-label'] {
75
+ display: block;
76
+ 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));
78
+ color: var(--xh-listbox-group-label-fg, var(--xh-fg-subtle));
79
+ font-size: var(--xh-listbox-group-label-font-size, var(--xh-font-size-xs));
80
+ font-weight: var(--xh-listbox-group-label-font-weight, var(--xh-font-weight-medium));
81
+ line-height: var(--xh-leading-none);
82
+ user-select: none;
83
+ }
84
+
85
+ [data-scope='listbox'][data-part='item-group-label'][hidden] {
86
+ display: none;
87
+ }
88
+
89
+ [data-scope='listbox'][data-part='item-group-label'][data-disabled] {
90
+ color: var(--xh-fg-disabled);
91
+ }
92
+
93
+ [data-scope='listbox'][data-part='item'] {
94
+ display: flex;
95
+ align-items: center;
96
+ 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));
99
+ border-radius: var(--xh-listbox-item-radius, var(--xh-shape-control));
100
+ color: var(--xh-listbox-item-fg, var(--xh-fg-default));
101
+ font-size: var(--xh-listbox-item-font-size, var(--xh-text-body-size));
102
+ /* 行高留出文字的上下伸展空间,行内那层 overflow:hidden 才不会把字削掉 */
103
+ line-height: var(--xh-listbox-item-leading, var(--xh-leading-normal));
104
+ white-space: nowrap;
105
+ cursor: pointer;
106
+ user-select: none;
107
+ }
108
+
109
+ /* 条目可以按需收起(过滤掉的选项),有 display 就得补这条 */
110
+ [data-scope='listbox'][data-part='item'][hidden] {
111
+ display: none;
112
+ }
113
+
114
+ /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边。
115
+ data-highlighted 跟的是连接层记的焦点条目,与选中互相独立 */
116
+ [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));
118
+ }
119
+
120
+ /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,
121
+ 两者因此能同时看见 */
122
+ [data-scope='listbox'][data-part='item'][data-state='checked'] {
123
+ color: var(--xh-listbox-item-fg-selected, var(--xh-fg-brand-strong));
124
+ font-weight: var(--xh-listbox-item-font-weight-selected, var(--xh-font-weight-medium));
125
+ }
126
+
127
+ [data-scope='listbox'][data-part='item']:focus {
128
+ outline: none;
129
+ }
130
+
131
+ /* 禁用走 aria-disabled 而非原生 disabled:条目仍要可聚焦(它还得当方向键的起点),
132
+ 所以只能靠 data-disabled 表达,写 :disabled 永远不命中 */
133
+ [data-scope='listbox'][data-part='item'][data-disabled] {
134
+ color: var(--xh-fg-disabled);
135
+ cursor: not-allowed;
136
+ }
137
+
138
+ [data-scope='listbox'][data-part='item-text'] {
139
+ flex: 1;
140
+ /* 弹性子项默认最小尺寸是内容尺寸,不归零则长文本把行撑破、省略号轮不上 */
141
+ min-inline-size: 0;
142
+ overflow: hidden;
143
+ white-space: nowrap;
144
+ text-overflow: ellipsis;
145
+ }
146
+
147
+ /* 勾选标记只在选中项上出现;未选中时占位不塌,切换选中不引起行宽抖动 */
148
+ [data-scope='listbox'][data-part='item-indicator'] {
149
+ display: inline-flex;
150
+ flex: none;
151
+ align-items: center;
152
+ justify-content: center;
153
+ inline-size: var(--xh-listbox-item-indicator-size, var(--xh-control-indicator-size));
154
+ color: var(--xh-listbox-item-indicator-fg, var(--xh-bg-brand));
155
+ visibility: hidden;
156
+ }
157
+
158
+ [data-scope='listbox'][data-part='item-indicator'][hidden] {
159
+ display: none;
160
+ }
161
+
162
+ /* 没写内容就由皮肤画勾;作者往部件里塞了自己的图形,这条规则即不命中 */
163
+ [data-scope='listbox'][data-part='item-indicator']:empty::before {
164
+ content: '✓';
165
+ }
166
+
167
+ [data-scope='listbox'][data-part='item-indicator'][data-state='checked'] {
168
+ visibility: visible;
169
+ }
170
+
171
+ [data-scope='listbox'][data-part='item-indicator'][data-disabled] {
172
+ color: var(--xh-fg-disabled);
173
+ }
174
+ }
@@ -0,0 +1,66 @@
1
+ @layer xihan.components {
2
+ /*
3
+ * 贴在视口顶边的一条细带。厚度那条轴(block-size)由连接层写进内联样式——
4
+ * height 这个 prop 的缺省值在 connect 里收口,这里再声明一次只会被内联盖掉,
5
+ * 还会让人误以为厚度归样式层管。
6
+ *
7
+ * fixed 认的是视口,祖先上一旦出现 transform / filter / backdrop-filter / contain
8
+ * 就会把包含块抢走,条子立刻贴错位置——覆盖钩子里别加这几个属性。
9
+ */
10
+ [data-scope='loading-bar'][data-part='root'] {
11
+ position: fixed;
12
+ inset-block-start: 0;
13
+ inset-inline: 0;
14
+ z-index: var(--xh-loading-bar-layer, var(--xh-layer-sticky));
15
+
16
+ /* 它只报进度,不吃点击:盖在顶栏上的一条带子若拦住指针,页面顶部就点不动了 */
17
+ pointer-events: none;
18
+ opacity: 1;
19
+ transition: opacity var(--xh-loading-bar-fade, var(--xh-motion-duration-exit)) var(--xh-motion-ease-exit);
20
+ }
21
+
22
+ /* 只要声明过任何 display,就盖掉了 UA 的 [hidden]{display:none};
23
+ 这里虽然没声明,作者层随手给一条 display 也会盖掉,所以补齐这条兜住收起态。
24
+ 连接层只会给 root 写 hidden,track / range 不需要同样的兜底 */
25
+ [data-scope='loading-bar'][data-part='root'][hidden] {
26
+ display: none;
27
+ }
28
+
29
+ /* 冲到 100 之后进入淡出:机器会在淡出窗口走完之前一直停在 finishing,
30
+ 这段过渡才有时间跑完;窗口与时长两处默认值都是 200ms,改一处记得对齐另一处 */
31
+ [data-scope='loading-bar'][data-part='root'][data-state='finishing'] {
32
+ opacity: 0;
33
+ }
34
+
35
+ /* 背景槽。默认透明:顶部加载条通常直接压在页面顶边上,一条贯穿整屏的浅色底
36
+ 在视觉上等于凭空多出一道分隔线 */
37
+ [data-scope='loading-bar'][data-part='track'] {
38
+ overflow: hidden;
39
+ inline-size: 100%;
40
+ block-size: 100%;
41
+ background: var(--xh-loading-bar-track, transparent);
42
+ }
43
+
44
+ /*
45
+ * 进度段。宽度那条轴(inline-size)由连接层写进内联样式,这里绝不重复写。
46
+ * 背景色同理:给了 color 就走内联,盖过下面这条。
47
+ *
48
+ * 语气取柔和档 --xh-_tone-soft(500)而不是 --xh-_tone(600):600 是给实心底与
49
+ * 文字用的强调档,压在这么一条细带上偏重。槽位由 root 上的 data-tone 声明,继承下来。
50
+ * 没写 data-tone 时该槽没被声明,退回原来的品牌色。
51
+ */
52
+ [data-scope='loading-bar'][data-part='range'] {
53
+ block-size: 100%;
54
+ background: var(--xh-loading-bar-range, var(--xh-_tone-soft, var(--xh-bg-brand)));
55
+ transition: inline-size var(--xh-loading-bar-speed, var(--xh-motion-duration-enter)) var(--xh-motion-ease-enter);
56
+ }
57
+
58
+ /* 减少动效:宽度与淡出都立刻到位。进度本身照常推进,只是不再有过渡的过程——
59
+ 一条按 200ms 一拍持续变宽的带子正是最容易引发不适的那种动效 */
60
+ @media (prefers-reduced-motion: reduce) {
61
+ [data-scope='loading-bar'][data-part='root'],
62
+ [data-scope='loading-bar'][data-part='range'] {
63
+ transition: none;
64
+ }
65
+ }
66
+ }
package/css/log.css ADDED
@@ -0,0 +1,66 @@
1
+ @layer xihan.components {
2
+ /* 行高是本组件的定尺基准:视口按行数定高、每行排版,都从这一个槽位来。
3
+ 它的兜底值须与连接层内联高度里用的那份逐字一致,否则 rows 算出来的高度对不上行。 */
4
+ [data-scope='log'][data-part='root'] {
5
+ --xh-_log-line-h: var(--xh-log-line-height, 1.25rem);
6
+
7
+ position: relative;
8
+ display: flex;
9
+ flex-direction: column;
10
+ overflow: hidden;
11
+ border: var(--xh-stroke-thin) solid var(--xh-log-border, var(--xh-border-default));
12
+ border-radius: var(--xh-log-radius, var(--xh-shape-surface));
13
+ background: var(--xh-log-bg, var(--xh-bg-subtle));
14
+ color: var(--xh-log-fg, var(--xh-fg-default));
15
+ }
16
+
17
+ /* 上面声明的 display 会盖掉 UA 的 [hidden]{display:none},这里补回 */
18
+ [data-scope='log'][data-part='root'][hidden] {
19
+ display: none;
20
+ }
21
+
22
+ /* 滚动容器,粘底读的是它的 scrollTop / clientHeight / scrollHeight。
23
+ flex-basis 取 auto,连接层写的按行数高度才算数,同时外层给了高度时还能撑满;
24
+ min-block-size: 0 让它在弹性父级里能被压缩从而真的溢出;
25
+ 两个轴都 auto:长行不折,横向也要能滚;
26
+ overscroll-behavior: contain 阻止滚动量传给页面;
27
+ scrollbar-gutter: stable 避免滚动条出现与消失时推动文字。 */
28
+ [data-scope='log'][data-part='viewport'] {
29
+ flex: 1 1 auto;
30
+ min-block-size: 0;
31
+ block-size: calc(var(--xh-_log-line-h) * var(--xh-log-rows, 16));
32
+ overflow: auto;
33
+ overscroll-behavior: contain;
34
+ scrollbar-gutter: stable;
35
+ }
36
+
37
+ /* 视口带 tabindex=0,需要焦点环;偏移取负画在内侧,避免被 root 裁掉 */
38
+ [data-scope='log'][data-part='viewport']:focus-visible {
39
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
40
+ outline-offset: calc(-1 * var(--xh-ring-width));
41
+ }
42
+
43
+ /* 取行期间指针给出忙的反馈 */
44
+ [data-scope='log'][data-part='root'][data-loading] [data-scope='log'][data-part='viewport'] {
45
+ cursor: progress;
46
+ }
47
+
48
+ /* 等宽排版落在包裹层上,各行继承;行与行之间不留空隙,视口高度才等于整数行 */
49
+ [data-scope='log'][data-part='content'] {
50
+ display: block;
51
+ padding-inline: var(--xh-log-content-px, var(--xh-space-3));
52
+ font-family: var(--xh-log-font, var(--xh-font-family-mono));
53
+ font-size: var(--xh-log-font-size, var(--xh-font-size-sm));
54
+ }
55
+
56
+ [data-scope='log'][data-part='content'][hidden] {
57
+ display: none;
58
+ }
59
+
60
+ /* 一行不折行,超出的部分由视口横向滚动;行高与定高基准同源 */
61
+ [data-scope='log'][data-part='line'] {
62
+ line-height: var(--xh-_log-line-h);
63
+ white-space: pre;
64
+ tab-size: var(--xh-log-tab-size, 4);
65
+ }
66
+ }
@@ -0,0 +1,182 @@
1
+ @layer xihan.motion {
2
+ /*
3
+ * 横轴一条、纵轴一条。四个方向乘两种铺法共八组起止点,若不外提就要写八份几乎一样的关键帧。
4
+ * 关键帧里的自定义属性取元素上的计算值,一圈跑下来它不变,所以两条就够。
5
+ */
6
+ @keyframes xh-marquee-x {
7
+ from {
8
+ transform: translateX(var(--xh-_marquee-from));
9
+ }
10
+
11
+ to {
12
+ transform: translateX(var(--xh-_marquee-to));
13
+ }
14
+ }
15
+
16
+ @keyframes xh-marquee-y {
17
+ from {
18
+ transform: translateY(var(--xh-_marquee-from));
19
+ }
20
+
21
+ to {
22
+ transform: translateY(var(--xh-_marquee-to));
23
+ }
24
+ }
25
+ }
26
+
27
+ @layer xihan.components {
28
+ /*
29
+ * 窗口:只露出轨道的一段。
30
+ *
31
+ * 方向、轴与铺法全部换成根上的私有槽,轨道与每一份内容各自只有一条规则去取值。
32
+ * 这样写不只是为了短:轨道那条选择器因此能写成 [data-scope][data-part] 的复合式,
33
+ * 而不是从根往下够的后代式——marquee 里嵌一个同样有 content 部件的组件时,
34
+ * 后代式会一路匹配到别人家的节点上去。
35
+ *
36
+ * 一圈的时长 = 一份内容的长度 ÷ 每秒像素数。--xh-_marquee-span 是一份内容在滚动轴上的长度,
37
+ * 单位像素、写成纯数字。CSS 读不到布局尺寸,这里给的是缺省值;内容明显长于或短于它时,
38
+ * 由使用者改写 --xh-marquee-span 对齐真实长度,速度才逐字等于每秒那么多像素。
39
+ */
40
+ [data-scope='marquee'][data-part='root'] {
41
+ --xh-_marquee-span: var(--xh-marquee-span, 600);
42
+ --xh-_marquee-speed: var(--xh-marquee-speed, 60);
43
+
44
+ /* 铺一份时一圈走两倍自身长度:从窗口一侧整段进来,再整段出到另一侧 */
45
+ --xh-_marquee-duration: calc(2 * var(--xh-_marquee-span) / var(--xh-_marquee-speed) * 1s);
46
+ --xh-_marquee-play: running;
47
+ --xh-_marquee-flow: row;
48
+ --xh-_marquee-name: xh-marquee-x;
49
+ --xh-_marquee-track-inline: max-content;
50
+ --xh-_marquee-track-block: auto;
51
+
52
+ /* 轨道自身至少铺满窗口,起点那一跳才真的落在窗口外面 */
53
+ --xh-_marquee-fill-inline: 100%;
54
+ --xh-_marquee-fill-block: auto;
55
+ --xh-_marquee-copy-inline: auto;
56
+ --xh-_marquee-copy-block: auto;
57
+ --xh-_marquee-gap-inline: var(--xh-marquee-gap, var(--xh-space-6));
58
+ --xh-_marquee-gap-block: 0;
59
+ --xh-_marquee-from: 100%;
60
+ --xh-_marquee-to: -100%;
61
+
62
+ display: flex;
63
+ overflow: hidden;
64
+ max-inline-size: 100%;
65
+ }
66
+
67
+ /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},补这条兜住收起态 */
68
+ [data-scope='marquee'][data-part='root'][hidden] {
69
+ display: none;
70
+ }
71
+
72
+ /* 竖着滚要有一个定高的窗口,否则内容自己把窗口撑到一样高,一格都走不动 */
73
+ [data-scope='marquee'][data-part='root'][data-orientation='vertical'] {
74
+ --xh-_marquee-flow: column;
75
+ --xh-_marquee-name: xh-marquee-y;
76
+ --xh-_marquee-track-inline: auto;
77
+ --xh-_marquee-track-block: max-content;
78
+ --xh-_marquee-fill-inline: auto;
79
+ --xh-_marquee-fill-block: 100%;
80
+ --xh-_marquee-gap-inline: 0;
81
+ --xh-_marquee-gap-block: var(--xh-marquee-gap, var(--xh-space-6));
82
+
83
+ block-size: var(--xh-marquee-block-size, 10rem);
84
+ }
85
+
86
+ /* 往右、往下是同一条轨迹倒过来走 */
87
+ [data-scope='marquee'][data-part='root'][data-direction='right'],
88
+ [data-scope='marquee'][data-part='root'][data-direction='down'] {
89
+ --xh-_marquee-from: -100%;
90
+ --xh-_marquee-to: 100%;
91
+ }
92
+
93
+ /*
94
+ * 铺两份:一圈只走一份的长度,走完那一刻第二份正好压在起点上,看不出接缝。
95
+ * 这条排在竖轴那条之后:两者同权,铺两份时轨道自己不必铺满窗口,改由每一份各自铺满。
96
+ */
97
+ [data-scope='marquee'][data-part='root'][data-auto-fill] {
98
+ --xh-_marquee-duration: calc(var(--xh-_marquee-span) / var(--xh-_marquee-speed) * 1s);
99
+ --xh-_marquee-fill-inline: auto;
100
+ --xh-_marquee-fill-block: auto;
101
+ }
102
+
103
+ /* 内容短于窗口时每份自己先铺满一整窗,两份接起来才不会在接缝处露出空白 */
104
+ [data-scope='marquee'][data-part='root'][data-auto-fill][data-orientation='horizontal'] {
105
+ --xh-_marquee-copy-inline: 100%;
106
+ }
107
+
108
+ [data-scope='marquee'][data-part='root'][data-auto-fill][data-orientation='vertical'] {
109
+ --xh-_marquee-copy-block: 100%;
110
+ }
111
+
112
+ /* 起止点逐档写全:铺法与方向同权,交叉的四种情形各写一条才不看书写顺序 */
113
+ [data-scope='marquee'][data-part='root'][data-auto-fill][data-direction='left'],
114
+ [data-scope='marquee'][data-part='root'][data-auto-fill][data-direction='up'] {
115
+ --xh-_marquee-from: 0%;
116
+ --xh-_marquee-to: -50%;
117
+ }
118
+
119
+ [data-scope='marquee'][data-part='root'][data-auto-fill][data-direction='right'],
120
+ [data-scope='marquee'][data-part='root'][data-auto-fill][data-direction='down'] {
121
+ --xh-_marquee-from: -50%;
122
+ --xh-_marquee-to: 0%;
123
+ }
124
+
125
+ /* 悬停暂停:只在能用指针的设备上生效,触摸端不留一个按不掉的悬停态 */
126
+ @media (hover: hover) {
127
+ [data-scope='marquee'][data-part='root'][data-pause-on-hover]:hover {
128
+ --xh-_marquee-play: paused;
129
+ }
130
+ }
131
+
132
+ /* 键盘用户没有悬停:焦点落进窗口时同样停住,否则轨道里的链接刚聚焦就被带走了 */
133
+ [data-scope='marquee'][data-part='root'][data-pause-on-hover]:focus-within {
134
+ --xh-_marquee-play: paused;
135
+ }
136
+
137
+ /* 轨道:走的是这一层,一圈走完立刻回到起点接着走 */
138
+ [data-scope='marquee'][data-part='content'] {
139
+ display: flex;
140
+ flex: none;
141
+ flex-direction: var(--xh-_marquee-flow);
142
+ inline-size: var(--xh-_marquee-track-inline);
143
+ min-inline-size: var(--xh-_marquee-fill-inline);
144
+ block-size: var(--xh-_marquee-track-block);
145
+ min-block-size: var(--xh-_marquee-fill-block);
146
+ animation-name: var(--xh-_marquee-name);
147
+ animation-duration: var(--xh-_marquee-duration);
148
+ animation-timing-function: linear;
149
+ animation-iteration-count: infinite;
150
+ animation-play-state: var(--xh-_marquee-play);
151
+ }
152
+
153
+ /*
154
+ * 一份内容。份与份之间的空白做成每份自己的收尾内边距,而不是轨道上的 gap:
155
+ * gap 只加在中间那一道,两份就不等长了,而接缝对不对得上看的正是"走完的距离等于一份的长度"。
156
+ */
157
+ [data-scope='marquee'][data-part='content'] > [data-xh-copy] {
158
+ display: flex;
159
+ flex: none;
160
+ flex-direction: var(--xh-_marquee-flow);
161
+ align-items: center;
162
+ min-inline-size: var(--xh-_marquee-copy-inline);
163
+ min-block-size: var(--xh-_marquee-copy-block);
164
+ padding-inline-end: var(--xh-_marquee-gap-inline);
165
+ padding-block-end: var(--xh-_marquee-gap-block);
166
+ }
167
+
168
+ /*
169
+ * 减少动效:轨道停在起点不再走。一段自己动起来、不停到底的横移正是最容易引发不适的那一类,
170
+ * 这里不能只把速度压到很慢——慢速横移仍然是横移。
171
+ * 停下后窗口外那段就够不着了,故窗口同时改成可滚,内容仍然读得全。
172
+ */
173
+ @media (prefers-reduced-motion: reduce) {
174
+ [data-scope='marquee'][data-part='content'] {
175
+ animation: none;
176
+ }
177
+
178
+ [data-scope='marquee'][data-part='root'] {
179
+ overflow: auto;
180
+ }
181
+ }
182
+ }