@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.
- package/CHANGELOG.md +44 -0
- package/LICENSE +21 -0
- package/README.md +27 -0
- package/css/accordion.css +108 -0
- package/css/affix.css +27 -0
- package/css/alert.css +108 -0
- package/css/anchor.css +137 -0
- package/css/avatar-group.css +82 -0
- package/css/avatar.css +55 -0
- package/css/back-top.css +61 -0
- package/css/badge.css +50 -0
- package/css/breadcrumb.css +127 -0
- package/css/button-group.css +131 -0
- package/css/button.css +98 -0
- package/css/calendar.css +219 -0
- package/css/card.css +116 -0
- package/css/carousel.css +193 -0
- package/css/cascader.css +397 -0
- package/css/checkbox-group.css +185 -0
- package/css/checkbox.css +91 -0
- package/css/clipboard.css +133 -0
- package/css/code-block.css +81 -0
- package/css/collapsible.css +63 -0
- package/css/color-picker.css +424 -0
- package/css/combobox.css +433 -0
- package/css/composer.css +127 -0
- package/css/context-menu.css +263 -0
- package/css/countdown.css +21 -0
- package/css/date-field.css +200 -0
- package/css/date-picker.css +231 -0
- package/css/descriptions.css +200 -0
- package/css/dialog.css +127 -0
- package/css/drawer.css +372 -0
- package/css/dynamic-input.css +157 -0
- package/css/editable.css +249 -0
- package/css/ellipsis.css +41 -0
- package/css/empty-state.css +78 -0
- package/css/field.css +65 -0
- package/css/file-upload.css +298 -0
- package/css/flex.css +95 -0
- package/css/float-button.css +121 -0
- package/css/form.css +185 -0
- package/css/gradient-text.css +62 -0
- package/css/grid.css +428 -0
- package/css/highlight.css +21 -0
- package/css/hover-card.css +146 -0
- package/css/icon-wrapper.css +64 -0
- package/css/icon.css +54 -0
- package/css/image.css +45 -0
- package/css/infinite-scroll.css +22 -0
- package/css/layers.css +2 -0
- package/css/layout.css +169 -0
- package/css/list.css +117 -0
- package/css/listbox.css +174 -0
- package/css/loading-bar.css +66 -0
- package/css/log.css +66 -0
- package/css/marquee.css +182 -0
- package/css/mention.css +222 -0
- package/css/menu.css +144 -0
- package/css/menubar.css +271 -0
- package/css/motion.css +58 -0
- package/css/navigation-menu.css +270 -0
- package/css/number-animation.css +32 -0
- package/css/number-field.css +219 -0
- package/css/page-header.css +85 -0
- package/css/pagination.css +148 -0
- package/css/pin-input.css +157 -0
- package/css/popconfirm.css +150 -0
- package/css/popover.css +150 -0
- package/css/popselect.css +207 -0
- package/css/progress.css +37 -0
- package/css/qr-code.css +50 -0
- package/css/radio-group.css +101 -0
- package/css/rating.css +141 -0
- package/css/reset.css +22 -0
- package/css/result.css +104 -0
- package/css/scroll-area.css +176 -0
- package/css/select.css +264 -0
- package/css/separator.css +17 -0
- package/css/skeleton.css +78 -0
- package/css/slider.css +197 -0
- package/css/spinner.css +74 -0
- package/css/splitter.css +127 -0
- package/css/statistic.css +67 -0
- package/css/steps.css +252 -0
- package/css/switch.css +61 -0
- package/css/table.css +404 -0
- package/css/tabs.css +157 -0
- package/css/tags-input.css +360 -0
- package/css/text-field.css +195 -0
- package/css/thread.css +107 -0
- package/css/time-field.css +200 -0
- package/css/time-picker.css +347 -0
- package/css/time.css +20 -0
- package/css/timeline.css +284 -0
- package/css/toast.css +195 -0
- package/css/toaster.css +69 -0
- package/css/toggle-group.css +160 -0
- package/css/toggle.css +103 -0
- package/css/tone.css +85 -0
- package/css/toolbar.css +114 -0
- package/css/tooltip.css +109 -0
- package/css/tour.css +305 -0
- package/css/transfer.css +318 -0
- package/css/tree-select.css +441 -0
- package/css/tree.css +204 -0
- package/css/typography.css +151 -0
- package/css/undefined.css +38 -0
- package/css/virtualizer.css +104 -0
- package/css/visually-hidden.css +13 -0
- package/css/watermark.css +48 -0
- package/index.css +113 -0
- package/index.unlayered.css +16164 -0
- package/package.json +163 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
@layer xihan.components {
|
|
2
|
+
/* 分段控件:段与段之间不留缝,靠共边连成一体,所以 root 不设 gap。
|
|
3
|
+
排布只能读 data-orientation——aria-orientation 在多选模式下连接层根本不输出(role=group 不收) */
|
|
4
|
+
[data-scope='toggle-group'][data-part='root'] {
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
align-items: stretch;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
[data-scope='toggle-group'][data-part='root'][data-orientation='horizontal'] {
|
|
10
|
+
flex-direction: row;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
[data-scope='toggle-group'][data-part='root'][data-orientation='vertical'] {
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* root 会短暂拿到 tabindex=0,但它的 onFocus 立刻把焦点转投给条目,
|
|
18
|
+
在它身上画环只会闪一下就跳走,高亮要长在条目上 */
|
|
19
|
+
[data-scope='toggle-group'][data-part='root']:focus-visible {
|
|
20
|
+
outline: none;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
[data-scope='toggle-group'][data-part='root'][data-disabled] {
|
|
24
|
+
cursor: not-allowed;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
[data-scope='toggle-group'][data-part='item'] {
|
|
28
|
+
/* 私有槽位:组件令牌 → 语义令牌两级回退。状态规则只改写槽位,
|
|
29
|
+
底色/描边/前景三者永远同步换,不会出现选中了底色却没跟上描边 */
|
|
30
|
+
--xh-_bg: var(--xh-toggle-group-item-bg, var(--xh-bg-canvas));
|
|
31
|
+
--xh-_bg-hover: var(--xh-toggle-group-item-bg-hover, var(--xh-bg-subtle-hover));
|
|
32
|
+
--xh-_bg-active: var(--xh-toggle-group-item-bg-active, var(--xh-bg-subtle-active));
|
|
33
|
+
--xh-_fg: var(--xh-toggle-group-item-fg, var(--xh-fg-default));
|
|
34
|
+
--xh-_border: var(--xh-toggle-group-item-border, var(--xh-border-default));
|
|
35
|
+
|
|
36
|
+
/* 相邻两段靠负外边距压掉一条重边,谁的描边浮在上面必须能指定,
|
|
37
|
+
因此条目必须是定位元素——静态元素的 z-index 不生效 */
|
|
38
|
+
position: relative;
|
|
39
|
+
z-index: 0;
|
|
40
|
+
display: inline-flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
gap: var(--xh-toggle-group-item-gap, var(--xh-control-gap-md));
|
|
44
|
+
block-size: var(--xh-toggle-group-item-h, var(--xh-control-h-md));
|
|
45
|
+
padding-inline: var(--xh-toggle-group-item-px, var(--xh-control-px-md));
|
|
46
|
+
border: var(--xh-stroke-thin) solid var(--xh-_border);
|
|
47
|
+
/* 中间段一律直角,圆角只由首末两端补回来 */
|
|
48
|
+
border-radius: 0;
|
|
49
|
+
background: var(--xh-_bg);
|
|
50
|
+
color: var(--xh-_fg);
|
|
51
|
+
font-size: var(--xh-toggle-group-item-font-size, var(--xh-text-label-size));
|
|
52
|
+
font-weight: var(--xh-toggle-group-item-font-weight, var(--xh-text-label-weight));
|
|
53
|
+
line-height: var(--xh-leading-none);
|
|
54
|
+
white-space: nowrap;
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
user-select: none;
|
|
57
|
+
transition:
|
|
58
|
+
background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
|
|
59
|
+
border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
|
|
60
|
+
color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* 共边:后一段往回挪一个描边宽度,两条相邻的边重合成一条。
|
|
64
|
+
方向跟着 data-orientation 走,横排收 inline 轴、竖排收 block 轴 */
|
|
65
|
+
[data-scope='toggle-group'][data-part='root'][data-orientation='horizontal']
|
|
66
|
+
> [data-scope='toggle-group'][data-part='item']
|
|
67
|
+
+ [data-scope='toggle-group'][data-part='item'] {
|
|
68
|
+
margin-inline-start: calc(-1 * var(--xh-stroke-thin));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
[data-scope='toggle-group'][data-part='root'][data-orientation='vertical']
|
|
72
|
+
> [data-scope='toggle-group'][data-part='item']
|
|
73
|
+
+ [data-scope='toggle-group'][data-part='item'] {
|
|
74
|
+
margin-block-start: calc(-1 * var(--xh-stroke-thin));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* 两端圆角用逻辑角属性写,rtl 与竖排都跟着换边,不必另写一套。
|
|
78
|
+
只认 root 的直接子节点:条目之间插了别的节点时宁可退化成直角,也不要圆错位置 */
|
|
79
|
+
[data-scope='toggle-group'][data-part='root'][data-orientation='horizontal']
|
|
80
|
+
> [data-scope='toggle-group'][data-part='item']:first-child {
|
|
81
|
+
border-start-start-radius: var(--xh-toggle-group-radius, var(--xh-shape-control));
|
|
82
|
+
border-end-start-radius: var(--xh-toggle-group-radius, var(--xh-shape-control));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
[data-scope='toggle-group'][data-part='root'][data-orientation='horizontal']
|
|
86
|
+
> [data-scope='toggle-group'][data-part='item']:last-child {
|
|
87
|
+
border-start-end-radius: var(--xh-toggle-group-radius, var(--xh-shape-control));
|
|
88
|
+
border-end-end-radius: var(--xh-toggle-group-radius, var(--xh-shape-control));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
[data-scope='toggle-group'][data-part='root'][data-orientation='vertical']
|
|
92
|
+
> [data-scope='toggle-group'][data-part='item']:first-child {
|
|
93
|
+
border-start-start-radius: var(--xh-toggle-group-radius, var(--xh-shape-control));
|
|
94
|
+
border-start-end-radius: var(--xh-toggle-group-radius, var(--xh-shape-control));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
[data-scope='toggle-group'][data-part='root'][data-orientation='vertical']
|
|
98
|
+
> [data-scope='toggle-group'][data-part='item']:last-child {
|
|
99
|
+
border-end-start-radius: var(--xh-toggle-group-radius, var(--xh-shape-control));
|
|
100
|
+
border-end-end-radius: var(--xh-toggle-group-radius, var(--xh-shape-control));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[data-scope='toggle-group'][data-part='item']:not([data-disabled]):hover {
|
|
104
|
+
background: var(--xh-_bg-hover);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
[data-scope='toggle-group'][data-part='item']:not([data-disabled]):active {
|
|
108
|
+
background: var(--xh-_bg-active);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* 选中态:单选是 role=radio + aria-checked,多选是原生按钮 + aria-pressed,
|
|
112
|
+
两套 ARIA 各出一半,样式一条都对不上——只有 data-state 是两种模式共同的出口 */
|
|
113
|
+
[data-scope='toggle-group'][data-part='item'][data-state='on'] {
|
|
114
|
+
--xh-_bg: var(--xh-toggle-group-item-bg-on, var(--xh-bg-brand));
|
|
115
|
+
--xh-_bg-hover: var(--xh-toggle-group-item-bg-on-hover, var(--xh-bg-brand-hover));
|
|
116
|
+
--xh-_bg-active: var(--xh-toggle-group-item-bg-on-active, var(--xh-bg-brand-active));
|
|
117
|
+
--xh-_fg: var(--xh-toggle-group-item-fg-on, var(--xh-fg-on-brand));
|
|
118
|
+
--xh-_border: var(--xh-toggle-group-item-border-on, var(--xh-bg-brand));
|
|
119
|
+
|
|
120
|
+
/* 抬一层,让 brand 描边压住邻居的灰边。共边处两条边重合在同一像素上,
|
|
121
|
+
不抬层就由文档序决定谁在上,选中段的左右两侧会一边有色一边灰 */
|
|
122
|
+
z-index: 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* 集合条目一律 aria-disabled 而非原生 disabled(禁用项仍要能当方向键的起点),
|
|
126
|
+
所以这里读 data-disabled,写 :disabled 永远不命中。
|
|
127
|
+
另外禁用不能用 opacity 变淡:半透明会把压在下面那条边透出来,共边处当场变成两条线 */
|
|
128
|
+
[data-scope='toggle-group'][data-part='item'][data-disabled] {
|
|
129
|
+
--xh-_bg: var(--xh-toggle-group-item-bg-disabled, var(--xh-bg-muted));
|
|
130
|
+
--xh-_fg: var(--xh-toggle-group-item-fg-disabled, var(--xh-fg-disabled));
|
|
131
|
+
--xh-_border: var(--xh-toggle-group-item-border-disabled, var(--xh-border-subtle));
|
|
132
|
+
|
|
133
|
+
cursor: not-allowed;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* 禁用且选中:底色不能一路退回 muted,那样"这一段是当前值"的信息就整个没了。
|
|
137
|
+
掺一半 muted 进 brand,既看得出选中,也看得出改不动 */
|
|
138
|
+
[data-scope='toggle-group'][data-part='item'][data-disabled][data-state='on'] {
|
|
139
|
+
--xh-_bg: var(
|
|
140
|
+
--xh-toggle-group-item-bg-on-disabled,
|
|
141
|
+
color-mix(in oklab, var(--xh-bg-brand) 50%, var(--xh-bg-muted))
|
|
142
|
+
);
|
|
143
|
+
--xh-_fg: var(--xh-toggle-group-item-fg-on-disabled, var(--xh-fg-on-brand));
|
|
144
|
+
--xh-_border: var(--xh-toggle-group-item-border-on-disabled, var(--xh-bg-brand));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* 焦点环要浮在共边之上:outline 画在元素外侧,不抬层就会被后一段的底色与描边盖掉一半 */
|
|
148
|
+
[data-scope='toggle-group'][data-part='item']:focus-visible {
|
|
149
|
+
z-index: 2;
|
|
150
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
151
|
+
outline-offset: var(--xh-ring-offset);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* 给了 display 的角色节点必须补这条:display 声明会盖掉 UA 的 [hidden]{display:none},
|
|
155
|
+
不补的话作者给节点加 hidden 后它照样显示 */
|
|
156
|
+
[data-scope='toggle-group'][data-part='root'][hidden],
|
|
157
|
+
[data-scope='toggle-group'][data-part='item'][hidden] {
|
|
158
|
+
display: none;
|
|
159
|
+
}
|
|
160
|
+
}
|
package/css/toggle.css
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
@layer xihan.components {
|
|
2
|
+
[data-scope='toggle'][data-part='root'] {
|
|
3
|
+
/* 私有槽位:组件令牌 → 语义令牌两级回退,变体只改这几个槽位 */
|
|
4
|
+
--xh-_bg: var(--xh-toggle-bg, transparent);
|
|
5
|
+
--xh-_bg-hover: var(--xh-toggle-bg-hover, var(--xh-bg-subtle-hover));
|
|
6
|
+
--xh-_fg: var(--xh-toggle-fg, var(--xh-fg-default));
|
|
7
|
+
|
|
8
|
+
/* 按下态:强调交给底色。前景不用品牌色——subtle-active 这一档底上任何品牌色都过不了
|
|
9
|
+
AA(浅色最好的 brand-700 只有 4.37),强调靠底、可读靠前景是更稳的分工 */
|
|
10
|
+
--xh-_bg-pressed: var(--xh-toggle-bg-pressed, var(--xh-bg-subtle-active));
|
|
11
|
+
--xh-_fg-pressed: var(--xh-toggle-fg-pressed, var(--xh-fg-default));
|
|
12
|
+
|
|
13
|
+
display: inline-flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
gap: var(--xh-toggle-gap, var(--xh-control-gap-md));
|
|
17
|
+
block-size: var(--xh-toggle-h, var(--xh-control-h-md));
|
|
18
|
+
padding-inline: var(--xh-toggle-px, var(--xh-control-px-md));
|
|
19
|
+
border: var(--xh-stroke-thin) solid transparent;
|
|
20
|
+
border-radius: var(--xh-toggle-radius, var(--xh-shape-control));
|
|
21
|
+
background: var(--xh-_bg);
|
|
22
|
+
color: var(--xh-_fg);
|
|
23
|
+
font-size: var(--xh-toggle-font-size, var(--xh-text-label-size));
|
|
24
|
+
font-weight: var(--xh-toggle-font-weight, var(--xh-text-label-weight));
|
|
25
|
+
line-height: var(--xh-leading-none);
|
|
26
|
+
white-space: nowrap;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
user-select: none;
|
|
29
|
+
transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
[data-scope='toggle'][data-part='root']:hover {
|
|
33
|
+
background: var(--xh-_bg-hover);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[data-scope='toggle'][data-part='root'][data-disabled] {
|
|
37
|
+
cursor: not-allowed;
|
|
38
|
+
opacity: var(--xh-state-disabled-opacity);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[data-scope='toggle'][data-part='root']:focus-visible {
|
|
42
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
43
|
+
outline-offset: var(--xh-ring-offset);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* —— 变体 —— */
|
|
47
|
+
/* 形态只决定颜色怎么用;用哪族颜色由 data-tone 决定,没写 tone 就沿用原来的中性配色 */
|
|
48
|
+
[data-scope='toggle'][data-part='root'][data-variant='solid'] {
|
|
49
|
+
--xh-_bg: transparent;
|
|
50
|
+
--xh-_bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle-hover));
|
|
51
|
+
--xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
|
|
52
|
+
--xh-_bg-pressed: var(--xh-_tone, var(--xh-bg-brand));
|
|
53
|
+
--xh-_fg-pressed: var(--xh-_tone-on, var(--xh-fg-on-brand));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
[data-scope='toggle'][data-part='root'][data-variant='subtle'] {
|
|
57
|
+
--xh-_bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
|
|
58
|
+
--xh-_bg-hover: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
|
|
59
|
+
--xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
|
|
60
|
+
--xh-_bg-pressed: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
|
|
61
|
+
--xh-_fg-pressed: var(--xh-_tone-fg, var(--xh-fg-default));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
[data-scope='toggle'][data-part='root'][data-variant='outline'] {
|
|
65
|
+
--xh-_bg: transparent;
|
|
66
|
+
--xh-_bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle-hover));
|
|
67
|
+
--xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
|
|
68
|
+
--xh-_bg-pressed: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
|
|
69
|
+
--xh-_fg-pressed: var(--xh-_tone-fg, var(--xh-fg-default));
|
|
70
|
+
|
|
71
|
+
border-color: var(--xh-_tone-border, var(--xh-border-default));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
[data-scope='toggle'][data-part='root'][data-variant='ghost'] {
|
|
75
|
+
--xh-_bg: transparent;
|
|
76
|
+
--xh-_bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle-hover));
|
|
77
|
+
--xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
|
|
78
|
+
--xh-_bg-pressed: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
|
|
79
|
+
--xh-_fg-pressed: var(--xh-_tone-fg, var(--xh-fg-default));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* 按下态排在变体之后:两者特异度相同,后写的那条赢,
|
|
83
|
+
变体只负责给出按下档的取值,切换由这里一处完成 */
|
|
84
|
+
[data-scope='toggle'][data-part='root'][data-state='on'] {
|
|
85
|
+
--xh-_bg: var(--xh-_bg-pressed);
|
|
86
|
+
--xh-_bg-hover: var(--xh-_bg-pressed);
|
|
87
|
+
|
|
88
|
+
color: var(--xh-_fg-pressed);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* —— 尺寸 —— */
|
|
92
|
+
[data-scope='toggle'][data-part='root'][data-size='sm'] {
|
|
93
|
+
block-size: var(--xh-control-h-sm);
|
|
94
|
+
padding-inline: var(--xh-control-px-sm);
|
|
95
|
+
font-size: var(--xh-font-size-sm);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[data-scope='toggle'][data-part='root'][data-size='lg'] {
|
|
99
|
+
block-size: var(--xh-control-h-lg);
|
|
100
|
+
padding-inline: var(--xh-control-px-lg);
|
|
101
|
+
font-size: var(--xh-font-size-lg);
|
|
102
|
+
}
|
|
103
|
+
}
|
package/css/tone.css
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 语气层:与组件无关,全库共用一份。
|
|
3
|
+
*
|
|
4
|
+
* 三个视觉轴各管一件事,互不干涉:
|
|
5
|
+
* data-variant 形态 solid / subtle / outline / ghost —— 颜色怎么用
|
|
6
|
+
* data-tone 语气 brand / neutral / success / warning / danger / info —— 用哪族颜色
|
|
7
|
+
* data-size 尺寸 sm / md / lg
|
|
8
|
+
*
|
|
9
|
+
* 这里只声明私有槽,各组件的形态规则去消费。所以加一个语气是改这一处 6 条规则,
|
|
10
|
+
* 而不是逐个组件写 6 遍。
|
|
11
|
+
*
|
|
12
|
+
* --xh-_tone-on 是实心底上的前景色,按实测对比度分派而非统一白字:
|
|
13
|
+
* 600 档上白字对 brand 5.08、neutral 7.80、danger 4.83 达标,而 success 3.04、
|
|
14
|
+
* warning 2.70、info 3.47 都不到 4.5,这三族必须配深字。
|
|
15
|
+
*
|
|
16
|
+
* --xh-_tone-shift 是交互态要往哪个方向挪:永远背离前景色走,悬停与按下才不会
|
|
17
|
+
* 反过来压低对比度。白字族往深处挪,深字族往浅处挪。
|
|
18
|
+
*
|
|
19
|
+
* 这两个槽用的都是固定原语而不是 --xh-fg-default:实心底本身是原语色、不随主题翻,
|
|
20
|
+
* 配色若跟着主题翻,暗色下就会变成白字压在浅黄底上(实测对比度 2.70)。
|
|
21
|
+
*/
|
|
22
|
+
@layer xihan.components {
|
|
23
|
+
[data-tone] {
|
|
24
|
+
/* 先给一份中性缺省:写了 data-tone 却拼错取值时退成中性,
|
|
25
|
+
而不是让 --xh-_tone 悬空——悬空会让下面每条 color-mix 变成计算期无效,
|
|
26
|
+
用它的声明会整条失效,且 var() 兜底也救不回来(槽位是「已声明但无效」)。 */
|
|
27
|
+
--xh-_tone: var(--xh-color-neutral-600);
|
|
28
|
+
/* 装饰性强调(色条、图标这类)用 500 档:600 是给实心底与文字用的强调档,
|
|
29
|
+
压在细长的色条上偏重。alert 与 toast 原本就用 500,接语气层不该改变它们的观感 */
|
|
30
|
+
--xh-_tone-soft: var(--xh-color-neutral-500);
|
|
31
|
+
--xh-_tone-on: var(--xh-color-neutral-0);
|
|
32
|
+
--xh-_tone-shift: var(--xh-color-neutral-950);
|
|
33
|
+
|
|
34
|
+
/* 交互态与淡底都从主色兑出来,兑的对象是主题色,深浅两态自动跟随 */
|
|
35
|
+
--xh-_tone-hover: color-mix(in oklab, var(--xh-_tone) 88%, var(--xh-_tone-shift));
|
|
36
|
+
--xh-_tone-active: color-mix(in oklab, var(--xh-_tone) 78%, var(--xh-_tone-shift));
|
|
37
|
+
--xh-_tone-subtle: color-mix(in oklab, var(--xh-_tone) 12%, var(--xh-bg-surface));
|
|
38
|
+
--xh-_tone-subtle-hover: color-mix(in oklab, var(--xh-_tone) 20%, var(--xh-bg-surface));
|
|
39
|
+
--xh-_tone-subtle-active: color-mix(in oklab, var(--xh-_tone) 28%, var(--xh-bg-surface));
|
|
40
|
+
--xh-_tone-border: color-mix(in oklab, var(--xh-_tone) 40%, var(--xh-bg-surface));
|
|
41
|
+
/* 在普通背景上表达该语气的文字色:往前景色兑,浅色态变深、深色态变浅 */
|
|
42
|
+
--xh-_tone-fg: color-mix(in oklab, var(--xh-_tone) 72%, var(--xh-fg-default));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
[data-tone='brand'] {
|
|
46
|
+
--xh-_tone: var(--xh-color-brand-600);
|
|
47
|
+
--xh-_tone-soft: var(--xh-color-brand-500);
|
|
48
|
+
--xh-_tone-on: var(--xh-color-neutral-0);
|
|
49
|
+
--xh-_tone-shift: var(--xh-color-neutral-950);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
[data-tone='neutral'] {
|
|
53
|
+
--xh-_tone: var(--xh-color-neutral-600);
|
|
54
|
+
--xh-_tone-on: var(--xh-color-neutral-0);
|
|
55
|
+
--xh-_tone-shift: var(--xh-color-neutral-950);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
[data-tone='danger'] {
|
|
59
|
+
--xh-_tone: var(--xh-color-danger-600);
|
|
60
|
+
--xh-_tone-soft: var(--xh-color-danger-500);
|
|
61
|
+
--xh-_tone-on: var(--xh-color-neutral-0);
|
|
62
|
+
--xh-_tone-shift: var(--xh-color-neutral-950);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
[data-tone='success'] {
|
|
66
|
+
--xh-_tone: var(--xh-color-success-600);
|
|
67
|
+
--xh-_tone-soft: var(--xh-color-success-500);
|
|
68
|
+
--xh-_tone-on: var(--xh-color-neutral-950);
|
|
69
|
+
--xh-_tone-shift: var(--xh-color-neutral-0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
[data-tone='warning'] {
|
|
73
|
+
--xh-_tone: var(--xh-color-warning-600);
|
|
74
|
+
--xh-_tone-soft: var(--xh-color-warning-500);
|
|
75
|
+
--xh-_tone-on: var(--xh-color-neutral-950);
|
|
76
|
+
--xh-_tone-shift: var(--xh-color-neutral-0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
[data-tone='info'] {
|
|
80
|
+
--xh-_tone: var(--xh-color-info-600);
|
|
81
|
+
--xh-_tone-soft: var(--xh-color-info-500);
|
|
82
|
+
--xh-_tone-on: var(--xh-color-neutral-950);
|
|
83
|
+
--xh-_tone-shift: var(--xh-color-neutral-0);
|
|
84
|
+
}
|
|
85
|
+
}
|
package/css/toolbar.css
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
@layer xihan.components {
|
|
2
|
+
/* 一条控件带:条目之间留固定间距,分隔线再把它们切成语义上的几段。
|
|
3
|
+
排布只能读 data-orientation——它与 aria-orientation 同源,但样式不该去读 ARIA */
|
|
4
|
+
[data-scope='toolbar'][data-part='root'] {
|
|
5
|
+
/* 尺寸档只换这两个私有槽:条目之间的间距,与整条的内边距 */
|
|
6
|
+
--xh-_toolbar-gap: var(--xh-space-1);
|
|
7
|
+
--xh-_toolbar-p: var(--xh-space-1);
|
|
8
|
+
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: var(--xh-toolbar-gap, var(--xh-_toolbar-gap));
|
|
12
|
+
padding-block: var(--xh-toolbar-py, var(--xh-_toolbar-p));
|
|
13
|
+
padding-inline: var(--xh-toolbar-px, var(--xh-_toolbar-p));
|
|
14
|
+
border: var(--xh-stroke-thin) solid var(--xh-toolbar-border, var(--xh-border-default));
|
|
15
|
+
border-radius: var(--xh-toolbar-radius, var(--xh-shape-surface));
|
|
16
|
+
background: var(--xh-toolbar-bg, var(--xh-bg-surface));
|
|
17
|
+
color: var(--xh-toolbar-fg, var(--xh-fg-default));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
[data-scope='toolbar'][data-part='root'][data-orientation='horizontal'] {
|
|
21
|
+
flex-direction: row;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* 竖排时交叉轴改为拉伸:竖着的一条里每个条目该一样宽,居中会让它们参差不齐 */
|
|
25
|
+
[data-scope='toolbar'][data-part='root'][data-orientation='vertical'] {
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
align-items: stretch;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* —— 尺寸 —— */
|
|
31
|
+
/* 缺省档写在上面的基础规则里。工具条自己不定条目的高度与字号(那归条目的皮肤),
|
|
32
|
+
这里只换排布尺寸;缺省的 --xh-space-1 已经是控件间距那把尺的小档,容不下三档,故一律取空间尺 */
|
|
33
|
+
[data-scope='toolbar'][data-part='root'][data-size='sm'] {
|
|
34
|
+
--xh-_toolbar-gap: var(--xh-space-0_5);
|
|
35
|
+
--xh-_toolbar-p: var(--xh-space-0_5);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
[data-scope='toolbar'][data-part='root'][data-size='lg'] {
|
|
39
|
+
--xh-_toolbar-gap: var(--xh-space-2);
|
|
40
|
+
--xh-_toolbar-p: var(--xh-space-2);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* root 会在焦点进条前短暂拿到 tabindex=0,但它的 onFocus 立刻把焦点转投给条目,
|
|
44
|
+
在它身上画环只会闪一下就跳走,高亮要长在条目上 */
|
|
45
|
+
[data-scope='toolbar'][data-part='root']:focus-visible {
|
|
46
|
+
outline: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[data-scope='toolbar'][data-part='root'][data-disabled] {
|
|
50
|
+
background: var(--xh-toolbar-bg-disabled, var(--xh-bg-muted));
|
|
51
|
+
color: var(--xh-fg-disabled);
|
|
52
|
+
cursor: not-allowed;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* 给了 display 的角色节点必须补这条:display 声明会盖掉 UA 的 [hidden]{display:none},
|
|
56
|
+
不补的话作者给节点加 hidden 后它照样显示 */
|
|
57
|
+
[data-scope='toolbar'][data-part='root'][hidden] {
|
|
58
|
+
display: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* 分组把一串相关控件收得更紧:组内间距比条上的总间距小,
|
|
62
|
+
"这几个是一伙的"才看得出来 */
|
|
63
|
+
[data-scope='toolbar'][data-part='group'] {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: var(--xh-toolbar-group-gap, var(--xh-space-0_5));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
[data-scope='toolbar'][data-part='group'][data-orientation='vertical'] {
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
align-items: stretch;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
[data-scope='toolbar'][data-part='group'][hidden] {
|
|
75
|
+
display: none;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* 条目是作者自己的控件(按钮、切换钮、下拉触发器),观感归它自己的皮肤。
|
|
79
|
+
这里只补两件与导航直接相关的事:焦点环与禁用光标。
|
|
80
|
+
刻意不给 display——给了会把条目自身皮肤里的排布盖掉,也就不必补 [hidden] 那条 */
|
|
81
|
+
[data-scope='toolbar'][data-part='item']:focus-visible {
|
|
82
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
83
|
+
outline-offset: var(--xh-ring-offset);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* 集合条目一律 aria-disabled 而非原生 disabled(禁用项仍要能当方向键的起点),
|
|
87
|
+
所以这里读 data-disabled,写 :disabled 永远不命中 */
|
|
88
|
+
[data-scope='toolbar'][data-part='item'][data-disabled] {
|
|
89
|
+
color: var(--xh-fg-disabled);
|
|
90
|
+
cursor: not-allowed;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* 分隔线画的是它自己的朝向:横排工具条里它是竖线,连接层给的就是 vertical。
|
|
94
|
+
粗的那一维写死描边宽度,长的那一维交给 align-self 拉伸,
|
|
95
|
+
沿长边再内缩一点,线才不会顶到工具条的上下边 */
|
|
96
|
+
[data-scope='toolbar'][data-part='separator'] {
|
|
97
|
+
flex: none;
|
|
98
|
+
align-self: stretch;
|
|
99
|
+
border-radius: var(--xh-toolbar-separator-radius, var(--xh-radius-full));
|
|
100
|
+
background: var(--xh-toolbar-separator-color, var(--xh-border-default));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[data-scope='toolbar'][data-part='separator'][data-orientation='vertical'] {
|
|
104
|
+
inline-size: var(--xh-toolbar-separator-thickness, var(--xh-stroke-thin));
|
|
105
|
+
margin-block: var(--xh-toolbar-separator-inset, var(--xh-space-1));
|
|
106
|
+
margin-inline: var(--xh-toolbar-separator-gap, var(--xh-space-1));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
[data-scope='toolbar'][data-part='separator'][data-orientation='horizontal'] {
|
|
110
|
+
block-size: var(--xh-toolbar-separator-thickness, var(--xh-stroke-thin));
|
|
111
|
+
margin-block: var(--xh-toolbar-separator-gap, var(--xh-space-1));
|
|
112
|
+
margin-inline: var(--xh-toolbar-separator-inset, var(--xh-space-1));
|
|
113
|
+
}
|
|
114
|
+
}
|
package/css/tooltip.css
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
@layer xihan.components {
|
|
2
|
+
[data-scope='tooltip'][data-part='trigger'] {
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
justify-content: center;
|
|
6
|
+
gap: var(--xh-tooltip-trigger-gap, var(--xh-control-gap-sm));
|
|
7
|
+
border: 0;
|
|
8
|
+
background: transparent;
|
|
9
|
+
color: inherit;
|
|
10
|
+
font: inherit;
|
|
11
|
+
cursor: inherit;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
[data-scope='tooltip'][data-part='trigger']:focus-visible {
|
|
15
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
16
|
+
outline-offset: var(--xh-ring-offset);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* 坐标由定位引擎写在行内 style 上,这里只兜底 SSR 首帧 */
|
|
20
|
+
[data-scope='tooltip'][data-part='positioner'] {
|
|
21
|
+
position: fixed;
|
|
22
|
+
inset-block-start: 0;
|
|
23
|
+
inset-inline-start: 0;
|
|
24
|
+
z-index: var(--xh-layer-tooltip);
|
|
25
|
+
/* 浮层不承载交互:指针必须能穿过去点到底下的东西 */
|
|
26
|
+
pointer-events: none;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* 锚点滚出可视区时连浮层一起隐藏 */
|
|
30
|
+
[data-scope='tooltip'][data-part='positioner'][data-hidden] {
|
|
31
|
+
visibility: hidden;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
[data-scope='tooltip'][data-part='content'] {
|
|
35
|
+
/* 尺寸档只换这三个私有槽。tooltip 没有 root 部件,两轴都落在浮层树最外层的 content 上,
|
|
36
|
+
箭头是它的后代,语气槽一并继承 */
|
|
37
|
+
--xh-_tooltip-py: var(--xh-space-1);
|
|
38
|
+
--xh-_tooltip-px: var(--xh-space-2);
|
|
39
|
+
--xh-_tooltip-font-size: var(--xh-font-size-sm);
|
|
40
|
+
|
|
41
|
+
/* 与 positioner 相反:浮层本体要能接指针,指针停在提示上就不该收起
|
|
42
|
+
(长文案需要停留细看)。positioner 仍是 none,空白区域照样点得穿。 */
|
|
43
|
+
pointer-events: auto;
|
|
44
|
+
max-inline-size: var(--xh-tooltip-max-w, var(--xh-overlay-max-w));
|
|
45
|
+
padding-block: var(--xh-tooltip-py, var(--xh-_tooltip-py));
|
|
46
|
+
padding-inline: var(--xh-tooltip-px, var(--xh-_tooltip-px));
|
|
47
|
+
border-radius: var(--xh-tooltip-radius, var(--xh-shape-control));
|
|
48
|
+
/* 实心底与其上的文字色随语气走。没写 data-tone 时两个槽都没被声明,退回原来的取值 */
|
|
49
|
+
background: var(--xh-tooltip-bg, var(--xh-_tone, var(--xh-fg-default)));
|
|
50
|
+
color: var(--xh-tooltip-fg, var(--xh-_tone-on, var(--xh-bg-surface)));
|
|
51
|
+
font-size: var(--xh-tooltip-font-size, var(--xh-_tooltip-font-size));
|
|
52
|
+
line-height: var(--xh-leading-normal);
|
|
53
|
+
box-shadow: var(--xh-tooltip-shadow, var(--xh-elevation-2));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
[data-scope='tooltip'][data-part='content'][data-state='open'] {
|
|
57
|
+
animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* —— 尺寸 —— */
|
|
61
|
+
/* 缺省档写在 content 的基础规则里。提示的内边距与字号本就不在 control-* 那把尺上
|
|
62
|
+
(它是浮层不是控件),故按 space / font-size 档上下各挪一级 */
|
|
63
|
+
[data-scope='tooltip'][data-part='content'][data-size='sm'] {
|
|
64
|
+
--xh-_tooltip-py: var(--xh-space-0_5);
|
|
65
|
+
--xh-_tooltip-px: var(--xh-space-1_5);
|
|
66
|
+
--xh-_tooltip-font-size: var(--xh-font-size-xs);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
[data-scope='tooltip'][data-part='content'][data-size='lg'] {
|
|
70
|
+
--xh-_tooltip-py: var(--xh-space-1_5);
|
|
71
|
+
--xh-_tooltip-px: var(--xh-space-2_5);
|
|
72
|
+
--xh-_tooltip-font-size: var(--xh-font-size-md);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* 箭头是旋转 45° 的方块,按落定朝向贴在 content 对侧边的中点 */
|
|
76
|
+
[data-scope='tooltip'][data-part='arrow'] {
|
|
77
|
+
position: absolute;
|
|
78
|
+
inline-size: var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size));
|
|
79
|
+
block-size: var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size));
|
|
80
|
+
background: var(--xh-tooltip-bg, var(--xh-_tone, var(--xh-fg-default)));
|
|
81
|
+
transform: rotate(45deg);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
[data-scope='tooltip'][data-part='positioner'][data-placement^='bottom'] [data-scope='tooltip'][data-part='arrow'] {
|
|
85
|
+
inset-block-start: calc(var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / -2);
|
|
86
|
+
inset-inline-start: calc(50% - var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / 2);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
[data-scope='tooltip'][data-part='positioner'][data-placement^='top'] [data-scope='tooltip'][data-part='arrow'] {
|
|
90
|
+
inset-block-end: calc(var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / -2);
|
|
91
|
+
inset-inline-start: calc(50% - var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / 2);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
[data-scope='tooltip'][data-part='positioner'][data-placement^='left'] [data-scope='tooltip'][data-part='arrow'] {
|
|
95
|
+
inset-block-start: calc(50% - var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / 2);
|
|
96
|
+
inset-inline-end: calc(var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / -2);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
[data-scope='tooltip'][data-part='positioner'][data-placement^='right'] [data-scope='tooltip'][data-part='arrow'] {
|
|
100
|
+
inset-block-start: calc(50% - var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / 2);
|
|
101
|
+
inset-inline-start: calc(var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / -2);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@media (prefers-reduced-motion: reduce) {
|
|
105
|
+
[data-scope='tooltip'][data-part='content'] {
|
|
106
|
+
animation-duration: 0.01ms !important;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|