@xihan-ui/styles 1.0.0-alpha.2 → 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 (122) hide show
  1. package/CHANGELOG.md +2177 -0
  2. package/README.md +1 -1
  3. package/css/accordion.css +80 -6
  4. package/css/alert.css +43 -14
  5. package/css/anchor.css +8 -8
  6. package/css/avatar-group.css +7 -3
  7. package/css/avatar.css +9 -3
  8. package/css/back-top.css +22 -1
  9. package/css/badge.css +104 -32
  10. package/css/breadcrumb.css +25 -5
  11. package/css/button-group.css +18 -13
  12. package/css/button.css +48 -11
  13. package/css/calendar.css +150 -7
  14. package/css/card.css +20 -11
  15. package/css/carousel.css +36 -5
  16. package/css/cascader.css +211 -124
  17. package/css/checkbox-group.css +72 -22
  18. package/css/checkbox.css +70 -13
  19. package/css/clipboard.css +2 -1
  20. package/css/code-block.css +12 -7
  21. package/css/collapsible.css +49 -3
  22. package/css/color-picker.css +132 -46
  23. package/css/combobox.css +132 -60
  24. package/css/composer.css +5 -3
  25. package/css/context-menu.css +113 -74
  26. package/css/date-field.css +93 -16
  27. package/css/date-picker.css +293 -72
  28. package/css/descriptions.css +11 -5
  29. package/css/dialog.css +47 -14
  30. package/css/download-trigger.css +76 -0
  31. package/css/drawer.css +57 -16
  32. package/css/dynamic-input.css +75 -17
  33. package/css/editable.css +12 -10
  34. package/css/ellipsis.css +7 -1
  35. package/css/empty-state.css +22 -12
  36. package/css/field.css +16 -6
  37. package/css/fieldset.css +69 -0
  38. package/css/file-upload.css +46 -8
  39. package/css/flex.css +13 -7
  40. package/css/float-button.css +23 -1
  41. package/css/floating-panel.css +316 -0
  42. package/css/form.css +4 -4
  43. package/css/grid.css +11 -5
  44. package/css/heatmap.css +411 -0
  45. package/css/highlight.css +2 -2
  46. package/css/hotkeys.css +90 -0
  47. package/css/hover-card.css +43 -45
  48. package/css/image-cropper.css +233 -0
  49. package/css/image-viewer.css +155 -17
  50. package/css/image.css +2 -2
  51. package/css/infinite-scroll.css +1 -1
  52. package/css/json-viewer.css +274 -0
  53. package/css/layout.css +19 -15
  54. package/css/list.css +22 -13
  55. package/css/listbox.css +24 -6
  56. package/css/loading-bar.css +12 -5
  57. package/css/log.css +7 -3
  58. package/css/marquee.css +9 -0
  59. package/css/masonry.css +63 -0
  60. package/css/mention.css +100 -36
  61. package/css/menu.css +127 -50
  62. package/css/menubar.css +155 -61
  63. package/css/navigation-menu.css +56 -31
  64. package/css/notification.css +377 -0
  65. package/css/number-animation.css +3 -3
  66. package/css/number-field.css +182 -19
  67. package/css/overlay-arrow.css +51 -0
  68. package/css/page-header.css +12 -5
  69. package/css/pagination.css +171 -14
  70. package/css/password-input.css +356 -0
  71. package/css/pin-input.css +15 -12
  72. package/css/popconfirm.css +55 -12
  73. package/css/popover.css +73 -45
  74. package/css/popselect.css +207 -57
  75. package/css/progress.css +54 -8
  76. package/css/qr-code.css +1 -1
  77. package/css/radio-group.css +27 -10
  78. package/css/rating.css +4 -4
  79. package/css/reset.css +17 -0
  80. package/css/result.css +21 -11
  81. package/css/scroll-area.css +61 -89
  82. package/css/scrollbar.css +170 -0
  83. package/css/segmented.css +215 -0
  84. package/css/select.css +298 -131
  85. package/css/separator.css +2 -2
  86. package/css/side-nav.css +127 -24
  87. package/css/signature-pad.css +144 -0
  88. package/css/skeleton.css +7 -1
  89. package/css/slider.css +14 -12
  90. package/css/space.css +117 -0
  91. package/css/spinner.css +22 -10
  92. package/css/statistic.css +9 -9
  93. package/css/steps.css +29 -12
  94. package/css/switch.css +57 -8
  95. package/css/table.css +146 -44
  96. package/css/tabs.css +13 -5
  97. package/css/tag.css +179 -0
  98. package/css/tags-input.css +80 -42
  99. package/css/text-field.css +151 -38
  100. package/css/thread.css +11 -3
  101. package/css/time-field.css +99 -18
  102. package/css/time-picker.css +261 -71
  103. package/css/timeline.css +15 -15
  104. package/css/timer.css +119 -0
  105. package/css/toast.css +129 -55
  106. package/css/toggle-group.css +30 -4
  107. package/css/toggle.css +12 -6
  108. package/css/tone.css +57 -22
  109. package/css/toolbar.css +1 -1
  110. package/css/tooltip.css +40 -31
  111. package/css/tour.css +112 -61
  112. package/css/transfer.css +98 -21
  113. package/css/tree-select.css +215 -89
  114. package/css/tree.css +131 -5
  115. package/css/typography.css +5 -5
  116. package/css/undefined.css +12 -1
  117. package/css/virtualizer.css +3 -1
  118. package/css/watermark.css +1 -2
  119. package/index.css +18 -1
  120. package/index.unlayered.css +10356 -3255
  121. package/package.json +20 -4
  122. package/css/toaster.css +0 -74
@@ -0,0 +1,411 @@
1
+ @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='heatmap'] {
4
+ --xh-heatmap-skin: 1;
5
+ }
6
+
7
+ /* 缺省的 calendar 形态:上下两条横排,月份行在网格之外(它只是一条对齐用的坐标轴),
8
+ 网格里是七行星期。格子一律是固定尺寸的方块,列与列之间靠尺寸对齐,不必另立网格轨道。
9
+ 另外两种形态只在下面的 [data-variant] 段里改摆法,这里的取值一概不动。 */
10
+ [data-scope='heatmap'][data-part='root'] {
11
+ /* 尺寸档只换这里的私有槽,各部件从 root 继承它们,换档不必给每个部件各写一条选择器。
12
+ 格宽在这一处就把使用者令牌解析掉:错列与月份名的宽度都按它算,
13
+ 各处分头读会让作者改宽格子后错列与月份名对不上 */
14
+ --xh-_heatmap-cell-size: var(--xh-heatmap-cell-size, var(--xh-space-2_5));
15
+ /* 矩阵形态的行高。格宽那把尺是给「一天一个小方块」定的,矩阵一行左边还挂着行名,
16
+ 格子照那把尺定高就矮过行名,行高由行名的字定、格子在行里摊成一条。
17
+ 这里取格宽尺的两倍:三档一起走,且每一档都高过行名那行字。
18
+ 不接控件高度那把尺——它是给可点可输的控件在一行里排齐用的,矩阵格子不可交互,
19
+ 接上去密度档一换矩阵就跟着变形 */
20
+ --xh-_heatmap-row-h: var(--xh-heatmap-row-h, var(--xh-space-5));
21
+ /* 格间距等于聚焦环占的宽度(偏移 + 环宽):环因此整圈落在格子之间的空当里,
22
+ 不压到相邻的格子上(外缘与相邻格相接) */
23
+ --xh-_heatmap-gap: calc(var(--xh-ring-offset) + var(--xh-ring-width));
24
+ --xh-_heatmap-gutter: var(--xh-heatmap-gutter, var(--xh-space-8));
25
+ --xh-_heatmap-font-size: var(--xh-text-caption-size);
26
+ /* 星期名隔行画:第 0/2/4/6 行那个字的颜色。缺省透明——只是字不上色,
27
+ 盒子、底色与命中区都还在,钉住那一列的实色底不缺一块,七行的悬停行为也一致。
28
+ 改写这个槽为一个看得见的颜色(`var(--xh-heatmap-label-fg, var(--xh-fg-subtle))`)即七行全画。
29
+ 周首日是星期一时留下的是二 / 四 / 六,是星期日时留下的是一 / 三 / 五 */
30
+ --xh-_heatmap-week-day-skip: var(--xh-heatmap-week-day-skip, transparent);
31
+ /* 色阶的两端:0 档的空格底与满档的实心底。没写 data-tone 也没写 data-palette 时就是这里的取值 */
32
+ --xh-_heatmap-empty: var(--xh-heatmap-empty, var(--xh-bg-subtle));
33
+ --xh-_heatmap-ink: var(--xh-heatmap-ink, var(--xh-bg-brand));
34
+ /* 钉住的行首那一列自带的底色:格子从它下面滚过去时不能透出来。
35
+ 整块摆在别的底色上(卡片、分区)时改写 --xh-heatmap-bg,否则那一列会是一块补丁 */
36
+ --xh-_heatmap-surface: var(--xh-heatmap-bg, var(--xh-bg-surface));
37
+
38
+ /* 详情条按内边距盒绝对定位,参照系就是它 */
39
+ position: relative;
40
+ /* 自建层叠上下文:钉住那一列与详情条的层号只跟组件里的兄弟比,
41
+ 不写这条它们是拿去跟页面同层的元素比,热力图会压住页面上后来的定位元素 */
42
+ isolation: isolate;
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: var(--xh-heatmap-gap, var(--xh-space-2));
46
+ /* 一整年五十几列,一屏放不下就自己横着滚。横向滚动会把纵向也变成 auto,
47
+ 上下留出一圈聚焦环的余量,最上一行与最下一行的环才不会被裁掉 */
48
+ padding-block: var(--xh-heatmap-py, calc(var(--xh-ring-offset) + var(--xh-ring-width)));
49
+ overflow-x: auto;
50
+ /* 键盘走到视口外的格子时 scrollIntoView 会把它滚进来,落点要让开钉住的那一列,
51
+ 否则往回走的那一格正好停在行首星期名底下 */
52
+ scroll-padding-inline-start: calc(var(--xh-_heatmap-gutter) + var(--xh-_heatmap-gap));
53
+ color: var(--xh-heatmap-fg, var(--xh-fg-muted));
54
+ font-size: var(--xh-heatmap-font-size, var(--xh-_heatmap-font-size));
55
+ line-height: var(--xh-leading-none);
56
+ }
57
+
58
+ /* —— 语气 —— */
59
+ /* 选择器带 [data-tone] 限定:不限定的话,放在语气容器里的热力图会跟着改色 */
60
+ [data-scope='heatmap'][data-part='root'][data-tone] {
61
+ --xh-_heatmap-ink: var(--xh-heatmap-ink, var(--xh-_tone, var(--xh-bg-brand)));
62
+ }
63
+
64
+ /* —— 色板 —— */
65
+ /* 直接按颜色点名,定的是满档那一端的实心底;0 档的空格底与中间各档的兑法一概不动。
66
+ 六条规则与上面那条语气规则同特指度,排在它之后:两个都写时色板赢——
67
+ 色板指名了一个具体颜色,语气只是推得出一个颜色,指名的那个更具体。
68
+ 取值一律逐值写死原语、不经 --xh-_tone:那一族槽位是语气层的派生结果,
69
+ 色板借道它就会连带把语气的悬停 / 淡底 / 前景一起绑进来,也会被祖先的 data-tone 染色。
70
+ 色板拼错取值时六条都不命中,落回上面两条,与没写一样 */
71
+ [data-scope='heatmap'][data-part='root'][data-palette='green'] {
72
+ --xh-_heatmap-ink: var(--xh-heatmap-ink, var(--xh-color-success-600));
73
+ }
74
+
75
+ [data-scope='heatmap'][data-part='root'][data-palette='blue'] {
76
+ --xh-_heatmap-ink: var(--xh-heatmap-ink, var(--xh-color-info-600));
77
+ }
78
+
79
+ [data-scope='heatmap'][data-part='root'][data-palette='orange'] {
80
+ --xh-_heatmap-ink: var(--xh-heatmap-ink, var(--xh-color-warning-600));
81
+ }
82
+
83
+ [data-scope='heatmap'][data-part='root'][data-palette='purple'] {
84
+ --xh-_heatmap-ink: var(--xh-heatmap-ink, var(--xh-color-purple-600));
85
+ }
86
+
87
+ [data-scope='heatmap'][data-part='root'][data-palette='red'] {
88
+ --xh-_heatmap-ink: var(--xh-heatmap-ink, var(--xh-color-danger-600));
89
+ }
90
+
91
+ /* 灰是唯一按主题换档的一族:五个彩色族的 600 档明度在 0.577–0.705,
92
+ 深色态的空格底(neutral-800,明度 0.269)离得够远;中性 600 档只有 0.439,
93
+ 五档摊下来每档只差 0.0425、相邻两档对比度 1.16–1.20,等于看不出分档。
94
+ 深色态改取 neutral-450(明度 0.65),步长回到 0.095、相邻两档 1.42–1.50,与彩色族齐平。
95
+ 不取更亮的 neutral-400:高对比档的 border-default 正是那一档,满档格子的描边会与底色同色。
96
+ data-theme 写在自己身上还是写在祖先上都算,两种用法都成立 */
97
+ [data-scope='heatmap'][data-part='root'][data-palette='gray'] {
98
+ --xh-_heatmap-ink: var(--xh-heatmap-ink, var(--xh-color-neutral-600));
99
+ }
100
+
101
+ :is([data-theme='dark'] *, [data-theme='dark'])[data-scope='heatmap'][data-part='root'][data-palette='gray'] {
102
+ --xh-_heatmap-ink: var(--xh-heatmap-ink, var(--xh-color-neutral-450));
103
+ }
104
+
105
+ /* —— 尺寸 —— */
106
+ /* 缺省的 md 档写在上面的基础规则里 */
107
+ [data-scope='heatmap'][data-part='root'][data-size='sm'] {
108
+ --xh-_heatmap-cell-size: var(--xh-heatmap-cell-size, var(--xh-space-2));
109
+ --xh-_heatmap-row-h: var(--xh-heatmap-row-h, var(--xh-space-4));
110
+ --xh-_heatmap-gutter: var(--xh-heatmap-gutter, var(--xh-space-6));
111
+ }
112
+
113
+ [data-scope='heatmap'][data-part='root'][data-size='lg'] {
114
+ --xh-_heatmap-cell-size: var(--xh-heatmap-cell-size, var(--xh-space-3));
115
+ --xh-_heatmap-row-h: var(--xh-heatmap-row-h, var(--xh-space-6));
116
+ }
117
+
118
+ [data-scope='heatmap'][data-part='grid'] {
119
+ display: flex;
120
+ flex-direction: column;
121
+ gap: var(--xh-heatmap-grid-gap, var(--xh-_heatmap-gap));
122
+ }
123
+
124
+ /* 网格自己只在没有格子可落焦点时才占 Tab 位,那几帧里也该看得出焦点在哪。
125
+ 环往内收:网格铺满 root,root 那层 overflow-x: auto 会把画在外沿的环裁掉 */
126
+ [data-scope='heatmap'][data-part='grid']:focus-visible {
127
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
128
+ outline-offset: calc(-1 * var(--xh-ring-width));
129
+ }
130
+
131
+ [data-scope='heatmap'][data-part='row'] {
132
+ /* 行首空出的列数由连接层写进这个槽:日历形态是区间起点不在周首日时的错列,
133
+ 月历形态是这一周里 1 号之前的那几个星期几 */
134
+ --xh-_heatmap-row-offset: 0;
135
+
136
+ display: flex;
137
+ /* 行至少要有内容那么宽。行是列方向 flex 的子项,缺省会被拉成容器的宽(一屏那么宽),
138
+ 行首钉住的那一列只能在自己这一行的盒子里钉——盒子只有一屏宽,滚过一屏它就跟着走了 */
139
+ min-inline-size: max-content;
140
+ align-items: center;
141
+ gap: var(--xh-heatmap-row-gap, var(--xh-_heatmap-gap));
142
+ }
143
+
144
+ /* 数据行的高度只由格子定。行首那一列星期名的字(12px)比格子(10px)高,让它撑开行
145
+ 会在纵向多挤出两像素,同一张图的横向格距与纵向格距就不一样宽。
146
+ 钉死成格高之后星期名的字仍照常画,只是溢出到行外——那几行隔行留白,撞不到东西 */
147
+ [data-scope='heatmap'][data-part='row'][data-week-day],
148
+ [data-scope='heatmap'][data-part='row'][data-week] {
149
+ block-size: var(--xh-_heatmap-cell-size);
150
+ }
151
+
152
+ /* 行首的星期名。月份行行首那个占位也是它,宽度一样,月份才对得上列。
153
+ 一整年放不下时网格横着滚,这一列钉在滚动容器的起始缘:跟着滚走的话,
154
+ 滚过头一屏就再也认不出哪一行是星期几。底色必须实色,格子从它下面滚过去不能透出来 */
155
+ [data-scope='heatmap'][data-part='week-day-label'] {
156
+ position: sticky;
157
+ /* 层号收在 root 建的层叠上下文里,只跟组件内的兄弟比 */
158
+ z-index: var(--xh-heatmap-sticky-layer, 1);
159
+ inset-inline-start: 0;
160
+ display: block;
161
+ flex: 0 0 auto;
162
+ inline-size: var(--xh-_heatmap-gutter);
163
+ overflow: hidden;
164
+ background: var(--xh-_heatmap-surface);
165
+ color: var(--xh-heatmap-label-fg, var(--xh-fg-subtle));
166
+ white-space: nowrap;
167
+ text-align: end;
168
+ }
169
+
170
+ /* 隔行画:这几行只是字不上色,节点、文字、盒子与底色都还在。
171
+ 不能用 visibility 关:那会连盒子一起不画,钉住那一列的实色底跟着缺四行,
172
+ 格子从行首透出来,且这四行不再参与命中测试——鼠标划过行首会弹出看不见那一格的详情条。
173
+ 一行只有 10px 高,七个 12px 的字挨着写会上下叠在一起;隔一行之后每个留下来的字
174
+ 有两行的高度(2 × 格高 + 格距)可用。哪一行是星期几由行自己的可及名字念出来,
175
+ 可见文字与可及名字是两件事,读屏这边一行都没少 */
176
+ [data-scope='heatmap'][data-part='week-day-label'][data-week-day='0'],
177
+ [data-scope='heatmap'][data-part='week-day-label'][data-week-day='2'],
178
+ [data-scope='heatmap'][data-part='week-day-label'][data-week-day='4'],
179
+ [data-scope='heatmap'][data-part='week-day-label'][data-week-day='6'] {
180
+ color: var(--xh-_heatmap-week-day-skip);
181
+ }
182
+
183
+ /* 月份名横跨这个月占的那几列:列宽加列间距,末尾少算一个间距(它由行的 gap 补上) */
184
+ [data-scope='heatmap'][data-part='month-label'] {
185
+ --xh-_heatmap-month-weeks: 1;
186
+
187
+ display: block;
188
+ flex: 0 0 auto;
189
+ inline-size: calc(
190
+ var(--xh-_heatmap-month-weeks) * (var(--xh-_heatmap-cell-size) + var(--xh-_heatmap-gap))
191
+ - var(--xh-_heatmap-gap)
192
+ );
193
+ overflow: hidden;
194
+ color: var(--xh-heatmap-label-fg, var(--xh-fg-subtle));
195
+ white-space: nowrap;
196
+ }
197
+
198
+ /* 一天一格。档位是连着的百分比,档数随便改都不必再写一条选择器 */
199
+ [data-scope='heatmap'][data-part='cell'] {
200
+ --xh-_heatmap-level: 0%;
201
+
202
+ flex: 0 0 auto;
203
+ inline-size: var(--xh-_heatmap-cell-size);
204
+ block-size: var(--xh-_heatmap-cell-size);
205
+ border-radius: var(--xh-heatmap-cell-radius, var(--xh-shape-inset));
206
+ background: var(
207
+ --xh-heatmap-cell-bg,
208
+ color-mix(in oklab, var(--xh-_heatmap-ink) var(--xh-_heatmap-level), var(--xh-_heatmap-empty))
209
+ );
210
+ /* 空格与浅档在浅色底上几乎没有边界,描一圈极淡的线把方格勾出来。
211
+ 这一档不能取 border-subtle:它与空格底色 bg-subtle 是同一个原色,描了等于没描 */
212
+ box-shadow: inset 0 0 0 var(--xh-stroke-thin) var(--xh-heatmap-cell-border, var(--xh-border-default));
213
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
214
+ }
215
+
216
+ /* 错列打在本行第一个格子上:打在行上会连行首的星期名一起往后推,
217
+ 左侧那一列星期名就变成锯齿。两条选择器合起来正好是「本行第一个格子」,
218
+ 行里写不写星期名都成立 */
219
+ [data-scope='heatmap'][data-part='row'] > [data-scope='heatmap'][data-part='cell']:first-child,
220
+ [data-scope='heatmap'][data-part='week-day-label'] + [data-scope='heatmap'][data-part='cell'] {
221
+ margin-inline-start: calc(
222
+ var(--xh-_heatmap-row-offset) * (var(--xh-_heatmap-cell-size) + var(--xh-_heatmap-gap))
223
+ );
224
+ }
225
+
226
+ [data-scope='heatmap'][data-part='cell']:focus-visible {
227
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
228
+ outline-offset: var(--xh-ring-offset);
229
+ }
230
+
231
+ /* —— 月历形态:一个自然月一块,块内一行是一周、一列是一个星期几 —— */
232
+ [data-scope='heatmap'][data-part='root'][data-variant='month'] [data-scope='heatmap'][data-part='grid'] {
233
+ flex-flow: row wrap;
234
+ gap: var(--xh-heatmap-block-gap, var(--xh-_heatmap-gutter));
235
+ }
236
+
237
+ [data-scope='heatmap'][data-part='month-block'] {
238
+ display: flex;
239
+ flex-direction: column;
240
+ gap: var(--xh-heatmap-block-inner-gap, var(--xh-_heatmap-gap));
241
+ }
242
+
243
+ /* 月历形态的月块是换行排开的,没有钉住的那一列,滚动落点也不必让开 */
244
+ [data-scope='heatmap'][data-part='root'][data-variant='month'] {
245
+ /* 这里的星期名是一排列头,七个一个都不能少:隔行不上色就有四列认不出是星期几 */
246
+ --xh-_heatmap-week-day-skip: var(--xh-heatmap-label-fg, var(--xh-fg-subtle));
247
+
248
+ /* 月块换行排开,宽度永不超过容器,这一档不当滚动容器:留着 overflow-x: auto 的话,
249
+ 下面那排列头(12px 的字装在 10px 的列里)往外溢的那点行盒会算进可滚动宽度,
250
+ 2px 的溢出就换来一条常驻的横向滚动条,白吃掉十几像素高。
251
+ clip 只裁不滚,再放出一个格距的余量让列头两端那点溢出照常画出来 */
252
+ overflow-x: clip;
253
+ overflow-clip-margin: var(--xh-_heatmap-gap);
254
+ scroll-padding-inline-start: 0;
255
+ }
256
+
257
+ /* 块里的星期名是一排列头,宽度改成格宽才对得上下面的格子,也不再钉住。
258
+ 字比一格宽,不裁:裁掉就是七个缺角的字,让它往两边的列间距里溢出去,
259
+ 相邻两个各溢出一点也还差着一段空白 */
260
+ [data-scope='heatmap'][data-part='root'][data-variant='month'] [data-scope='heatmap'][data-part='week-day-label'] {
261
+ position: static;
262
+ inline-size: var(--xh-_heatmap-cell-size);
263
+ overflow: visible;
264
+ background: none;
265
+ text-align: center;
266
+ }
267
+
268
+ /* 块的标题占整块宽,不再横跨若干列 */
269
+ [data-scope='heatmap'][data-part='root'][data-variant='month'] [data-scope='heatmap'][data-part='month-label'] {
270
+ inline-size: auto;
271
+ color: var(--xh-heatmap-title-fg, var(--xh-fg-default));
272
+ }
273
+
274
+ /* —— 矩阵形态:行列都由作者给,行名与列名是两条真表头 —— */
275
+ /* 行名与日历的星期名同一个位置、同一件事:列多到放不下时钉在起始缘 */
276
+ [data-scope='heatmap'][data-part='row-label'] {
277
+ position: sticky;
278
+ z-index: var(--xh-heatmap-sticky-layer, 1);
279
+ inset-inline-start: 0;
280
+ display: block;
281
+ flex: 0 0 auto;
282
+ inline-size: var(--xh-_heatmap-gutter);
283
+ overflow: hidden;
284
+ background: var(--xh-_heatmap-surface);
285
+ color: var(--xh-heatmap-label-fg, var(--xh-fg-subtle));
286
+ white-space: nowrap;
287
+ text-align: end;
288
+ }
289
+
290
+ /* 列名与它下面那一列的格子同宽:标签长就把列宽整体调大,别只调标签。
291
+ 两处的兜底都是行高,不写列宽时矩阵格子就是正方块 */
292
+ [data-scope='heatmap'][data-part='column-label'] {
293
+ display: block;
294
+ flex: 0 0 auto;
295
+ inline-size: var(--xh-heatmap-column-w, var(--xh-_heatmap-row-h));
296
+ overflow: hidden;
297
+ color: var(--xh-heatmap-label-fg, var(--xh-fg-subtle));
298
+ white-space: nowrap;
299
+ text-align: center;
300
+ }
301
+
302
+ /* 矩阵的格子两向都不走日方块那把尺:宽跟列走,高跟行走 */
303
+ [data-scope='heatmap'][data-part='root'][data-variant='matrix'] [data-scope='heatmap'][data-part='cell'] {
304
+ inline-size: var(--xh-heatmap-column-w, var(--xh-_heatmap-row-h));
305
+ block-size: var(--xh-_heatmap-row-h);
306
+ }
307
+
308
+ /* —— 详情条 —— */
309
+ /* 位置由连接层在事件那一刻量好写成内联样式(它量得到,样式表量不到);
310
+ 这四个是还没量到时的落点,量到即被内联样式盖过。四个都带长度单位:
311
+ 下面 block-start 那一档要把它们放进 calc(100% - …),无单位的 0 会让整条声明作废。
312
+ 不引浮层引擎:它按 root 的内边距盒绝对定位,跟着 root 一起横向滚动,
313
+ 摆上边还是摆下边由连接层按行序给出,因此始终压在网格自己身上,不会被 root 的滚动裁掉 */
314
+ [data-scope='heatmap'][data-part='tooltip'] {
315
+ --xh-_heatmap-tip-x: 0px;
316
+ --xh-_heatmap-tip-y: 0px;
317
+ --xh-_heatmap-tip-w: 0px;
318
+ --xh-_heatmap-tip-h: 0px;
319
+
320
+ position: absolute;
321
+ /* 排在钉住的行首那一列之上:条从末缘往回长时会盖到那一列上,它不该把条切掉 */
322
+ z-index: var(--xh-heatmap-tooltip-layer, 2);
323
+ display: block;
324
+ inset-inline-start: var(--xh-_heatmap-tip-x);
325
+ inset-block-start: var(--xh-_heatmap-tip-y);
326
+ /* 至少与格子同宽,一两个字的详情也不会瘦成一条 */
327
+ min-inline-size: var(--xh-_heatmap-tip-w);
328
+ max-inline-size: var(--xh-heatmap-tooltip-max-w, var(--xh-overlay-max-w));
329
+ padding-block: var(--xh-heatmap-tooltip-py, var(--xh-space-1));
330
+ padding-inline: var(--xh-heatmap-tooltip-px, var(--xh-space-2));
331
+ border-radius: var(--xh-heatmap-tooltip-radius, var(--xh-shape-control));
332
+ background: var(--xh-heatmap-tooltip-bg, var(--xh-fg-default));
333
+ color: var(--xh-heatmap-tooltip-fg, var(--xh-bg-surface));
334
+ font-size: var(--xh-heatmap-tooltip-font-size, var(--xh-text-caption-size));
335
+ line-height: var(--xh-leading-normal);
336
+ white-space: nowrap;
337
+ box-shadow: var(--xh-heatmap-tooltip-shadow, var(--xh-elevation-floating));
338
+ /* 详情条不接指针:压在格子上时指针要能穿过去,否则一进条里就算离开了格子,
339
+ 详情自己把自己收起来,来回闪 */
340
+ pointer-events: none;
341
+ }
342
+
343
+ /* 从格子的末缘往回长:条比一格宽得多,格子后面的地方比前面少时往后长会伸出滚动容器被裁掉,
344
+ 还会把横向滚动条撑长。按末缘定位,条自己多宽都贴着格子的末沿。
345
+ 百分比是内边距盒的宽,在这条 calc 里前后抵消,横向滚到哪里都成立 */
346
+ [data-scope='heatmap'][data-part='tooltip'][data-inline-anchor='end'] {
347
+ inset-inline-start: auto;
348
+ inset-inline-end: calc(100% - var(--xh-_heatmap-tip-x) - var(--xh-_heatmap-tip-w));
349
+ }
350
+
351
+ /* 摆在格子下边:从格子的下沿再让开一个格间距 */
352
+ [data-scope='heatmap'][data-part='tooltip'][data-placement='block-end'] {
353
+ inset-block-start: calc(
354
+ var(--xh-_heatmap-tip-y) + var(--xh-_heatmap-tip-h) + var(--xh-_heatmap-gap)
355
+ );
356
+ }
357
+
358
+ /* 摆在格子上边:按末缘定位,条自己多高都贴着格子的上沿。
359
+ 两头都写就是过约束,起始缘要先让开 */
360
+ [data-scope='heatmap'][data-part='tooltip'][data-placement='block-start'] {
361
+ inset-block-start: auto;
362
+ inset-block-end: calc(100% - var(--xh-_heatmap-tip-y) + var(--xh-_heatmap-gap));
363
+ }
364
+
365
+ [data-scope='heatmap'][data-part='legend'] {
366
+ display: flex;
367
+ align-items: center;
368
+ gap: var(--xh-heatmap-legend-gap, var(--xh-_heatmap-gap));
369
+ color: var(--xh-heatmap-label-fg, var(--xh-fg-subtle));
370
+ }
371
+
372
+ /* 对照条两端的那两个字。与色块同一行、同一档颜色,不换行:
373
+ 「少」「多」断成两行就跟色块对不上了 */
374
+ [data-scope='heatmap'][data-part='legend-label'] {
375
+ flex: 0 0 auto;
376
+ white-space: nowrap;
377
+ }
378
+
379
+ /* 对照条里的一格与网格里同档的格子同色,两处共用同一条色阶 */
380
+ [data-scope='heatmap'][data-part='legend-item'] {
381
+ --xh-_heatmap-level: 0%;
382
+
383
+ display: block;
384
+ flex: 0 0 auto;
385
+ inline-size: var(--xh-_heatmap-cell-size);
386
+ block-size: var(--xh-_heatmap-cell-size);
387
+ border-radius: var(--xh-heatmap-cell-radius, var(--xh-shape-inset));
388
+ background: var(
389
+ --xh-heatmap-cell-bg,
390
+ color-mix(in oklab, var(--xh-_heatmap-ink) var(--xh-_heatmap-level), var(--xh-_heatmap-empty))
391
+ );
392
+ box-shadow: inset 0 0 0 var(--xh-stroke-thin) var(--xh-heatmap-cell-border, var(--xh-border-default));
393
+ }
394
+
395
+ /* 给了 display 的角色节点必须补这条:display 声明会盖掉 UA 的 [hidden]{display:none},
396
+ 不补的话作者给节点加 hidden 后它照样显示。详情条收起靠的正是这一条。
397
+ 格子没有声明 display,UA 那条兜得住,不必列在这里 */
398
+ [data-scope='heatmap'][data-part='root'][hidden],
399
+ [data-scope='heatmap'][data-part='grid'][hidden],
400
+ [data-scope='heatmap'][data-part='month-block'][hidden],
401
+ [data-scope='heatmap'][data-part='row'][hidden],
402
+ [data-scope='heatmap'][data-part='week-day-label'][hidden],
403
+ [data-scope='heatmap'][data-part='month-label'][hidden],
404
+ [data-scope='heatmap'][data-part='row-label'][hidden],
405
+ [data-scope='heatmap'][data-part='column-label'][hidden],
406
+ [data-scope='heatmap'][data-part='tooltip'][hidden],
407
+ [data-scope='heatmap'][data-part='legend'][hidden],
408
+ [data-scope='heatmap'][data-part='legend-item'][hidden] {
409
+ display: none;
410
+ }
411
+ }
package/css/highlight.css CHANGED
@@ -13,8 +13,8 @@
13
13
  /* 浏览器给 mark 的默认底色是一个固定的亮黄,暗色主题下压在深底上刺眼、
14
14
  配的黑字也不随主题走;这里整份换成随主题走的品牌淡底 */
15
15
  [data-scope='highlight'][data-part='mark'] {
16
- padding-inline: var(--xh-space-0_5);
17
- border-radius: var(--xh-highlight-radius, var(--xh-radius-sm));
16
+ padding-inline: var(--xh-highlight-mark-px, var(--xh-space-0_5));
17
+ border-radius: var(--xh-highlight-radius, var(--xh-shape-inset));
18
18
  background: var(--xh-highlight-bg, var(--xh-bg-brand-subtle));
19
19
  color: var(--xh-highlight-fg, var(--xh-fg-brand-strong));
20
20
  font-weight: var(--xh-font-weight-medium);
@@ -0,0 +1,90 @@
1
+ @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='hotkeys'] {
4
+ --xh-hotkeys-skin: 1;
5
+ }
6
+
7
+ /* 一排键帽连着连接符。整块不换行:一组组合被折成两行就读不出是一组了 */
8
+ [data-scope='hotkeys'][data-part='root'] {
9
+ /* 尺寸档只换这两个私有槽,键帽从 root 继承它们,换档不必给每个部件各写一条选择器 */
10
+ --xh-_hotkeys-font-size: var(--xh-control-caption-md);
11
+ --xh-_hotkeys-key-px: var(--xh-space-1);
12
+
13
+ display: inline-flex;
14
+ align-items: center;
15
+ gap: var(--xh-hotkeys-gap, var(--xh-space-0_5));
16
+ color: var(--xh-hotkeys-fg, var(--xh-fg-muted));
17
+ font-size: var(--xh-hotkeys-font-size, var(--xh-_hotkeys-font-size));
18
+ line-height: var(--xh-leading-none);
19
+ white-space: nowrap;
20
+ }
21
+
22
+ /* 一枚键帽:等宽字,带描边与一层薄阴影凑出按键的样子。
23
+ min-inline-size 保证单个字母的键帽不至于窄成一条 */
24
+ [data-scope='hotkeys'][data-part='key'] {
25
+ display: inline-flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ min-inline-size: var(--xh-hotkeys-key-min-w, var(--xh-control-indicator-size));
29
+ padding-block: var(--xh-hotkeys-key-py, var(--xh-space-0_5));
30
+ padding-inline: var(--xh-hotkeys-key-px, var(--xh-_hotkeys-key-px));
31
+ border: var(--xh-stroke-thin) solid var(--xh-hotkeys-key-border, var(--xh-border-subtle));
32
+ border-radius: var(--xh-hotkeys-key-radius, var(--xh-shape-inset));
33
+ background: var(--xh-hotkeys-key-bg, var(--xh-bg-surface));
34
+ box-shadow: var(--xh-hotkeys-key-shadow, var(--xh-elevation-raised));
35
+ color: var(--xh-hotkeys-key-fg, var(--xh-fg-default));
36
+ /* kbd 的 UA 字体是等宽,但字号会被压小;两者都显式接过来,档位才归尺寸轴管 */
37
+ font-family: var(--xh-hotkeys-key-font, var(--xh-font-family-mono));
38
+ font-size: inherit;
39
+ font-weight: var(--xh-hotkeys-key-font-weight, var(--xh-font-weight-medium));
40
+ line-height: var(--xh-leading-none);
41
+ }
42
+
43
+ /* 修饰键多是符号,与主键的字母混排时字宽差得远,单独留一档给使用者调 */
44
+ [data-scope='hotkeys'][data-part='key'][data-modifier] {
45
+ color: var(--xh-hotkeys-key-fg-modifier, var(--xh-fg-muted));
46
+ }
47
+
48
+ /* 连接符是排版符号,不参与键帽的盒子 */
49
+ [data-scope='hotkeys'][data-part='separator'] {
50
+ color: var(--xh-hotkeys-separator-fg, var(--xh-fg-subtle));
51
+ user-select: none;
52
+ }
53
+
54
+ /* 监听关掉时组合按不出来,整块转成不可用的样子:键帽换成暗底、去掉阴影,前景压到 fg-subtle。
55
+ 这块是 role=img 的说明性文字而不是失效控件,读者仍要认出按的是哪几枚键,
56
+ 所以前景不用 fg-disabled(那一档压在 muted 底上只有 2.36:1 / 1.94:1),也不叠 opacity */
57
+ [data-scope='hotkeys'][data-part='root'][data-disabled] {
58
+ color: var(--xh-fg-subtle);
59
+ }
60
+
61
+ [data-scope='hotkeys'][data-part='root'][data-disabled] [data-scope='hotkeys'][data-part='key'] {
62
+ background: var(--xh-hotkeys-key-bg-disabled, var(--xh-bg-muted));
63
+ box-shadow: none;
64
+ color: var(--xh-fg-subtle);
65
+ }
66
+
67
+ [data-scope='hotkeys'][data-part='root'][data-disabled] [data-scope='hotkeys'][data-part='separator'] {
68
+ color: var(--xh-fg-subtle);
69
+ }
70
+
71
+ /* —— 尺寸 —— */
72
+ /* 缺省的 md 档写在上面的基础规则里 */
73
+ [data-scope='hotkeys'][data-part='root'][data-size='sm'] {
74
+ --xh-_hotkeys-font-size: var(--xh-control-caption-sm);
75
+ --xh-_hotkeys-key-px: var(--xh-space-0_5);
76
+ }
77
+
78
+ [data-scope='hotkeys'][data-part='root'][data-size='lg'] {
79
+ --xh-_hotkeys-font-size: var(--xh-control-caption-lg);
80
+ --xh-_hotkeys-key-px: var(--xh-space-1_5);
81
+ }
82
+
83
+ /* 给了 display 的角色节点必须补这条:display 声明会盖掉 UA 的 [hidden]{display:none},
84
+ 不补的话作者给节点加 hidden 后它照样显示。Mac 写法下连接符恒带 hidden,靠这条收起 */
85
+ [data-scope='hotkeys'][data-part='root'][hidden],
86
+ [data-scope='hotkeys'][data-part='key'][hidden],
87
+ [data-scope='hotkeys'][data-part='separator'][hidden] {
88
+ display: none;
89
+ }
90
+ }
@@ -1,13 +1,26 @@
1
1
  @layer xihan.motion {
2
2
  /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
3
3
  动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
4
- @keyframes xh-fade-in {
4
+ @keyframes xh-pop-in {
5
5
  from {
6
6
  opacity: 0;
7
+ scale: var(--xh-motion-scale-enter);
7
8
  }
8
9
 
9
10
  to {
10
11
  opacity: 1;
12
+ scale: 1;
13
+ }
14
+ }
15
+ @keyframes xh-pop-out {
16
+ from {
17
+ opacity: 1;
18
+ scale: 1;
19
+ }
20
+
21
+ to {
22
+ opacity: 0;
23
+ scale: var(--xh-motion-scale-enter);
11
24
  }
12
25
  }
13
26
  }
@@ -50,8 +63,15 @@
50
63
 
51
64
  /* 坐标由定位引擎写进 style(inset-inline-start / inset-block-start),这里只定位模式与层级 */
52
65
  [data-scope='hover-card'][data-part='positioner'] {
66
+ /* 落定那一侧的可用高度,由定位引擎经内联自定义属性写进来,内容层继承它限高;
67
+ 引擎没算(尚未落位 / 无引擎 / 贴边归零)时不额外限高 */
68
+ --xh-_hover-card-available-h: 100vh;
69
+
70
+ /* 卡片内容的自绘条浮在内容之上:轨道不上底色,实色轨道会在卡片右缘糊一条竖灰带 */
71
+ --xh-scrollbar-track-bg: transparent;
72
+
53
73
  position: fixed;
54
- z-index: var(--xh-layer-popover);
74
+ z-index: var(--xh-hover-card-layer, var(--xh-layer-popover));
55
75
  /* 空壳不吃指针:trigger 与 content 之间那段间隙要能点得穿,
56
76
  指针穿行的判定全靠 content 自己的进出事件 */
57
77
  pointer-events: none;
@@ -77,22 +97,24 @@
77
97
  gap: var(--xh-hover-card-gap, var(--xh-space-2));
78
98
  inline-size: max-content;
79
99
  max-inline-size: var(--xh-hover-card-max-w, var(--xh-_hover-card-max-w));
100
+ /* 内容由作者给,长度不可预知:不限高就整块伸出视口且滚不到 */
101
+ max-block-size: min(var(--xh-hover-card-max-h, var(--xh-overlay-max-h)), var(--xh-_hover-card-available-h));
102
+ overflow-y: auto;
80
103
  padding-block: var(--xh-hover-card-py, var(--xh-_hover-card-py));
81
104
  padding-inline: var(--xh-hover-card-px, var(--xh-_hover-card-px));
82
105
  border: var(--xh-stroke-thin) solid var(--xh-hover-card-border, var(--xh-border-default));
83
106
  border-radius: var(--xh-hover-card-radius, var(--xh-shape-surface));
84
107
  background: var(--xh-hover-card-bg, var(--xh-bg-surface));
85
108
  color: var(--xh-hover-card-fg, var(--xh-fg-default));
86
- font-size: var(--xh-hover-card-font-size, var(--xh-text-body-size));
87
- line-height: var(--xh-leading-normal);
88
- box-shadow: var(--xh-hover-card-shadow, var(--xh-elevation-2));
109
+ box-shadow: var(--xh-hover-card-shadow, var(--xh-elevation-floating));
89
110
  pointer-events: auto;
90
111
  }
91
112
 
92
- /* display 会盖掉 hidden 属性的 UA 规则,收起态显式让位 */
93
- [data-scope='hover-card'][data-part='content'][hidden] {
94
- display: none;
95
- }
113
+ /* 这里刻意没有 [data-part='content'][hidden] { display: none },与 dialog 一致:
114
+ 收起态连接层会给 contenthidden,而上面那条 display 正是要盖掉 UA 的
115
+ [hidden]{display:none}——退场动画得先能播。presence 读的是 content 的 animationName,
116
+ 真正的收起由宿主在退场结束后落成内联 display:none(Vue 那边直接不渲染)。
117
+ 其余声明了 display 的角色节点仍补 [hidden],那几处没有退场动画。 */
96
118
 
97
119
  /* 卡片是可编程聚焦的容器(tabindex=-1),焦点环留给里面真正的可聚焦元素 */
98
120
  [data-scope='hover-card'][data-part='content']:focus-visible {
@@ -100,7 +122,13 @@
100
122
  }
101
123
 
102
124
  [data-scope='hover-card'][data-part='content'][data-state='open'] {
103
- animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
125
+ animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
126
+ }
127
+
128
+ /* 退场:收起那一帧 hidden 由退场闸门押后,这里才播得出来。forwards 把末态钉住,
129
+ 免得动画结束到真收起之间闪一下原样 */
130
+ [data-scope='hover-card'][data-part='content'][data-state='closed'] {
131
+ animation: xh-pop-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
104
132
  }
105
133
 
106
134
  /* —— 尺寸 —— */
@@ -118,46 +146,16 @@
118
146
  --xh-_hover-card-max-w: var(--xh-overlay-max-w-lg);
119
147
  }
120
148
 
121
- /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
149
+ /* 箭头的底色与描边色,几何在 overlay-arrow.css。
122
150
  交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
123
151
  [data-scope='hover-card'][data-part='arrow'] {
124
- --xh-_hover-card-arrow-size: var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size));
125
152
  --xh-_hover-card-arrow-x: 50%;
126
153
  --xh-_hover-card-arrow-y: 50%;
154
+ --xh-_overlay-arrow-size: var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size));
155
+ --xh-_overlay-arrow-x: var(--xh-_hover-card-arrow-x);
156
+ --xh-_overlay-arrow-y: var(--xh-_hover-card-arrow-y);
127
157
 
128
- position: absolute;
129
- inline-size: var(--xh-_hover-card-arrow-size);
130
- block-size: var(--xh-_hover-card-arrow-size);
131
- border: var(--xh-stroke-thin) solid var(--xh-hover-card-border, var(--xh-border-default));
132
158
  background: var(--xh-hover-card-bg, var(--xh-bg-surface));
133
- rotate: 45deg;
134
- }
135
-
136
- [data-scope='hover-card'][data-part='arrow'][data-placement^='bottom'] {
137
- inset-block-start: calc(var(--xh-_hover-card-arrow-size) / -2);
138
- inset-inline-start: calc(var(--xh-_hover-card-arrow-x) - var(--xh-_hover-card-arrow-size) / 2);
139
- border-block-end: 0;
140
- border-inline-end: 0;
141
- }
142
-
143
- [data-scope='hover-card'][data-part='arrow'][data-placement^='top'] {
144
- inset-block-end: calc(var(--xh-_hover-card-arrow-size) / -2);
145
- inset-inline-start: calc(var(--xh-_hover-card-arrow-x) - var(--xh-_hover-card-arrow-size) / 2);
146
- border-block-start: 0;
147
- border-inline-start: 0;
148
- }
149
-
150
- [data-scope='hover-card'][data-part='arrow'][data-placement^='right'] {
151
- inset-inline-start: calc(var(--xh-_hover-card-arrow-size) / -2);
152
- inset-block-start: calc(var(--xh-_hover-card-arrow-y) - var(--xh-_hover-card-arrow-size) / 2);
153
- border-block-start: 0;
154
- border-inline-end: 0;
155
- }
156
-
157
- [data-scope='hover-card'][data-part='arrow'][data-placement^='left'] {
158
- inset-inline-end: calc(var(--xh-_hover-card-arrow-size) / -2);
159
- inset-block-start: calc(var(--xh-_hover-card-arrow-y) - var(--xh-_hover-card-arrow-size) / 2);
160
- border-block-end: 0;
161
- border-inline-start: 0;
159
+ border: var(--xh-stroke-thin) solid var(--xh-hover-card-border, var(--xh-border-default));
162
160
  }
163
161
  }