@manohub/ui 0.10.4 → 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.
- package/README.md +155 -155
- package/dist/components/form/index.js +8 -1
- package/dist/components/tabs/index.d.ts +9 -6
- package/dist/components/tabs/index.js +6 -1
- package/dist/styles/components/avatar.css +131 -131
- package/dist/styles/components/avatar.tokens.css +24 -24
- package/dist/styles/components/badge.css +266 -266
- package/dist/styles/components/badge.tokens.css +24 -24
- package/dist/styles/components/button.css +460 -460
- package/dist/styles/components/capsule.css +128 -128
- package/dist/styles/components/capsule.tokens.css +27 -27
- package/dist/styles/components/card.css +198 -198
- package/dist/styles/components/checkbox.css +145 -145
- package/dist/styles/components/checkbox.tokens.css +26 -26
- package/dist/styles/components/code-block.css +216 -216
- package/dist/styles/components/code-block.tokens.css +40 -40
- package/dist/styles/components/collapse.css +62 -62
- package/dist/styles/components/collapse.tokens.css +20 -20
- package/dist/styles/components/dialog.css +158 -158
- package/dist/styles/components/dialog.tokens.css +24 -24
- package/dist/styles/components/divider.css +75 -75
- package/dist/styles/components/divider.tokens.css +22 -22
- package/dist/styles/components/drawer.css +143 -143
- package/dist/styles/components/drawer.tokens.css +22 -22
- package/dist/styles/components/filter.css +29 -29
- package/dist/styles/components/form.css +442 -430
- package/dist/styles/components/form.tokens.css +15 -15
- package/dist/styles/components/input.css +538 -533
- package/dist/styles/components/input.tokens.css +43 -43
- package/dist/styles/components/layout.css +200 -200
- package/dist/styles/components/list-view.css +244 -244
- package/dist/styles/components/loading.css +76 -76
- package/dist/styles/components/loading.tokens.css +23 -23
- package/dist/styles/components/nav.css +48 -48
- package/dist/styles/components/notice.css +153 -153
- package/dist/styles/components/notice.tokens.css +17 -17
- package/dist/styles/components/notification.css +159 -159
- package/dist/styles/components/notification.tokens.css +37 -37
- package/dist/styles/components/page.css +473 -473
- package/dist/styles/components/pagination.css +303 -303
- package/dist/styles/components/pagination.tokens.css +32 -32
- package/dist/styles/components/panel.css +77 -77
- package/dist/styles/components/query-state.css +88 -88
- package/dist/styles/components/radio.css +271 -271
- package/dist/styles/components/radio.tokens.css +26 -26
- package/dist/styles/components/search.css +34 -34
- package/dist/styles/components/select.css +326 -326
- package/dist/styles/components/select.tokens.css +44 -44
- package/dist/styles/components/skeleton.css +60 -60
- package/dist/styles/components/skeleton.tokens.css +27 -27
- package/dist/styles/components/steps.css +205 -205
- package/dist/styles/components/steps.tokens.css +44 -44
- package/dist/styles/components/switch.css +165 -165
- package/dist/styles/components/switch.tokens.css +26 -26
- package/dist/styles/components/tabbar.css +235 -235
- package/dist/styles/components/tabbar.tokens.css +30 -30
- package/dist/styles/components/table.css +504 -504
- package/dist/styles/components/table.tokens.css +44 -44
- package/dist/styles/components/tag.css +127 -127
- package/dist/styles/components/tag.tokens.css +24 -24
- package/dist/styles/components/toast.css +185 -185
- package/dist/styles/components/toast.tokens.css +26 -26
- package/dist/styles/components/tooltip.css +89 -89
- package/dist/styles/components/tooltip.tokens.css +25 -25
- package/dist/styles/components/tree.css +173 -173
- package/dist/styles/components/upload.css +151 -151
- package/dist/styles/components/upload.tokens.css +27 -27
- package/dist/styles/index.css +153 -153
- package/dist/styles/operations/collapse.css +84 -84
- package/dist/styles/operations/popup.css +193 -193
- package/dist/styles/operations/tabs.css +82 -73
- package/package.json +62 -62
|
@@ -1,460 +1,460 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Button —— 自绘按钮族(六型 + 三档真实尺寸 + 外形 / 危险色 / 整宽修饰 + 加载 + 图标/文本/菜单形态 + 组容器 + 菜单皮肤)。
|
|
3
|
-
*
|
|
4
|
-
* 出处(几何以设计稿《MH后台-0911》品牌页为准,规范同值处不再复述):
|
|
5
|
-
* - 控件高 **32** / 内距 **12** / 图文距 **4** / 圆角 **6** / 字号 **14/18 w400** / 组间距 8;
|
|
6
|
-
* - 主要 = 主色实底 **无投影**(`0:3897`);次要 = 白底 + `#e6e6e6` 描边 + **中性字色**
|
|
7
|
-
* (`0:3892`,文本 `#1a1a1a`);禁用 = `#f2f2f2` 底 + `#888888` 字(`次要按钮备份`);
|
|
8
|
-
* - 小号 24、大号 40(设计未覆盖,沿用);图标槽 = 全局图标档(见 `--ui-button-icon-size`);加载环 14 · 0.8s linear;
|
|
9
|
-
* 虚线 = 区域内添加,文本 = 行内操作,图标 = 方形热区;菜单按钮 = 文本区 ‖ 分割线 ‖ 箭头区。
|
|
10
|
-
*
|
|
11
|
-
* **色彩槽位走局部变量**(`--ui-button-accent*`):六型只描述「底 / 边 / 字的取用位置」,
|
|
12
|
-
* 具体颜色由槽位变量给。危险色只是把槽位换成错误色,故 `danger` 与任意型**天然正交**,
|
|
13
|
-
* hover / active / 加载态一并跟随,无需为每个型再写一遍危险态(这是本文件的核心结构)。
|
|
14
|
-
*
|
|
15
|
-
* **高度三档全部吃主题令牌**(`--ui-control-height-sm` / `-md` / `-lg`,default 24 / 32 / 40)——
|
|
16
|
-
* 换主题即换控件尺寸,这正是 theme 层的职责。md 与 lg **不同高**:同高会让 `size`
|
|
17
|
-
* 沦为无视觉差别的语义标签,调用方无从选择。
|
|
18
|
-
*
|
|
19
|
-
* **类名一律 `mh-` 前缀**:本包独立发布,不迁就任何下游的既有类名;同一词根在本包内只有一套类(见 AGENTS)。
|
|
20
|
-
* 自绘件服务「不装 farris」的场景:静态页 / 其他框架 / 微前端里不带 farris 的子应用。
|
|
21
|
-
*
|
|
22
|
-
* **投影**:主要与次要两型**不再带投影**(设计稿的按钮是平的,节点上无 effects)。
|
|
23
|
-
* 图标型的 hover 光晕仍引用 `--ui-shadow-card-hover` —— 那是 hover 反馈,不是静止装饰,
|
|
24
|
-
* 且投影一律走令牌、不新造字面量(与 shadows.css「拟物重投影一律收敛到令牌」的口径一致)。
|
|
25
|
-
*
|
|
26
|
-
* 边界(有意不做):规范的 `.btn-seg`(分段按钮,原生同名 radio 驱动)**不在这里实现** ——
|
|
27
|
-
* 它的用途(一组互斥选项的轻量选择器)由 `capsule` 族承担,重复实现会出现两套分段控件。
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
/* 几何只挂在按钮本体上:令牌在同名 `.tokens.css`(锚 `[data-manohub-ui]`),几何若跟着挂会把容器也画成按钮 */
|
|
31
|
-
.mh-button {
|
|
32
|
-
display: inline-flex;
|
|
33
|
-
align-items: center;
|
|
34
|
-
justify-content: center;
|
|
35
|
-
gap: var(--ui-button-gap);
|
|
36
|
-
height: var(--ui-button-height);
|
|
37
|
-
padding: 0 var(--ui-button-padding-x);
|
|
38
|
-
border: none;
|
|
39
|
-
border-radius: var(--ui-button-radius);
|
|
40
|
-
font-family: inherit;
|
|
41
|
-
font-size: var(--ui-button-font-size);
|
|
42
|
-
line-height: var(--ui-line-body);
|
|
43
|
-
/* 链接形态时本体就是 `<a>`(见组件层),基线不再代管 `a { text-decoration: inherit }`,这里自己摘掉下划线 */
|
|
44
|
-
text-decoration: none;
|
|
45
|
-
cursor: pointer;
|
|
46
|
-
white-space: nowrap;
|
|
47
|
-
/* 三态补间(底 / 边 / 字 / 影) */
|
|
48
|
-
transition-property: background-color, border-color, color, box-shadow;
|
|
49
|
-
transition-duration: 0.15s;
|
|
50
|
-
transition-timing-function: ease;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/* 危险色修饰:换槽位即可,六型与各自 hover / active 一并跟随
|
|
54
|
-
(`--ui-button-accent-soft` 随无消费者的孤令牌一并撤除,2026-09-30) */
|
|
55
|
-
.mh-button--danger {
|
|
56
|
-
--ui-button-accent: var(--ui-error);
|
|
57
|
-
--ui-button-accent-hover: color-mix(in srgb, var(--ui-error) 88%, black);
|
|
58
|
-
--ui-button-accent-content: var(--ui-error-content);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/* 键盘焦点环:画在按钮自身(与 radio / tag 同一句) */
|
|
62
|
-
.mh-button:focus-visible {
|
|
63
|
-
outline: 2px solid var(--ui-primary);
|
|
64
|
-
outline-offset: -2px;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.mh-button__icon {
|
|
68
|
-
flex: none;
|
|
69
|
-
width: var(--ui-button-icon-size);
|
|
70
|
-
height: var(--ui-button-icon-size);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/* ===== 尺寸档 ===== */
|
|
74
|
-
.mh-button--sm {
|
|
75
|
-
height: var(--ui-button-height-sm);
|
|
76
|
-
padding: 0 var(--ui-button-padding-x-sm);
|
|
77
|
-
font-size: var(--ui-button-font-size-sm);
|
|
78
|
-
}
|
|
79
|
-
.mh-button--sm .mh-button__icon,
|
|
80
|
-
.mh-button--sm .mh-button__caret {
|
|
81
|
-
width: var(--ui-button-icon-size-sm);
|
|
82
|
-
height: var(--ui-button-icon-size-sm);
|
|
83
|
-
}
|
|
84
|
-
.mh-button--lg {
|
|
85
|
-
height: var(--ui-button-height-lg);
|
|
86
|
-
padding: 0 var(--ui-button-padding-x-lg);
|
|
87
|
-
font-size: var(--ui-button-font-size-lg);
|
|
88
|
-
}
|
|
89
|
-
.mh-button--lg .mh-button__icon,
|
|
90
|
-
.mh-button--lg .mh-button__caret {
|
|
91
|
-
width: var(--ui-button-icon-size-lg);
|
|
92
|
-
height: var(--ui-button-icon-size-lg);
|
|
93
|
-
}
|
|
94
|
-
/* 尺寸档的菜单结构:文字区内距与分割线高度必须跟着档位走 */
|
|
95
|
-
.mh-button--sm.mh-button--menu .mh-button__label {
|
|
96
|
-
padding: 0 var(--ui-button-padding-x-sm);
|
|
97
|
-
}
|
|
98
|
-
.mh-button--lg.mh-button--menu .mh-button__label {
|
|
99
|
-
padding: 0 var(--ui-button-padding-x-lg);
|
|
100
|
-
}
|
|
101
|
-
.mh-button--sm .mh-button__divider {
|
|
102
|
-
height: var(--ui-button-height-sm);
|
|
103
|
-
}
|
|
104
|
-
.mh-button--lg .mh-button__divider {
|
|
105
|
-
height: var(--ui-button-height-lg);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/* ===== 外形修饰(与型正交) ===== */
|
|
109
|
-
.mh-button--round {
|
|
110
|
-
border-radius: var(--ui-radius-full);
|
|
111
|
-
}
|
|
112
|
-
.mh-button--circle {
|
|
113
|
-
width: var(--ui-button-height);
|
|
114
|
-
height: var(--ui-button-height);
|
|
115
|
-
padding: 0;
|
|
116
|
-
border-radius: var(--ui-radius-full);
|
|
117
|
-
}
|
|
118
|
-
.mh-button--sm.mh-button--circle {
|
|
119
|
-
width: var(--ui-button-height-sm);
|
|
120
|
-
height: var(--ui-button-height-sm);
|
|
121
|
-
}
|
|
122
|
-
.mh-button--lg.mh-button--circle {
|
|
123
|
-
width: var(--ui-button-height-lg);
|
|
124
|
-
height: var(--ui-button-height-lg);
|
|
125
|
-
}
|
|
126
|
-
/* 整宽:表单底栏 / 移动端主操作;用 flex 而非 width 单给,保证菜单结构的内部分布不变 */
|
|
127
|
-
.mh-button--block {
|
|
128
|
-
display: flex;
|
|
129
|
-
width: 100%;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/* ===== 主要:主色实底(**无投影**) =====
|
|
133
|
-
设计实证:`0:3897` 是纯 `#2660ff` 实底 + r6,节点上没有任何 effects。
|
|
134
|
-
改前挂的 `--ui-shadow-card-hover` 会让按钮在设计里「浮起来」,属多余装饰。 */
|
|
135
|
-
.mh-button--primary {
|
|
136
|
-
background: var(--ui-button-accent);
|
|
137
|
-
color: var(--ui-button-accent-content);
|
|
138
|
-
}
|
|
139
|
-
.mh-button--primary:hover,
|
|
140
|
-
.mh-button--primary.is-hover {
|
|
141
|
-
background: var(--ui-button-accent-hover);
|
|
142
|
-
}
|
|
143
|
-
.mh-button--primary:active,
|
|
144
|
-
.mh-button--primary.is-active {
|
|
145
|
-
background: color-mix(in srgb, var(--ui-button-accent) 88%, black); /* 规范按下档比滑过更沉 */
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/* ===== 次要:白底描边 + **中性字色**(无投影) =====
|
|
149
|
-
设计实证:`0:3892` = 白底 + `#e6e6e6` 描边 + r6 + 文本 `#1a1a1a`(**中性**,不是主色蓝)。
|
|
150
|
-
改前用 `--ui-button-accent`(主色蓝)+ `--ui-shadow-card`,页头上会出现「蓝字带投影」的次要按钮,
|
|
151
|
-
与设计的「黑字平贴」观感不同。白底按钮在浅色页面上本就靠描边成形,**不需要投影**。 */
|
|
152
|
-
.mh-button--secondary {
|
|
153
|
-
background: var(--ui-base-100);
|
|
154
|
-
border: 1px solid var(--ui-base-300);
|
|
155
|
-
color: var(--ui-base-content);
|
|
156
|
-
}
|
|
157
|
-
.mh-button--secondary:hover,
|
|
158
|
-
.mh-button--secondary.is-hover {
|
|
159
|
-
border-color: var(--ui-button-accent-hover);
|
|
160
|
-
color: var(--ui-button-accent-hover);
|
|
161
|
-
}
|
|
162
|
-
.mh-button--secondary:active,
|
|
163
|
-
.mh-button--secondary.is-active {
|
|
164
|
-
border-color: var(--ui-button-accent);
|
|
165
|
-
color: var(--ui-button-accent);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/* ===== 虚线:区域内添加内容 =====
|
|
169
|
-
* 虚线取 `--ui-line-dashed`(= `base-400`,比实线深一档):同样粗细下虚线的墨迹只有实线的一半,
|
|
170
|
-
* 取实线色(`base-300`)会明显偏浅、整条框看着发虚。
|
|
171
|
-
* 这条口径 2026-09-30 一天内翻过一次:早些时候按「线框色只留一套」把虚线也收敛到 `base-300`,
|
|
172
|
-
* 当日晚按实机观感反转 —— 定稿见 `@manohub/theme` 的 `default/colors.css` 头「三条口径」第 2 条。 */
|
|
173
|
-
.mh-button--dashed {
|
|
174
|
-
background: var(--ui-base-100);
|
|
175
|
-
border: 1px dashed var(--ui-line-dashed);
|
|
176
|
-
color: var(--ui-button-accent);
|
|
177
|
-
}
|
|
178
|
-
.mh-button--dashed:hover,
|
|
179
|
-
.mh-button--dashed.is-hover {
|
|
180
|
-
border-color: var(--ui-button-accent-hover);
|
|
181
|
-
color: var(--ui-button-accent-hover);
|
|
182
|
-
}
|
|
183
|
-
.mh-button--dashed:active,
|
|
184
|
-
.mh-button--dashed.is-active {
|
|
185
|
-
border-color: var(--ui-button-accent);
|
|
186
|
-
color: var(--ui-button-accent);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/* ===== 文本按钮(**只用于行内 / 批量操作**:表格操作列、批量条、列表行内操作;
|
|
190
|
-
* 页头与区块的操作区一律用 primary + secondary —— 与规范 README 同口径) ===== */
|
|
191
|
-
.mh-button--text {
|
|
192
|
-
padding: 0 var(--ui-space-1);
|
|
193
|
-
background: transparent;
|
|
194
|
-
color: var(--ui-button-accent);
|
|
195
|
-
}
|
|
196
|
-
.mh-button--text:hover,
|
|
197
|
-
.mh-button--text.is-hover {
|
|
198
|
-
color: var(--ui-button-accent-hover);
|
|
199
|
-
}
|
|
200
|
-
.mh-button--text:active,
|
|
201
|
-
.mh-button--text.is-active {
|
|
202
|
-
color: color-mix(in srgb, var(--ui-button-accent) 80%, black);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/* ===== 链接按钮(跳转语义:带下划线,与「行内文本操作」的 text 档区分开) ===== */
|
|
206
|
-
.mh-button--link {
|
|
207
|
-
padding: 0;
|
|
208
|
-
background: transparent;
|
|
209
|
-
color: var(--ui-button-accent);
|
|
210
|
-
text-decoration: underline;
|
|
211
|
-
text-underline-offset: 3px;
|
|
212
|
-
}
|
|
213
|
-
.mh-button--link:hover,
|
|
214
|
-
.mh-button--link.is-hover {
|
|
215
|
-
color: var(--ui-button-accent-hover);
|
|
216
|
-
}
|
|
217
|
-
.mh-button--link:active,
|
|
218
|
-
.mh-button--link.is-active {
|
|
219
|
-
color: color-mix(in srgb, var(--ui-button-accent) 80%, black);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/* ===== 图标按钮(方形热区 + 16 字形;滑过/点击带主色光晕) ===== */
|
|
223
|
-
.mh-button--icon {
|
|
224
|
-
width: var(--ui-button-height);
|
|
225
|
-
height: var(--ui-button-height);
|
|
226
|
-
padding: 0; /* 显式清 UA 给 <button> 的内距,否则 border-box 下字形被挤扁 */
|
|
227
|
-
background: transparent;
|
|
228
|
-
color: var(--ui-base-content);
|
|
229
|
-
}
|
|
230
|
-
.mh-button--sm.mh-button--icon {
|
|
231
|
-
width: var(--ui-button-height-sm);
|
|
232
|
-
height: var(--ui-button-height-sm);
|
|
233
|
-
}
|
|
234
|
-
.mh-button--lg.mh-button--icon {
|
|
235
|
-
width: var(--ui-button-height-lg);
|
|
236
|
-
height: var(--ui-button-height-lg);
|
|
237
|
-
}
|
|
238
|
-
.mh-button--icon:hover,
|
|
239
|
-
.mh-button--icon.is-hover {
|
|
240
|
-
color: var(--ui-button-accent-hover);
|
|
241
|
-
box-shadow: var(--ui-shadow-card-hover);
|
|
242
|
-
}
|
|
243
|
-
.mh-button--icon:active,
|
|
244
|
-
.mh-button--icon.is-active {
|
|
245
|
-
color: var(--ui-button-accent);
|
|
246
|
-
}
|
|
247
|
-
/* 危险色 + 图标 / 文本 / 链接:这三型的字色不走 accent 槽(分别是中性 / 主色语义),单列一条 */
|
|
248
|
-
.mh-button--danger.mh-button--icon,
|
|
249
|
-
.mh-button--danger.mh-button--text,
|
|
250
|
-
.mh-button--danger.mh-button--link {
|
|
251
|
-
color: var(--ui-error);
|
|
252
|
-
}
|
|
253
|
-
.mh-button--danger.mh-button--icon:hover,
|
|
254
|
-
.mh-button--danger.mh-button--text:hover,
|
|
255
|
-
.mh-button--danger.mh-button--link:hover {
|
|
256
|
-
color: color-mix(in srgb, var(--ui-error) 88%, black);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/* ===== 禁用(各类通用;用专用令牌而非 opacity,避免禁用态被底色影响)
|
|
260
|
-
*
|
|
261
|
-
* 描边走 **inset 阴影而不是 `border`**:`border: 1px` 会把「无边框」的常态按钮撑宽 2px,
|
|
262
|
-
* 按钮一进禁用就在底栏里抖一下 —— inset 阴影画的还是同一圈 1px、位置也一致,但元素尺寸不变。
|
|
263
|
-
*
|
|
264
|
-
* **加载态排除在外**(`:not()`):`loading` 虽然也置原生 disabled(吞点击),但它**外观保持本型**
|
|
265
|
-
* —— 点击前后只有「文字 → 环」这一处变化,不换底 / 不换边 / 不换字。不排除的话,禁用档
|
|
266
|
-
* (2 个类)会压过 `.mh-button--loading`(1 个类),全屏最显眼的主操作会在点下去的一瞬间
|
|
267
|
-
* 整块变成禁用灰,观感突兀(浏览器实测过:底 `rgb(241,242,245)` + 灰字 + 灰边)。 */
|
|
268
|
-
.mh-button:disabled:not(.mh-button--loading),
|
|
269
|
-
.mh-button.is-disabled:not(.mh-button--loading),
|
|
270
|
-
.mh-button[aria-disabled='true']:not(.mh-button--loading) {
|
|
271
|
-
background: var(--ui-button-bg-disabled);
|
|
272
|
-
box-shadow: inset 0 0 0 1px var(--ui-button-border-disabled);
|
|
273
|
-
color: var(--ui-button-text-disabled);
|
|
274
|
-
cursor: not-allowed;
|
|
275
|
-
}
|
|
276
|
-
/* 文本 / 链接 / 图标三型没有底与边,禁用只换字色 */
|
|
277
|
-
.mh-button--text:disabled:not(.mh-button--loading),
|
|
278
|
-
.mh-button--text.is-disabled:not(.mh-button--loading),
|
|
279
|
-
.mh-button--text[aria-disabled='true']:not(.mh-button--loading),
|
|
280
|
-
.mh-button--link:disabled:not(.mh-button--loading),
|
|
281
|
-
.mh-button--link.is-disabled:not(.mh-button--loading),
|
|
282
|
-
.mh-button--link[aria-disabled='true']:not(.mh-button--loading),
|
|
283
|
-
.mh-button--icon:disabled:not(.mh-button--loading),
|
|
284
|
-
.mh-button--icon.is-disabled:not(.mh-button--loading),
|
|
285
|
-
.mh-button--icon[aria-disabled='true']:not(.mh-button--loading) {
|
|
286
|
-
background: transparent;
|
|
287
|
-
border: none;
|
|
288
|
-
color: var(--ui-button-text-disabled);
|
|
289
|
-
box-shadow: none;
|
|
290
|
-
text-decoration: none;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/* ===== 加载(**本型外观 + 主色降透明 + 环**) =====
|
|
294
|
-
*
|
|
295
|
-
* 加载态不改色彩角色:`primary` 还是主色、`danger` 还是错误色、`secondary` 还是白底黑字 ——
|
|
296
|
-
* 只是实底型的**主色底**按 `--ui-button-loading-mix` 与面板色显式合成一档更淡的同色。
|
|
297
|
-
* 忙碌时的按钮该是「同一个按钮淡了一点点、在转圈」,而不是换一副面孔(整块变灰或换档色都太突兀)。
|
|
298
|
-
*
|
|
299
|
-
* **为什么是显式合成而不是 `opacity`**:`opacity` 压的是整块,连文案与加载环一起淡 ——
|
|
300
|
-
* 忙碌时的文案恰恰要读得清;写成 `color-mix(主色 × mix, 面板色)` 只让**底**淡一档。
|
|
301
|
-
* (也别用覆盖层:主色实底上再盖一层同色半透明层等于什么都没做 —— 那是条死路。)
|
|
302
|
-
*
|
|
303
|
-
* 其余五型(`secondary` / `dashed` / `text` / `link` / `icon`)的「主色」在描边或字上,
|
|
304
|
-
* 底本来就是白的或透明的 —— 再降一档只会把边与字读淡,故它们加载时**外观完全不变**,只多一个环。
|
|
305
|
-
* 环取 **`currentColor`**:一条规则自动跟上六型与 `danger`(实底型白环、浅底与图标型本型字色环、
|
|
306
|
-
* 透明底型主色环),既不必逐型特判,也不额外引令牌。 */
|
|
307
|
-
.mh-button--loading {
|
|
308
|
-
cursor: not-allowed; /* 禁用档被排除了,`:disabled` 那条已不匹配它 */
|
|
309
|
-
}
|
|
310
|
-
/* 实底型的加载底:主色与面板色按令牌比例显式混出。
|
|
311
|
-
写在 hover / active 之后:同特异性(0.2.0)下后者胜出,故加载中滑过、按下都不会回到满主色 */
|
|
312
|
-
.mh-button--loading.mh-button--primary {
|
|
313
|
-
background: color-mix(
|
|
314
|
-
in srgb,
|
|
315
|
-
var(--ui-button-accent) var(--ui-button-loading-mix),
|
|
316
|
-
var(--ui-base-100)
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
.mh-button__spinner {
|
|
320
|
-
flex: none;
|
|
321
|
-
width: var(--ui-button-spinner-size);
|
|
322
|
-
height: var(--ui-button-spinner-size);
|
|
323
|
-
border: 1.5px solid currentColor; /* 跟随按钮的字色,六型通用 */
|
|
324
|
-
border-top-color: transparent;
|
|
325
|
-
border-radius: var(--ui-radius-full);
|
|
326
|
-
animation: mh-button-spin 0.8s linear infinite; /* 规范 0.8s linear */
|
|
327
|
-
}
|
|
328
|
-
@keyframes mh-button-spin {
|
|
329
|
-
to {
|
|
330
|
-
transform: rotate(360deg);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/* ===== 下拉箭头槽 =====
|
|
335
|
-
* **只给槽位尺寸,不给字形**:字形由 `@manohub/icon` 的 `<Icon name="chevron-down">` 画,
|
|
336
|
-
* 展开时的转向由操作层 `popup.css` 的 `.mh-popup-caret` 承担(本件不定义第二份 mask / 第二处转向)。
|
|
337
|
-
* 故组件把两个类同时挂在图标上:`class="mh-button__caret mh-popup-caret"`。 */
|
|
338
|
-
.mh-button__caret {
|
|
339
|
-
flex: none;
|
|
340
|
-
width: var(--ui-button-icon-size);
|
|
341
|
-
height: var(--ui-button-icon-size);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/* ===== 菜单按钮(文本区 ‖ 分割线 ‖ 箭头区;只管结构,色调随修饰符) ===== */
|
|
345
|
-
.mh-button--menu {
|
|
346
|
-
padding: 0;
|
|
347
|
-
gap: 0;
|
|
348
|
-
}
|
|
349
|
-
.mh-button__label {
|
|
350
|
-
padding: 0 var(--ui-button-padding-x);
|
|
351
|
-
}
|
|
352
|
-
.mh-button--menu .mh-button__caret {
|
|
353
|
-
margin: 0 var(--ui-button-gap);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/* 竖分割线(1px,跟按钮同高) */
|
|
357
|
-
.mh-button__divider {
|
|
358
|
-
flex: none;
|
|
359
|
-
width: 1px;
|
|
360
|
-
height: var(--ui-button-height);
|
|
361
|
-
background: var(--ui-base-300);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/* ===== 组容器(组件层结构,不是排版行):组内间距恒 8 ===== */
|
|
365
|
-
.mh-button__group {
|
|
366
|
-
display: inline-flex;
|
|
367
|
-
align-items: center;
|
|
368
|
-
gap: var(--ui-button-group-gap);
|
|
369
|
-
}
|
|
370
|
-
/* 组内文本按钮把水平内距交还容器 —— 间距只由 gap 单一拥有(否则 4+8+4=16) */
|
|
371
|
-
.mh-button__group > .mh-button--text {
|
|
372
|
-
padding-inline: 0;
|
|
373
|
-
}
|
|
374
|
-
/* 连体档:相邻按钮边重合、中间圆角归零(工具条形态;首末保留圆角) */
|
|
375
|
-
.mh-button__group--compact {
|
|
376
|
-
gap: 0;
|
|
377
|
-
}
|
|
378
|
-
.mh-button__group--compact > .mh-button {
|
|
379
|
-
border-radius: 0;
|
|
380
|
-
}
|
|
381
|
-
.mh-button__group--compact > .mh-button:first-child {
|
|
382
|
-
border-radius: var(--ui-button-radius) 0 0 var(--ui-button-radius);
|
|
383
|
-
}
|
|
384
|
-
.mh-button__group--compact > .mh-button:last-child {
|
|
385
|
-
border-radius: 0 var(--ui-button-radius) var(--ui-button-radius) 0;
|
|
386
|
-
}
|
|
387
|
-
.mh-button__group--compact > .mh-button + .mh-button {
|
|
388
|
-
margin-left: -1px; /* 相邻描边重合,避免连体处出现 2px 双线 */
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/* ===== 连体切换(选中半区 + 未选半区连体;状态由组件或静态档给) ===== */
|
|
392
|
-
.mh-button__switch {
|
|
393
|
-
display: inline-flex;
|
|
394
|
-
border-radius: var(--ui-button-radius);
|
|
395
|
-
}
|
|
396
|
-
.mh-button__switch > .mh-button {
|
|
397
|
-
border-radius: 0;
|
|
398
|
-
}
|
|
399
|
-
.mh-button__switch > .mh-button:first-child {
|
|
400
|
-
border-radius: var(--ui-button-radius) 0 0 var(--ui-button-radius);
|
|
401
|
-
}
|
|
402
|
-
.mh-button__switch > .mh-button:last-child {
|
|
403
|
-
border-radius: 0 var(--ui-button-radius) var(--ui-button-radius) 0;
|
|
404
|
-
}
|
|
405
|
-
.mh-button--off {
|
|
406
|
-
background: var(--ui-base-100);
|
|
407
|
-
border: 1px solid var(--ui-base-300);
|
|
408
|
-
color: var(--ui-base-content);
|
|
409
|
-
box-shadow: var(--ui-shadow-card);
|
|
410
|
-
}
|
|
411
|
-
.mh-button--off:hover,
|
|
412
|
-
.mh-button--off.is-hover {
|
|
413
|
-
border-color: var(--ui-button-accent-hover);
|
|
414
|
-
color: var(--ui-button-accent-hover);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/* ===== 按钮当「文件选择器」触发器(upload 族复用:点按钮即唤起系统选择,零 JS) ===== */
|
|
418
|
-
.mh-button > input[type='file'] {
|
|
419
|
-
position: absolute;
|
|
420
|
-
width: 1px;
|
|
421
|
-
height: 1px;
|
|
422
|
-
margin: -1px;
|
|
423
|
-
padding: 0;
|
|
424
|
-
border: 0;
|
|
425
|
-
overflow: hidden;
|
|
426
|
-
white-space: nowrap;
|
|
427
|
-
clip-path: inset(50%);
|
|
428
|
-
appearance: none;
|
|
429
|
-
pointer-events: none;
|
|
430
|
-
}
|
|
431
|
-
/* 焦点环画在按钮上(隐身的 file input 没有可见焦点) */
|
|
432
|
-
.mh-button:has(> input[type='file']:focus-visible) {
|
|
433
|
-
outline: 2px solid var(--ui-primary);
|
|
434
|
-
outline-offset: -2px;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/* ===== 子菜单浮层皮肤(与操作层并列组合:`class="mh-popup mh-button__menu"`) =====
|
|
438
|
-
* 只给面(底 / 圆角 / 内距 / 阴影 / 选项行),开合、Esc、点外部关闭、顶层渲染、定位档全在 popup.css。 */
|
|
439
|
-
.mh-button__menu {
|
|
440
|
-
width: 130px;
|
|
441
|
-
padding: var(--ui-space-1) 6px;
|
|
442
|
-
/* 圆角取容器档(原为硬编码 10px —— 与令牌里的容器档同义却写死,改版时不会跟随);
|
|
443
|
-
投影取锚定浮层档(`--ui-shadow-popover`),不是居中浮层档。 */
|
|
444
|
-
border-radius: var(--ui-radius-md);
|
|
445
|
-
background: var(--ui-base-100);
|
|
446
|
-
box-shadow: var(--ui-shadow-popover);
|
|
447
|
-
}
|
|
448
|
-
.mh-button__menu-item {
|
|
449
|
-
height: 30px;
|
|
450
|
-
padding: 0 var(--ui-space-2);
|
|
451
|
-
border-radius: var(--ui-button-radius);
|
|
452
|
-
font-size: var(--ui-button-font-size);
|
|
453
|
-
line-height: 30px;
|
|
454
|
-
color: var(--ui-base-content);
|
|
455
|
-
cursor: pointer;
|
|
456
|
-
}
|
|
457
|
-
.mh-button__menu-item:hover,
|
|
458
|
-
.mh-button__menu-item.is-hover {
|
|
459
|
-
background: var(--ui-base-200);
|
|
460
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Button —— 自绘按钮族(六型 + 三档真实尺寸 + 外形 / 危险色 / 整宽修饰 + 加载 + 图标/文本/菜单形态 + 组容器 + 菜单皮肤)。
|
|
3
|
+
*
|
|
4
|
+
* 出处(几何以设计稿《MH后台-0911》品牌页为准,规范同值处不再复述):
|
|
5
|
+
* - 控件高 **32** / 内距 **12** / 图文距 **4** / 圆角 **6** / 字号 **14/18 w400** / 组间距 8;
|
|
6
|
+
* - 主要 = 主色实底 **无投影**(`0:3897`);次要 = 白底 + `#e6e6e6` 描边 + **中性字色**
|
|
7
|
+
* (`0:3892`,文本 `#1a1a1a`);禁用 = `#f2f2f2` 底 + `#888888` 字(`次要按钮备份`);
|
|
8
|
+
* - 小号 24、大号 40(设计未覆盖,沿用);图标槽 = 全局图标档(见 `--ui-button-icon-size`);加载环 14 · 0.8s linear;
|
|
9
|
+
* 虚线 = 区域内添加,文本 = 行内操作,图标 = 方形热区;菜单按钮 = 文本区 ‖ 分割线 ‖ 箭头区。
|
|
10
|
+
*
|
|
11
|
+
* **色彩槽位走局部变量**(`--ui-button-accent*`):六型只描述「底 / 边 / 字的取用位置」,
|
|
12
|
+
* 具体颜色由槽位变量给。危险色只是把槽位换成错误色,故 `danger` 与任意型**天然正交**,
|
|
13
|
+
* hover / active / 加载态一并跟随,无需为每个型再写一遍危险态(这是本文件的核心结构)。
|
|
14
|
+
*
|
|
15
|
+
* **高度三档全部吃主题令牌**(`--ui-control-height-sm` / `-md` / `-lg`,default 24 / 32 / 40)——
|
|
16
|
+
* 换主题即换控件尺寸,这正是 theme 层的职责。md 与 lg **不同高**:同高会让 `size`
|
|
17
|
+
* 沦为无视觉差别的语义标签,调用方无从选择。
|
|
18
|
+
*
|
|
19
|
+
* **类名一律 `mh-` 前缀**:本包独立发布,不迁就任何下游的既有类名;同一词根在本包内只有一套类(见 AGENTS)。
|
|
20
|
+
* 自绘件服务「不装 farris」的场景:静态页 / 其他框架 / 微前端里不带 farris 的子应用。
|
|
21
|
+
*
|
|
22
|
+
* **投影**:主要与次要两型**不再带投影**(设计稿的按钮是平的,节点上无 effects)。
|
|
23
|
+
* 图标型的 hover 光晕仍引用 `--ui-shadow-card-hover` —— 那是 hover 反馈,不是静止装饰,
|
|
24
|
+
* 且投影一律走令牌、不新造字面量(与 shadows.css「拟物重投影一律收敛到令牌」的口径一致)。
|
|
25
|
+
*
|
|
26
|
+
* 边界(有意不做):规范的 `.btn-seg`(分段按钮,原生同名 radio 驱动)**不在这里实现** ——
|
|
27
|
+
* 它的用途(一组互斥选项的轻量选择器)由 `capsule` 族承担,重复实现会出现两套分段控件。
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/* 几何只挂在按钮本体上:令牌在同名 `.tokens.css`(锚 `[data-manohub-ui]`),几何若跟着挂会把容器也画成按钮 */
|
|
31
|
+
.mh-button {
|
|
32
|
+
display: inline-flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
gap: var(--ui-button-gap);
|
|
36
|
+
height: var(--ui-button-height);
|
|
37
|
+
padding: 0 var(--ui-button-padding-x);
|
|
38
|
+
border: none;
|
|
39
|
+
border-radius: var(--ui-button-radius);
|
|
40
|
+
font-family: inherit;
|
|
41
|
+
font-size: var(--ui-button-font-size);
|
|
42
|
+
line-height: var(--ui-line-body);
|
|
43
|
+
/* 链接形态时本体就是 `<a>`(见组件层),基线不再代管 `a { text-decoration: inherit }`,这里自己摘掉下划线 */
|
|
44
|
+
text-decoration: none;
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
white-space: nowrap;
|
|
47
|
+
/* 三态补间(底 / 边 / 字 / 影) */
|
|
48
|
+
transition-property: background-color, border-color, color, box-shadow;
|
|
49
|
+
transition-duration: 0.15s;
|
|
50
|
+
transition-timing-function: ease;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* 危险色修饰:换槽位即可,六型与各自 hover / active 一并跟随
|
|
54
|
+
(`--ui-button-accent-soft` 随无消费者的孤令牌一并撤除,2026-09-30) */
|
|
55
|
+
.mh-button--danger {
|
|
56
|
+
--ui-button-accent: var(--ui-error);
|
|
57
|
+
--ui-button-accent-hover: color-mix(in srgb, var(--ui-error) 88%, black);
|
|
58
|
+
--ui-button-accent-content: var(--ui-error-content);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* 键盘焦点环:画在按钮自身(与 radio / tag 同一句) */
|
|
62
|
+
.mh-button:focus-visible {
|
|
63
|
+
outline: 2px solid var(--ui-primary);
|
|
64
|
+
outline-offset: -2px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.mh-button__icon {
|
|
68
|
+
flex: none;
|
|
69
|
+
width: var(--ui-button-icon-size);
|
|
70
|
+
height: var(--ui-button-icon-size);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* ===== 尺寸档 ===== */
|
|
74
|
+
.mh-button--sm {
|
|
75
|
+
height: var(--ui-button-height-sm);
|
|
76
|
+
padding: 0 var(--ui-button-padding-x-sm);
|
|
77
|
+
font-size: var(--ui-button-font-size-sm);
|
|
78
|
+
}
|
|
79
|
+
.mh-button--sm .mh-button__icon,
|
|
80
|
+
.mh-button--sm .mh-button__caret {
|
|
81
|
+
width: var(--ui-button-icon-size-sm);
|
|
82
|
+
height: var(--ui-button-icon-size-sm);
|
|
83
|
+
}
|
|
84
|
+
.mh-button--lg {
|
|
85
|
+
height: var(--ui-button-height-lg);
|
|
86
|
+
padding: 0 var(--ui-button-padding-x-lg);
|
|
87
|
+
font-size: var(--ui-button-font-size-lg);
|
|
88
|
+
}
|
|
89
|
+
.mh-button--lg .mh-button__icon,
|
|
90
|
+
.mh-button--lg .mh-button__caret {
|
|
91
|
+
width: var(--ui-button-icon-size-lg);
|
|
92
|
+
height: var(--ui-button-icon-size-lg);
|
|
93
|
+
}
|
|
94
|
+
/* 尺寸档的菜单结构:文字区内距与分割线高度必须跟着档位走 */
|
|
95
|
+
.mh-button--sm.mh-button--menu .mh-button__label {
|
|
96
|
+
padding: 0 var(--ui-button-padding-x-sm);
|
|
97
|
+
}
|
|
98
|
+
.mh-button--lg.mh-button--menu .mh-button__label {
|
|
99
|
+
padding: 0 var(--ui-button-padding-x-lg);
|
|
100
|
+
}
|
|
101
|
+
.mh-button--sm .mh-button__divider {
|
|
102
|
+
height: var(--ui-button-height-sm);
|
|
103
|
+
}
|
|
104
|
+
.mh-button--lg .mh-button__divider {
|
|
105
|
+
height: var(--ui-button-height-lg);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* ===== 外形修饰(与型正交) ===== */
|
|
109
|
+
.mh-button--round {
|
|
110
|
+
border-radius: var(--ui-radius-full);
|
|
111
|
+
}
|
|
112
|
+
.mh-button--circle {
|
|
113
|
+
width: var(--ui-button-height);
|
|
114
|
+
height: var(--ui-button-height);
|
|
115
|
+
padding: 0;
|
|
116
|
+
border-radius: var(--ui-radius-full);
|
|
117
|
+
}
|
|
118
|
+
.mh-button--sm.mh-button--circle {
|
|
119
|
+
width: var(--ui-button-height-sm);
|
|
120
|
+
height: var(--ui-button-height-sm);
|
|
121
|
+
}
|
|
122
|
+
.mh-button--lg.mh-button--circle {
|
|
123
|
+
width: var(--ui-button-height-lg);
|
|
124
|
+
height: var(--ui-button-height-lg);
|
|
125
|
+
}
|
|
126
|
+
/* 整宽:表单底栏 / 移动端主操作;用 flex 而非 width 单给,保证菜单结构的内部分布不变 */
|
|
127
|
+
.mh-button--block {
|
|
128
|
+
display: flex;
|
|
129
|
+
width: 100%;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* ===== 主要:主色实底(**无投影**) =====
|
|
133
|
+
设计实证:`0:3897` 是纯 `#2660ff` 实底 + r6,节点上没有任何 effects。
|
|
134
|
+
改前挂的 `--ui-shadow-card-hover` 会让按钮在设计里「浮起来」,属多余装饰。 */
|
|
135
|
+
.mh-button--primary {
|
|
136
|
+
background: var(--ui-button-accent);
|
|
137
|
+
color: var(--ui-button-accent-content);
|
|
138
|
+
}
|
|
139
|
+
.mh-button--primary:hover,
|
|
140
|
+
.mh-button--primary.is-hover {
|
|
141
|
+
background: var(--ui-button-accent-hover);
|
|
142
|
+
}
|
|
143
|
+
.mh-button--primary:active,
|
|
144
|
+
.mh-button--primary.is-active {
|
|
145
|
+
background: color-mix(in srgb, var(--ui-button-accent) 88%, black); /* 规范按下档比滑过更沉 */
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* ===== 次要:白底描边 + **中性字色**(无投影) =====
|
|
149
|
+
设计实证:`0:3892` = 白底 + `#e6e6e6` 描边 + r6 + 文本 `#1a1a1a`(**中性**,不是主色蓝)。
|
|
150
|
+
改前用 `--ui-button-accent`(主色蓝)+ `--ui-shadow-card`,页头上会出现「蓝字带投影」的次要按钮,
|
|
151
|
+
与设计的「黑字平贴」观感不同。白底按钮在浅色页面上本就靠描边成形,**不需要投影**。 */
|
|
152
|
+
.mh-button--secondary {
|
|
153
|
+
background: var(--ui-base-100);
|
|
154
|
+
border: 1px solid var(--ui-base-300);
|
|
155
|
+
color: var(--ui-base-content);
|
|
156
|
+
}
|
|
157
|
+
.mh-button--secondary:hover,
|
|
158
|
+
.mh-button--secondary.is-hover {
|
|
159
|
+
border-color: var(--ui-button-accent-hover);
|
|
160
|
+
color: var(--ui-button-accent-hover);
|
|
161
|
+
}
|
|
162
|
+
.mh-button--secondary:active,
|
|
163
|
+
.mh-button--secondary.is-active {
|
|
164
|
+
border-color: var(--ui-button-accent);
|
|
165
|
+
color: var(--ui-button-accent);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ===== 虚线:区域内添加内容 =====
|
|
169
|
+
* 虚线取 `--ui-line-dashed`(= `base-400`,比实线深一档):同样粗细下虚线的墨迹只有实线的一半,
|
|
170
|
+
* 取实线色(`base-300`)会明显偏浅、整条框看着发虚。
|
|
171
|
+
* 这条口径 2026-09-30 一天内翻过一次:早些时候按「线框色只留一套」把虚线也收敛到 `base-300`,
|
|
172
|
+
* 当日晚按实机观感反转 —— 定稿见 `@manohub/theme` 的 `default/colors.css` 头「三条口径」第 2 条。 */
|
|
173
|
+
.mh-button--dashed {
|
|
174
|
+
background: var(--ui-base-100);
|
|
175
|
+
border: 1px dashed var(--ui-line-dashed);
|
|
176
|
+
color: var(--ui-button-accent);
|
|
177
|
+
}
|
|
178
|
+
.mh-button--dashed:hover,
|
|
179
|
+
.mh-button--dashed.is-hover {
|
|
180
|
+
border-color: var(--ui-button-accent-hover);
|
|
181
|
+
color: var(--ui-button-accent-hover);
|
|
182
|
+
}
|
|
183
|
+
.mh-button--dashed:active,
|
|
184
|
+
.mh-button--dashed.is-active {
|
|
185
|
+
border-color: var(--ui-button-accent);
|
|
186
|
+
color: var(--ui-button-accent);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* ===== 文本按钮(**只用于行内 / 批量操作**:表格操作列、批量条、列表行内操作;
|
|
190
|
+
* 页头与区块的操作区一律用 primary + secondary —— 与规范 README 同口径) ===== */
|
|
191
|
+
.mh-button--text {
|
|
192
|
+
padding: 0 var(--ui-space-1);
|
|
193
|
+
background: transparent;
|
|
194
|
+
color: var(--ui-button-accent);
|
|
195
|
+
}
|
|
196
|
+
.mh-button--text:hover,
|
|
197
|
+
.mh-button--text.is-hover {
|
|
198
|
+
color: var(--ui-button-accent-hover);
|
|
199
|
+
}
|
|
200
|
+
.mh-button--text:active,
|
|
201
|
+
.mh-button--text.is-active {
|
|
202
|
+
color: color-mix(in srgb, var(--ui-button-accent) 80%, black);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* ===== 链接按钮(跳转语义:带下划线,与「行内文本操作」的 text 档区分开) ===== */
|
|
206
|
+
.mh-button--link {
|
|
207
|
+
padding: 0;
|
|
208
|
+
background: transparent;
|
|
209
|
+
color: var(--ui-button-accent);
|
|
210
|
+
text-decoration: underline;
|
|
211
|
+
text-underline-offset: 3px;
|
|
212
|
+
}
|
|
213
|
+
.mh-button--link:hover,
|
|
214
|
+
.mh-button--link.is-hover {
|
|
215
|
+
color: var(--ui-button-accent-hover);
|
|
216
|
+
}
|
|
217
|
+
.mh-button--link:active,
|
|
218
|
+
.mh-button--link.is-active {
|
|
219
|
+
color: color-mix(in srgb, var(--ui-button-accent) 80%, black);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* ===== 图标按钮(方形热区 + 16 字形;滑过/点击带主色光晕) ===== */
|
|
223
|
+
.mh-button--icon {
|
|
224
|
+
width: var(--ui-button-height);
|
|
225
|
+
height: var(--ui-button-height);
|
|
226
|
+
padding: 0; /* 显式清 UA 给 <button> 的内距,否则 border-box 下字形被挤扁 */
|
|
227
|
+
background: transparent;
|
|
228
|
+
color: var(--ui-base-content);
|
|
229
|
+
}
|
|
230
|
+
.mh-button--sm.mh-button--icon {
|
|
231
|
+
width: var(--ui-button-height-sm);
|
|
232
|
+
height: var(--ui-button-height-sm);
|
|
233
|
+
}
|
|
234
|
+
.mh-button--lg.mh-button--icon {
|
|
235
|
+
width: var(--ui-button-height-lg);
|
|
236
|
+
height: var(--ui-button-height-lg);
|
|
237
|
+
}
|
|
238
|
+
.mh-button--icon:hover,
|
|
239
|
+
.mh-button--icon.is-hover {
|
|
240
|
+
color: var(--ui-button-accent-hover);
|
|
241
|
+
box-shadow: var(--ui-shadow-card-hover);
|
|
242
|
+
}
|
|
243
|
+
.mh-button--icon:active,
|
|
244
|
+
.mh-button--icon.is-active {
|
|
245
|
+
color: var(--ui-button-accent);
|
|
246
|
+
}
|
|
247
|
+
/* 危险色 + 图标 / 文本 / 链接:这三型的字色不走 accent 槽(分别是中性 / 主色语义),单列一条 */
|
|
248
|
+
.mh-button--danger.mh-button--icon,
|
|
249
|
+
.mh-button--danger.mh-button--text,
|
|
250
|
+
.mh-button--danger.mh-button--link {
|
|
251
|
+
color: var(--ui-error);
|
|
252
|
+
}
|
|
253
|
+
.mh-button--danger.mh-button--icon:hover,
|
|
254
|
+
.mh-button--danger.mh-button--text:hover,
|
|
255
|
+
.mh-button--danger.mh-button--link:hover {
|
|
256
|
+
color: color-mix(in srgb, var(--ui-error) 88%, black);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* ===== 禁用(各类通用;用专用令牌而非 opacity,避免禁用态被底色影响)
|
|
260
|
+
*
|
|
261
|
+
* 描边走 **inset 阴影而不是 `border`**:`border: 1px` 会把「无边框」的常态按钮撑宽 2px,
|
|
262
|
+
* 按钮一进禁用就在底栏里抖一下 —— inset 阴影画的还是同一圈 1px、位置也一致,但元素尺寸不变。
|
|
263
|
+
*
|
|
264
|
+
* **加载态排除在外**(`:not()`):`loading` 虽然也置原生 disabled(吞点击),但它**外观保持本型**
|
|
265
|
+
* —— 点击前后只有「文字 → 环」这一处变化,不换底 / 不换边 / 不换字。不排除的话,禁用档
|
|
266
|
+
* (2 个类)会压过 `.mh-button--loading`(1 个类),全屏最显眼的主操作会在点下去的一瞬间
|
|
267
|
+
* 整块变成禁用灰,观感突兀(浏览器实测过:底 `rgb(241,242,245)` + 灰字 + 灰边)。 */
|
|
268
|
+
.mh-button:disabled:not(.mh-button--loading),
|
|
269
|
+
.mh-button.is-disabled:not(.mh-button--loading),
|
|
270
|
+
.mh-button[aria-disabled='true']:not(.mh-button--loading) {
|
|
271
|
+
background: var(--ui-button-bg-disabled);
|
|
272
|
+
box-shadow: inset 0 0 0 1px var(--ui-button-border-disabled);
|
|
273
|
+
color: var(--ui-button-text-disabled);
|
|
274
|
+
cursor: not-allowed;
|
|
275
|
+
}
|
|
276
|
+
/* 文本 / 链接 / 图标三型没有底与边,禁用只换字色 */
|
|
277
|
+
.mh-button--text:disabled:not(.mh-button--loading),
|
|
278
|
+
.mh-button--text.is-disabled:not(.mh-button--loading),
|
|
279
|
+
.mh-button--text[aria-disabled='true']:not(.mh-button--loading),
|
|
280
|
+
.mh-button--link:disabled:not(.mh-button--loading),
|
|
281
|
+
.mh-button--link.is-disabled:not(.mh-button--loading),
|
|
282
|
+
.mh-button--link[aria-disabled='true']:not(.mh-button--loading),
|
|
283
|
+
.mh-button--icon:disabled:not(.mh-button--loading),
|
|
284
|
+
.mh-button--icon.is-disabled:not(.mh-button--loading),
|
|
285
|
+
.mh-button--icon[aria-disabled='true']:not(.mh-button--loading) {
|
|
286
|
+
background: transparent;
|
|
287
|
+
border: none;
|
|
288
|
+
color: var(--ui-button-text-disabled);
|
|
289
|
+
box-shadow: none;
|
|
290
|
+
text-decoration: none;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/* ===== 加载(**本型外观 + 主色降透明 + 环**) =====
|
|
294
|
+
*
|
|
295
|
+
* 加载态不改色彩角色:`primary` 还是主色、`danger` 还是错误色、`secondary` 还是白底黑字 ——
|
|
296
|
+
* 只是实底型的**主色底**按 `--ui-button-loading-mix` 与面板色显式合成一档更淡的同色。
|
|
297
|
+
* 忙碌时的按钮该是「同一个按钮淡了一点点、在转圈」,而不是换一副面孔(整块变灰或换档色都太突兀)。
|
|
298
|
+
*
|
|
299
|
+
* **为什么是显式合成而不是 `opacity`**:`opacity` 压的是整块,连文案与加载环一起淡 ——
|
|
300
|
+
* 忙碌时的文案恰恰要读得清;写成 `color-mix(主色 × mix, 面板色)` 只让**底**淡一档。
|
|
301
|
+
* (也别用覆盖层:主色实底上再盖一层同色半透明层等于什么都没做 —— 那是条死路。)
|
|
302
|
+
*
|
|
303
|
+
* 其余五型(`secondary` / `dashed` / `text` / `link` / `icon`)的「主色」在描边或字上,
|
|
304
|
+
* 底本来就是白的或透明的 —— 再降一档只会把边与字读淡,故它们加载时**外观完全不变**,只多一个环。
|
|
305
|
+
* 环取 **`currentColor`**:一条规则自动跟上六型与 `danger`(实底型白环、浅底与图标型本型字色环、
|
|
306
|
+
* 透明底型主色环),既不必逐型特判,也不额外引令牌。 */
|
|
307
|
+
.mh-button--loading {
|
|
308
|
+
cursor: not-allowed; /* 禁用档被排除了,`:disabled` 那条已不匹配它 */
|
|
309
|
+
}
|
|
310
|
+
/* 实底型的加载底:主色与面板色按令牌比例显式混出。
|
|
311
|
+
写在 hover / active 之后:同特异性(0.2.0)下后者胜出,故加载中滑过、按下都不会回到满主色 */
|
|
312
|
+
.mh-button--loading.mh-button--primary {
|
|
313
|
+
background: color-mix(
|
|
314
|
+
in srgb,
|
|
315
|
+
var(--ui-button-accent) var(--ui-button-loading-mix),
|
|
316
|
+
var(--ui-base-100)
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
.mh-button__spinner {
|
|
320
|
+
flex: none;
|
|
321
|
+
width: var(--ui-button-spinner-size);
|
|
322
|
+
height: var(--ui-button-spinner-size);
|
|
323
|
+
border: 1.5px solid currentColor; /* 跟随按钮的字色,六型通用 */
|
|
324
|
+
border-top-color: transparent;
|
|
325
|
+
border-radius: var(--ui-radius-full);
|
|
326
|
+
animation: mh-button-spin 0.8s linear infinite; /* 规范 0.8s linear */
|
|
327
|
+
}
|
|
328
|
+
@keyframes mh-button-spin {
|
|
329
|
+
to {
|
|
330
|
+
transform: rotate(360deg);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* ===== 下拉箭头槽 =====
|
|
335
|
+
* **只给槽位尺寸,不给字形**:字形由 `@manohub/icon` 的 `<Icon name="chevron-down">` 画,
|
|
336
|
+
* 展开时的转向由操作层 `popup.css` 的 `.mh-popup-caret` 承担(本件不定义第二份 mask / 第二处转向)。
|
|
337
|
+
* 故组件把两个类同时挂在图标上:`class="mh-button__caret mh-popup-caret"`。 */
|
|
338
|
+
.mh-button__caret {
|
|
339
|
+
flex: none;
|
|
340
|
+
width: var(--ui-button-icon-size);
|
|
341
|
+
height: var(--ui-button-icon-size);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* ===== 菜单按钮(文本区 ‖ 分割线 ‖ 箭头区;只管结构,色调随修饰符) ===== */
|
|
345
|
+
.mh-button--menu {
|
|
346
|
+
padding: 0;
|
|
347
|
+
gap: 0;
|
|
348
|
+
}
|
|
349
|
+
.mh-button__label {
|
|
350
|
+
padding: 0 var(--ui-button-padding-x);
|
|
351
|
+
}
|
|
352
|
+
.mh-button--menu .mh-button__caret {
|
|
353
|
+
margin: 0 var(--ui-button-gap);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/* 竖分割线(1px,跟按钮同高) */
|
|
357
|
+
.mh-button__divider {
|
|
358
|
+
flex: none;
|
|
359
|
+
width: 1px;
|
|
360
|
+
height: var(--ui-button-height);
|
|
361
|
+
background: var(--ui-base-300);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* ===== 组容器(组件层结构,不是排版行):组内间距恒 8 ===== */
|
|
365
|
+
.mh-button__group {
|
|
366
|
+
display: inline-flex;
|
|
367
|
+
align-items: center;
|
|
368
|
+
gap: var(--ui-button-group-gap);
|
|
369
|
+
}
|
|
370
|
+
/* 组内文本按钮把水平内距交还容器 —— 间距只由 gap 单一拥有(否则 4+8+4=16) */
|
|
371
|
+
.mh-button__group > .mh-button--text {
|
|
372
|
+
padding-inline: 0;
|
|
373
|
+
}
|
|
374
|
+
/* 连体档:相邻按钮边重合、中间圆角归零(工具条形态;首末保留圆角) */
|
|
375
|
+
.mh-button__group--compact {
|
|
376
|
+
gap: 0;
|
|
377
|
+
}
|
|
378
|
+
.mh-button__group--compact > .mh-button {
|
|
379
|
+
border-radius: 0;
|
|
380
|
+
}
|
|
381
|
+
.mh-button__group--compact > .mh-button:first-child {
|
|
382
|
+
border-radius: var(--ui-button-radius) 0 0 var(--ui-button-radius);
|
|
383
|
+
}
|
|
384
|
+
.mh-button__group--compact > .mh-button:last-child {
|
|
385
|
+
border-radius: 0 var(--ui-button-radius) var(--ui-button-radius) 0;
|
|
386
|
+
}
|
|
387
|
+
.mh-button__group--compact > .mh-button + .mh-button {
|
|
388
|
+
margin-left: -1px; /* 相邻描边重合,避免连体处出现 2px 双线 */
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/* ===== 连体切换(选中半区 + 未选半区连体;状态由组件或静态档给) ===== */
|
|
392
|
+
.mh-button__switch {
|
|
393
|
+
display: inline-flex;
|
|
394
|
+
border-radius: var(--ui-button-radius);
|
|
395
|
+
}
|
|
396
|
+
.mh-button__switch > .mh-button {
|
|
397
|
+
border-radius: 0;
|
|
398
|
+
}
|
|
399
|
+
.mh-button__switch > .mh-button:first-child {
|
|
400
|
+
border-radius: var(--ui-button-radius) 0 0 var(--ui-button-radius);
|
|
401
|
+
}
|
|
402
|
+
.mh-button__switch > .mh-button:last-child {
|
|
403
|
+
border-radius: 0 var(--ui-button-radius) var(--ui-button-radius) 0;
|
|
404
|
+
}
|
|
405
|
+
.mh-button--off {
|
|
406
|
+
background: var(--ui-base-100);
|
|
407
|
+
border: 1px solid var(--ui-base-300);
|
|
408
|
+
color: var(--ui-base-content);
|
|
409
|
+
box-shadow: var(--ui-shadow-card);
|
|
410
|
+
}
|
|
411
|
+
.mh-button--off:hover,
|
|
412
|
+
.mh-button--off.is-hover {
|
|
413
|
+
border-color: var(--ui-button-accent-hover);
|
|
414
|
+
color: var(--ui-button-accent-hover);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/* ===== 按钮当「文件选择器」触发器(upload 族复用:点按钮即唤起系统选择,零 JS) ===== */
|
|
418
|
+
.mh-button > input[type='file'] {
|
|
419
|
+
position: absolute;
|
|
420
|
+
width: 1px;
|
|
421
|
+
height: 1px;
|
|
422
|
+
margin: -1px;
|
|
423
|
+
padding: 0;
|
|
424
|
+
border: 0;
|
|
425
|
+
overflow: hidden;
|
|
426
|
+
white-space: nowrap;
|
|
427
|
+
clip-path: inset(50%);
|
|
428
|
+
appearance: none;
|
|
429
|
+
pointer-events: none;
|
|
430
|
+
}
|
|
431
|
+
/* 焦点环画在按钮上(隐身的 file input 没有可见焦点) */
|
|
432
|
+
.mh-button:has(> input[type='file']:focus-visible) {
|
|
433
|
+
outline: 2px solid var(--ui-primary);
|
|
434
|
+
outline-offset: -2px;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/* ===== 子菜单浮层皮肤(与操作层并列组合:`class="mh-popup mh-button__menu"`) =====
|
|
438
|
+
* 只给面(底 / 圆角 / 内距 / 阴影 / 选项行),开合、Esc、点外部关闭、顶层渲染、定位档全在 popup.css。 */
|
|
439
|
+
.mh-button__menu {
|
|
440
|
+
width: 130px;
|
|
441
|
+
padding: var(--ui-space-1) 6px;
|
|
442
|
+
/* 圆角取容器档(原为硬编码 10px —— 与令牌里的容器档同义却写死,改版时不会跟随);
|
|
443
|
+
投影取锚定浮层档(`--ui-shadow-popover`),不是居中浮层档。 */
|
|
444
|
+
border-radius: var(--ui-radius-md);
|
|
445
|
+
background: var(--ui-base-100);
|
|
446
|
+
box-shadow: var(--ui-shadow-popover);
|
|
447
|
+
}
|
|
448
|
+
.mh-button__menu-item {
|
|
449
|
+
height: 30px;
|
|
450
|
+
padding: 0 var(--ui-space-2);
|
|
451
|
+
border-radius: var(--ui-button-radius);
|
|
452
|
+
font-size: var(--ui-button-font-size);
|
|
453
|
+
line-height: 30px;
|
|
454
|
+
color: var(--ui-base-content);
|
|
455
|
+
cursor: pointer;
|
|
456
|
+
}
|
|
457
|
+
.mh-button__menu-item:hover,
|
|
458
|
+
.mh-button__menu-item.is-hover {
|
|
459
|
+
background: var(--ui-base-200);
|
|
460
|
+
}
|