@recoui/style 0.0.0-stage → 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +14 -2
- package/dist/index.css +1 -0
- package/package.json +51 -4
- package/src/index.scss +1 -0
- package/src/styles/container/box.scss +15 -0
- package/src/styles/container/dropdown.scss +93 -0
- package/src/styles/container/index.scss +3 -0
- package/src/styles/container/modal.scss +91 -0
- package/src/styles/display/badge.scss +55 -0
- package/src/styles/display/collapse.scss +5 -0
- package/src/styles/display/index.scss +5 -0
- package/src/styles/display/pill.scss +67 -0
- package/src/styles/display/table.scss +46 -0
- package/src/styles/display/tooltip.scss +161 -0
- package/src/styles/form/field.scss +74 -0
- package/src/styles/form/form.scss +131 -0
- package/src/styles/form/index.scss +2 -0
- package/src/styles/group/group.scss +15 -0
- package/src/styles/group/index.scss +2 -0
- package/src/styles/group/join.scss +64 -0
- package/src/styles/index.scss +6 -0
- package/src/styles/input/base.scss +99 -0
- package/src/styles/input/button.scss +147 -0
- package/src/styles/input/checkbox.scss +110 -0
- package/src/styles/input/index.scss +11 -0
- package/src/styles/input/indicators.scss +270 -0
- package/src/styles/input/input.scss +203 -0
- package/src/styles/input/label-wrapper.scss +37 -0
- package/src/styles/input/native.scss +154 -0
- package/src/styles/input/options.scss +99 -0
- package/src/styles/input/slider.scss +218 -0
- package/src/styles/input/switch.scss +111 -0
- package/src/styles/input/tabs.scss +179 -0
- package/src/styles/variables.scss +30 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
@layer components.ui {
|
|
2
|
+
// checkbox 和 radio 结构性样式
|
|
3
|
+
.ui-checkbox,
|
|
4
|
+
.ui-radio {
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
inline-size: 1.25rem;
|
|
9
|
+
block-size: 1.25rem;
|
|
10
|
+
|
|
11
|
+
border-radius: var(--ui-radius, 0);
|
|
12
|
+
border: 1px solid var(--ui-border);
|
|
13
|
+
background: var(--ui-fill);
|
|
14
|
+
|
|
15
|
+
color: var(--color-white);
|
|
16
|
+
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
transition:
|
|
19
|
+
border-color var(--transition),
|
|
20
|
+
background-color var(--transition),
|
|
21
|
+
box-shadow var(--transition);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// checkbox 和 radio 基本外观
|
|
25
|
+
.ui-checkbox,
|
|
26
|
+
.ui-radio {
|
|
27
|
+
--ui-color: var(--color-primary, grey);
|
|
28
|
+
--ui-border: var(--color-border);
|
|
29
|
+
--ui-fill: var(--color-background);
|
|
30
|
+
--ui-text: var(--color-foreground);
|
|
31
|
+
--ui-fill-checked: var(--ui-color);
|
|
32
|
+
--ui-ring: oklch(from var(--ui-color) l c h / 10%);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.ui-checkbox {
|
|
36
|
+
--ui-radius: var(--radius-sm);
|
|
37
|
+
}
|
|
38
|
+
.ui-radio {
|
|
39
|
+
--ui-radius: 1000px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.ui-radio::before,
|
|
43
|
+
.ui-checkbox::before {
|
|
44
|
+
content: "";
|
|
45
|
+
position: absolute;
|
|
46
|
+
inset: 0;
|
|
47
|
+
margin: auto;
|
|
48
|
+
width: 0;
|
|
49
|
+
height: 0;
|
|
50
|
+
opacity: 0;
|
|
51
|
+
transition:
|
|
52
|
+
opacity var(--transition),
|
|
53
|
+
transform var(--transition),
|
|
54
|
+
background-color var(--transition);
|
|
55
|
+
transform: scale(0.6);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.ui-radio::before {
|
|
59
|
+
width: 0.4rem;
|
|
60
|
+
height: 0.4rem;
|
|
61
|
+
border-radius: 50%;
|
|
62
|
+
background-color: currentColor;
|
|
63
|
+
color: var(--color-white);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.ui-checkbox::before {
|
|
67
|
+
width: 0.875rem;
|
|
68
|
+
height: 0.875rem;
|
|
69
|
+
background-color: currentColor;
|
|
70
|
+
// 描边绘制两段勾线,避免未闭合的填充路径被绘制成三角形。
|
|
71
|
+
mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M3 8l3.5 3.5L13 4.5' fill='none' stroke='%23fff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'/></svg>")
|
|
72
|
+
center/contain no-repeat;
|
|
73
|
+
-webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M3 8l3.5 3.5L13 4.5' fill='none' stroke='%23fff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'/></svg>")
|
|
74
|
+
center/contain no-repeat;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.ui-checkbox:is(:checked, :indeterminate)::before,
|
|
78
|
+
.ui-radio:checked::before {
|
|
79
|
+
opacity: 1;
|
|
80
|
+
transform: scale(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.ui-checkbox:is(:checked, :indeterminate),
|
|
84
|
+
.ui-radio:checked {
|
|
85
|
+
background: var(--ui-fill-checked);
|
|
86
|
+
border-color: var(--ui-fill-checked);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 不确定状态独立于 checked,优先显示居中的圆端点横线。
|
|
90
|
+
.ui-checkbox:indeterminate::before {
|
|
91
|
+
mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M4 8h8' fill='none' stroke='%23fff' stroke-width='2.5' stroke-linecap='round'/></svg>")
|
|
92
|
+
center/contain no-repeat;
|
|
93
|
+
-webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M4 8h8' fill='none' stroke='%23fff' stroke-width='2.5' stroke-linecap='round'/></svg>")
|
|
94
|
+
center/contain no-repeat;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.ui-checkbox:focus-visible,
|
|
98
|
+
.ui-radio:focus-visible {
|
|
99
|
+
outline: none;
|
|
100
|
+
border-color: var(--ui-color);
|
|
101
|
+
box-shadow: 0 0 0 4px var(--ui-ring);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
:is(.ui-checkbox, .ui-radio):is(:disabled, [aria-disabled="true"]) {
|
|
105
|
+
color: var(--color-disabled);
|
|
106
|
+
border-color: var(--ui-border);
|
|
107
|
+
background: color-mix(in oklch, var(--color-foreground) 2%, var(--color-background));
|
|
108
|
+
cursor: not-allowed;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
@use "./button";
|
|
2
|
+
|
|
3
|
+
// 沿用 Tabler 的 24px 画布、2px 描边和圆端点;不引入图标库依赖。
|
|
4
|
+
@function native-icon($path, $stroke: "%23fff", $view-box: "0 0 24 24") {
|
|
5
|
+
@return url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='#{$view-box}' fill='none' stroke='#{$stroke}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='#{$path}'/%3E%3C/svg%3E");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@function chevron-down($stroke) {
|
|
9
|
+
@return native-icon("M6 9l6 6l6 -6", $stroke);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// 缩短斜线长度,不缩放描边:与 16px Tabler 图标相同的 1⅓px 描边。
|
|
13
|
+
@function stepper-stroke($angle) {
|
|
14
|
+
@return linear-gradient(
|
|
15
|
+
$angle,
|
|
16
|
+
transparent calc(50% - 1rem / 24),
|
|
17
|
+
currentColor calc(50% - 1rem / 24),
|
|
18
|
+
currentColor calc(50% + 1rem / 24),
|
|
19
|
+
transparent calc(50% + 1rem / 24)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@layer components.ui {
|
|
24
|
+
.ui-input {
|
|
25
|
+
--ui-input-chevron-mask: #{chevron-down("%23fff")};
|
|
26
|
+
--ui-input-chevron-image: #{chevron-down("%2322222a")};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 组合式输入框的外侧留白由根元素提供。
|
|
30
|
+
.ui-input > input[list] {
|
|
31
|
+
--ui-input-indicator-padding: 0px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 不支持原生图标伪元素的浏览器使用同形背景图,并为文字预留空间。
|
|
35
|
+
%ui-chevron-background {
|
|
36
|
+
padding-inline-end: calc(var(--ui-input-indicator-padding, var(--ui-padding-inline)) + 1.5rem);
|
|
37
|
+
background-image: var(--ui-input-chevron-image);
|
|
38
|
+
background-repeat: no-repeat;
|
|
39
|
+
background-size: 1rem 1rem;
|
|
40
|
+
background-position: right var(--ui-input-indicator-padding, var(--ui-padding-inline)) center;
|
|
41
|
+
|
|
42
|
+
&:dir(rtl) {
|
|
43
|
+
background-position: left var(--ui-input-indicator-padding, var(--ui-padding-inline)) center;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&:is(:disabled, [aria-disabled="true"]) {
|
|
47
|
+
--ui-input-chevron-image: #{chevron-down("%23818198")};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
select.ui-input:not([multiple]):not([size]) {
|
|
52
|
+
@extend %ui-chevron-background;
|
|
53
|
+
appearance: none;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
:is(input.ui-input, .ui-input > input)[list]:is(
|
|
57
|
+
:not([type]),
|
|
58
|
+
[type="text"],
|
|
59
|
+
[type="search"],
|
|
60
|
+
[type="email"],
|
|
61
|
+
[type="url"],
|
|
62
|
+
[type="tel"],
|
|
63
|
+
[type="number"]
|
|
64
|
+
) {
|
|
65
|
+
@extend %ui-chevron-background;
|
|
66
|
+
|
|
67
|
+
&::-webkit-calendar-picker-indicator {
|
|
68
|
+
// Chromium 为 datalist 图标写入内联 list-item 和 1em 高度,需显式覆盖。
|
|
69
|
+
display: block !important;
|
|
70
|
+
flex: 0 0 auto;
|
|
71
|
+
box-sizing: content-box;
|
|
72
|
+
inline-size: 1rem;
|
|
73
|
+
block-size: 1rem !important;
|
|
74
|
+
margin: 0;
|
|
75
|
+
margin-inline-start: 0.5rem;
|
|
76
|
+
padding: 0;
|
|
77
|
+
border: 0;
|
|
78
|
+
list-style: none !important;
|
|
79
|
+
background: currentColor;
|
|
80
|
+
color: inherit;
|
|
81
|
+
mask: var(--ui-input-chevron-mask) center / contain no-repeat;
|
|
82
|
+
opacity: 1;
|
|
83
|
+
cursor: inherit;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@supports selector(input::-webkit-calendar-picker-indicator) {
|
|
88
|
+
:is(input.ui-input, .ui-input > input)[list]:is(
|
|
89
|
+
:not([type]),
|
|
90
|
+
[type="text"],
|
|
91
|
+
[type="search"],
|
|
92
|
+
[type="email"],
|
|
93
|
+
[type="url"],
|
|
94
|
+
[type="tel"],
|
|
95
|
+
[type="number"]
|
|
96
|
+
) {
|
|
97
|
+
padding-inline-end: var(--ui-input-indicator-padding, var(--ui-padding-inline));
|
|
98
|
+
background-image: none;
|
|
99
|
+
|
|
100
|
+
// 禁用时浏览器不生成 datalist 图标,使用相同的背景图保留外观。
|
|
101
|
+
&:disabled {
|
|
102
|
+
padding-inline-end: calc(
|
|
103
|
+
var(--ui-input-indicator-padding, var(--ui-padding-inline)) + 1.5rem
|
|
104
|
+
);
|
|
105
|
+
background-image: var(--ui-input-chevron-image);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@supports (appearance: base-select) {
|
|
111
|
+
select.ui-input:not([multiple]):not([size]) {
|
|
112
|
+
appearance: base-select;
|
|
113
|
+
column-gap: 0.5rem;
|
|
114
|
+
padding-inline-end: var(--ui-padding-inline);
|
|
115
|
+
background-image: none;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 与 WebKit 伪元素分别声明,避免不支持的选择器使另一条规则失效。
|
|
119
|
+
select.ui-input:not([multiple]):not([size])::picker-icon {
|
|
120
|
+
content: "";
|
|
121
|
+
display: block;
|
|
122
|
+
flex: 0 0 auto;
|
|
123
|
+
box-sizing: content-box;
|
|
124
|
+
inline-size: 1rem;
|
|
125
|
+
block-size: 1rem;
|
|
126
|
+
margin: 0;
|
|
127
|
+
margin-inline-start: auto;
|
|
128
|
+
padding: 0;
|
|
129
|
+
border: 0;
|
|
130
|
+
background: currentColor;
|
|
131
|
+
color: inherit;
|
|
132
|
+
mask: var(--ui-input-chevron-mask) center / contain no-repeat;
|
|
133
|
+
opacity: 1;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 仅修饰已存在的原生按钮,不创建额外点击层或改变其可用状态。
|
|
138
|
+
// 同组只包含 WebKit/Blink 伪元素,避免其他引擎的选择器使整组失效。
|
|
139
|
+
%ui-native-input-indicator {
|
|
140
|
+
appearance: none;
|
|
141
|
+
flex: 0 0 auto;
|
|
142
|
+
align-self: center;
|
|
143
|
+
box-sizing: content-box;
|
|
144
|
+
inline-size: 1rem;
|
|
145
|
+
block-size: 1rem;
|
|
146
|
+
margin: 0;
|
|
147
|
+
margin-inline-start: 0.5rem;
|
|
148
|
+
padding: 0;
|
|
149
|
+
border: 0;
|
|
150
|
+
color: inherit;
|
|
151
|
+
background: currentColor;
|
|
152
|
+
mask: var(--ui-input-indicator-mask) center / contain no-repeat;
|
|
153
|
+
cursor: pointer;
|
|
154
|
+
|
|
155
|
+
@media (forced-colors: active) {
|
|
156
|
+
forced-color-adjust: none;
|
|
157
|
+
background: ButtonText;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
:is(input.ui-input, .ui-input > input):is(
|
|
162
|
+
[type="date"],
|
|
163
|
+
[type="datetime-local"],
|
|
164
|
+
[type="month"],
|
|
165
|
+
[type="week"],
|
|
166
|
+
[type="time"]
|
|
167
|
+
) {
|
|
168
|
+
// 原生日期编辑区域已有内部 flex 布局,宿主使用 flex 会令其收缩到文字宽度。
|
|
169
|
+
display: inline-block;
|
|
170
|
+
--ui-block-display: block;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
:is(input.ui-input, .ui-input > input):is(
|
|
174
|
+
[type="date"],
|
|
175
|
+
[type="datetime-local"],
|
|
176
|
+
[type="month"],
|
|
177
|
+
[type="week"]
|
|
178
|
+
) {
|
|
179
|
+
--ui-input-indicator-mask: #{native-icon(
|
|
180
|
+
"M4 7a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12M16 3v4M8 3v4M4 11h16M7 14h.013M10.01 14h.005M13.01 14h.005M16.015 14h.005M13.015 17h.005M7.01 17h.005M10.01 17h.005"
|
|
181
|
+
)};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
:is(input.ui-input, .ui-input > input)[type="time"] {
|
|
185
|
+
--ui-input-indicator-mask: #{native-icon("M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0M12 7v5l3 3")};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
:is(input.ui-input, .ui-input > input):is(
|
|
189
|
+
[type="date"],
|
|
190
|
+
[type="datetime-local"],
|
|
191
|
+
[type="month"],
|
|
192
|
+
[type="week"],
|
|
193
|
+
[type="time"]
|
|
194
|
+
)::-webkit-calendar-picker-indicator {
|
|
195
|
+
@extend %ui-native-input-indicator;
|
|
196
|
+
opacity: 1;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
:is(input.ui-input, .ui-input > input)[type="number"] {
|
|
200
|
+
&::-webkit-inner-spin-button {
|
|
201
|
+
// 一组等于一个 icon 型 addon-button;上下半区由浏览器原生处理。
|
|
202
|
+
appearance: none;
|
|
203
|
+
flex: 0 0 auto;
|
|
204
|
+
align-self: center;
|
|
205
|
+
box-sizing: border-box;
|
|
206
|
+
inline-size: var(--ui-addon-height);
|
|
207
|
+
block-size: var(--ui-addon-height);
|
|
208
|
+
margin: 0;
|
|
209
|
+
margin-inline-start: 0.5rem;
|
|
210
|
+
padding: 0;
|
|
211
|
+
border: 1px solid transparent;
|
|
212
|
+
border-radius: max(0px, calc(var(--radius) - 0.125rem));
|
|
213
|
+
color: inherit;
|
|
214
|
+
--ui-display-color: var(--color-foreground, currentColor);
|
|
215
|
+
cursor: pointer;
|
|
216
|
+
background-color: transparent;
|
|
217
|
+
// 四条等粗斜线绘制小 Chevron,底色不受图标 mask 裁切。
|
|
218
|
+
background-image: stepper-stroke(135deg), stepper-stroke(45deg), stepper-stroke(45deg),
|
|
219
|
+
stepper-stroke(135deg);
|
|
220
|
+
background-repeat: no-repeat;
|
|
221
|
+
background-origin: border-box;
|
|
222
|
+
background-size: 0.1875rem 0.1875rem;
|
|
223
|
+
background-position:
|
|
224
|
+
calc(50% - 0.09375rem) calc(25% - 0.046875rem),
|
|
225
|
+
calc(50% + 0.09375rem) calc(25% - 0.046875rem),
|
|
226
|
+
calc(50% - 0.09375rem) calc(75% + 0.046875rem),
|
|
227
|
+
calc(50% + 0.09375rem) calc(75% + 0.046875rem);
|
|
228
|
+
transition: background-color var(--transition);
|
|
229
|
+
|
|
230
|
+
&:hover {
|
|
231
|
+
@extend %ui-ghost-button-hover;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
@media (forced-colors: active) {
|
|
235
|
+
forced-color-adjust: none;
|
|
236
|
+
color: ButtonText;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
:is(input.ui-input, .ui-input > input)[type="search"] {
|
|
242
|
+
--ui-input-indicator-mask: #{native-icon("M18 6l-12 12M6 6l12 12")};
|
|
243
|
+
|
|
244
|
+
&::-webkit-search-cancel-button {
|
|
245
|
+
@extend %ui-native-input-indicator;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 保留 Edge 原生眼睛及其状态切换:::-ms-reveal 没有对应的状态选择器。
|
|
250
|
+
:is(input.ui-input, .ui-input > input)[type="password"]::-ms-reveal {
|
|
251
|
+
box-sizing: border-box;
|
|
252
|
+
inline-size: 1rem;
|
|
253
|
+
block-size: 1rem;
|
|
254
|
+
margin: 0;
|
|
255
|
+
margin-inline-start: 0.5rem;
|
|
256
|
+
padding: 0;
|
|
257
|
+
border: 0;
|
|
258
|
+
border-radius: var(--radius-sm);
|
|
259
|
+
color: inherit;
|
|
260
|
+
background-color: transparent;
|
|
261
|
+
background-position: center;
|
|
262
|
+
background-repeat: no-repeat;
|
|
263
|
+
background-size: contain;
|
|
264
|
+
cursor: pointer;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
:is(input.ui-input, .ui-input > input)[type="password"]::-ms-reveal:hover {
|
|
268
|
+
background-color: color-mix(in oklch, currentColor 10%, transparent);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
@layer components.ui {
|
|
2
|
+
// input + button 结构性样式
|
|
3
|
+
.ui-input,
|
|
4
|
+
.ui-button,
|
|
5
|
+
.ui-addon-button {
|
|
6
|
+
display: inline-flex;
|
|
7
|
+
font-size: var(--font-size-input, 0.875rem);
|
|
8
|
+
min-block-size: var(--ui-height);
|
|
9
|
+
--ui-height: var(--height-input);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// label 结构性样式
|
|
13
|
+
.ui-label {
|
|
14
|
+
font-size: 0.875rem;
|
|
15
|
+
line-height: 1.25rem;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// input 结构性样式
|
|
19
|
+
.ui-input {
|
|
20
|
+
--ui-padding-inline: 0.625rem;
|
|
21
|
+
--ui-addon-height: max(0px, calc(var(--ui-height) - 0.5rem));
|
|
22
|
+
align-items: center;
|
|
23
|
+
inline-size: 100%;
|
|
24
|
+
min-inline-size: 0;
|
|
25
|
+
padding-inline: var(--ui-padding-inline);
|
|
26
|
+
padding-block: 0;
|
|
27
|
+
|
|
28
|
+
border: 1px solid var(--color-border, transparent);
|
|
29
|
+
border-radius: var(--ui-radius, 0);
|
|
30
|
+
|
|
31
|
+
--ui-display-color: var(--ui-color);
|
|
32
|
+
|
|
33
|
+
outline: 0 solid transparent;
|
|
34
|
+
|
|
35
|
+
transition:
|
|
36
|
+
border-color var(--transition),
|
|
37
|
+
box-shadow var(--transition),
|
|
38
|
+
background-color var(--transition),
|
|
39
|
+
outline var(--transition);
|
|
40
|
+
|
|
41
|
+
&:is(:focus-visible, select:open, select:focus),
|
|
42
|
+
&:has(> input:focus-visible),
|
|
43
|
+
&:has(> .ui-pill .ui-pill-button:focus-visible) {
|
|
44
|
+
outline-width: 4px;
|
|
45
|
+
border-color: var(--ui-display-color);
|
|
46
|
+
outline-color: oklch(from var(--ui-display-color) l c h / 20%);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.ui-input > input:not([type="hidden"]) {
|
|
51
|
+
flex: 1 1 0%;
|
|
52
|
+
align-self: stretch;
|
|
53
|
+
inline-size: 0;
|
|
54
|
+
min-inline-size: 0;
|
|
55
|
+
min-block-size: 0;
|
|
56
|
+
padding-block: 0;
|
|
57
|
+
// padding-inline: var(--ui-padding-inline);
|
|
58
|
+
border: 0;
|
|
59
|
+
border-radius: inherit;
|
|
60
|
+
outline: none;
|
|
61
|
+
background: transparent;
|
|
62
|
+
box-shadow: none;
|
|
63
|
+
color: inherit;
|
|
64
|
+
font: inherit;
|
|
65
|
+
text-align: inherit;
|
|
66
|
+
cursor: inherit;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.ui-input[data-width="icon"] {
|
|
70
|
+
--ui-padding-inline: 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 前后修饰组按 DOM 顺序排列,输入区域收缩时修饰内容保持原有尺寸。
|
|
74
|
+
.ui-input:has(> .ui-addon) {
|
|
75
|
+
column-gap: 0.5rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.ui-input > .ui-addon {
|
|
79
|
+
display: inline-flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
flex: 0 0 auto;
|
|
82
|
+
gap: 0.25rem;
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
cursor: default;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.ui-input:is([aria-disabled="true"], :has(> input:disabled)) > .ui-addon {
|
|
88
|
+
cursor: not-allowed;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 多值输入保留直接嵌套关系,值标签和编辑区域可自然换行。
|
|
92
|
+
.ui-input:has(> .ui-pill) {
|
|
93
|
+
flex-wrap: wrap;
|
|
94
|
+
gap: 0.25rem;
|
|
95
|
+
padding-block: 0.125rem;
|
|
96
|
+
|
|
97
|
+
> input:not([type="hidden"]) {
|
|
98
|
+
flex: 1 1 4rem;
|
|
99
|
+
align-self: center;
|
|
100
|
+
min-inline-size: min(4rem, 100%);
|
|
101
|
+
block-size: 1.25rem;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
> .ui-addon:last-child {
|
|
105
|
+
margin-inline-start: auto;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// textarea 结构性样式
|
|
110
|
+
textarea.ui-input {
|
|
111
|
+
// 多行编辑区域保留原有布局,不应用单行控件的垂直居中。
|
|
112
|
+
// align-items: normal;
|
|
113
|
+
padding-block: 0.5rem;
|
|
114
|
+
min-block-size: calc(var(--ui-height) + 0.5rem);
|
|
115
|
+
resize: vertical;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// select 结构性样式
|
|
119
|
+
@supports (appearance: base-select) {
|
|
120
|
+
select.ui-input:not([multiple]):not([size]) {
|
|
121
|
+
appearance: base-select;
|
|
122
|
+
align-items: center;
|
|
123
|
+
}
|
|
124
|
+
select.ui-input::picker(select) {
|
|
125
|
+
appearance: base-select;
|
|
126
|
+
border-radius: var(--ui-radius);
|
|
127
|
+
border-color: var(--color-border);
|
|
128
|
+
box-shadow: var(--shadow-sm);
|
|
129
|
+
padding: 0.25rem;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// input 基本外观
|
|
134
|
+
.ui-input {
|
|
135
|
+
--ui-color: var(--color-primary, grey);
|
|
136
|
+
--ui-radius: var(--radius);
|
|
137
|
+
background: var(--color-background, transparent);
|
|
138
|
+
color: var(--color-foreground, currentColor);
|
|
139
|
+
}
|
|
140
|
+
.ui-input:is(
|
|
141
|
+
:disabled,
|
|
142
|
+
[aria-disabled="true"],
|
|
143
|
+
:has(> input:is(:disabled, [aria-disabled="true"]))
|
|
144
|
+
) {
|
|
145
|
+
color: var(--color-disabled);
|
|
146
|
+
background: color-mix(in oklch, var(--color-foreground) 2%, var(--color-background));
|
|
147
|
+
cursor: not-allowed;
|
|
148
|
+
}
|
|
149
|
+
.ui-input:is(
|
|
150
|
+
:invalid,
|
|
151
|
+
[aria-invalid="true"],
|
|
152
|
+
[data-warning],
|
|
153
|
+
:has(> input:is(:invalid, [aria-invalid="true"], [data-warning]))
|
|
154
|
+
) {
|
|
155
|
+
border-color: var(--ui-display-color);
|
|
156
|
+
}
|
|
157
|
+
.ui-input:is([data-warning], :has(> input[data-warning])) {
|
|
158
|
+
--ui-display-color: var(--color-warning);
|
|
159
|
+
}
|
|
160
|
+
.ui-input:is(:invalid, [aria-invalid="true"], :has(> input:is(:invalid, [aria-invalid="true"]))) {
|
|
161
|
+
--ui-display-color: var(--color-error);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.ui-input[data-variant="filled"] {
|
|
165
|
+
border-color: transparent;
|
|
166
|
+
background: var(--color-input-fill);
|
|
167
|
+
|
|
168
|
+
&:is(
|
|
169
|
+
:invalid,
|
|
170
|
+
[aria-invalid="true"],
|
|
171
|
+
[data-warning],
|
|
172
|
+
:has(> input:is(:invalid, [aria-invalid="true"], [data-warning]))
|
|
173
|
+
) {
|
|
174
|
+
border-color: var(--ui-display-color);
|
|
175
|
+
}
|
|
176
|
+
&:is(:disabled, [aria-disabled="true"], :has(> input:is(:disabled, [aria-disabled="true"]))) {
|
|
177
|
+
background: oklch(from var(--color-input-fill) l c h / 50%);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.ui-input[data-variant="unstyled"] {
|
|
182
|
+
--ui-padding-inline: 0;
|
|
183
|
+
padding-inline: 0;
|
|
184
|
+
|
|
185
|
+
border: 0 none transparent;
|
|
186
|
+
outline: 0 none transparent;
|
|
187
|
+
background: transparent;
|
|
188
|
+
|
|
189
|
+
&:is(
|
|
190
|
+
:invalid,
|
|
191
|
+
[aria-invalid="true"],
|
|
192
|
+
[data-warning],
|
|
193
|
+
:has(> input:is(:invalid, [aria-invalid="true"], [data-warning]))
|
|
194
|
+
) {
|
|
195
|
+
color: var(--ui-display-color);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.ui-input :where(input:not([type="hidden"]), textarea, select) {
|
|
200
|
+
display: inline-flex;
|
|
201
|
+
font-size: 0.875rem;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
@layer components.ui {
|
|
2
|
+
.ui-label-wrapper {
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
gap: 0.5rem;
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
min-inline-size: 0;
|
|
8
|
+
max-inline-size: 100%;
|
|
9
|
+
margin: 0;
|
|
10
|
+
vertical-align: middle;
|
|
11
|
+
font-family: inherit;
|
|
12
|
+
font-size: var(--font-size-input, 0.875rem);
|
|
13
|
+
color: var(--color-foreground);
|
|
14
|
+
|
|
15
|
+
> :is(.ui-checkbox, .ui-radio, .ui-switch, input[type="checkbox"], input[type="radio"]) {
|
|
16
|
+
flex-shrink: 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
> .ui-label {
|
|
20
|
+
min-inline-size: 0;
|
|
21
|
+
font-family: inherit;
|
|
22
|
+
font-size: inherit;
|
|
23
|
+
line-height: 1.5;
|
|
24
|
+
color: inherit;
|
|
25
|
+
overflow-wrap: anywhere;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.ui-label-wrapper:has(
|
|
30
|
+
> :not(.ui-label):is(:disabled, [aria-disabled="true"]),
|
|
31
|
+
> :not(.ui-label) > :is(input, select, textarea):is(:disabled, [aria-disabled="true"]),
|
|
32
|
+
> .ui-switch input:is(:disabled, [aria-disabled="true"])
|
|
33
|
+
)
|
|
34
|
+
> .ui-label {
|
|
35
|
+
color: var(--color-disabled);
|
|
36
|
+
}
|
|
37
|
+
}
|