@attrus-ds/core 1.0.13 → 1.0.17
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/dist/css/components/buttons.css +46 -15
- package/dist/css/components/callout.css +45 -8
- package/dist/css/components/combobox.css +36 -23
- package/dist/css/components/inputs.css +55 -22
- package/dist/css/components/overlays.css +1 -1
- package/dist/css/components/primitives.css +68 -0
- package/dist/css/components/sidebar.css +33 -0
- package/dist/css/components/smart-search.css +56 -23
- package/dist/css/components/status-pills.css +6 -6
- package/dist/css/components/table.css +602 -36
- package/dist/css/components/tabs.css +1 -0
- package/dist/css/components/toggle-chip.css +2 -2
- package/dist/css/components/topbar.css +180 -20
- package/dist/css/tokens.css +50 -10
- package/dist/react/Button/Button.d.ts +10 -2
- package/dist/react/Button/Button.tsx +12 -4
- package/dist/react/Combobox/Combobox.d.ts +5 -0
- package/dist/react/Combobox/Combobox.tsx +26 -4
- package/dist/react/DataTable/DataTable.d.ts +91 -0
- package/dist/react/DataTable/DataTable.tsx +413 -35
- package/dist/react/SearchPill/SearchPill.d.ts +3 -0
- package/dist/react/SearchPill/SearchPill.tsx +6 -1
- package/dist/react/SmartSearch/SmartSearch.d.ts +4 -0
- package/dist/react/SmartSearch/SmartSearch.tsx +14 -1
- package/package.json +3 -2
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
flex-shrink: 0;
|
|
40
40
|
|
|
41
41
|
/* Box — default size = md (40px tall) */
|
|
42
|
-
height:
|
|
42
|
+
height: var(--control-h-md);
|
|
43
43
|
padding: 0 var(--space-5);
|
|
44
|
-
border:
|
|
44
|
+
border: var(--border-hairline) solid transparent;
|
|
45
45
|
border-radius: var(--radius-pill);
|
|
46
46
|
|
|
47
47
|
/* Type */
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
2. SIZES
|
|
94
94
|
---------------------------------------------------------------- */
|
|
95
95
|
.btn-xs {
|
|
96
|
-
height:
|
|
96
|
+
height: var(--control-h-xs);
|
|
97
97
|
padding: 0 var(--space-3);
|
|
98
98
|
font-size: var(--fs-sm);
|
|
99
99
|
gap: var(--space-1);
|
|
@@ -101,16 +101,16 @@
|
|
|
101
101
|
.btn-xs > i, .btn-xs > svg, .btn-xs > svg { width: var(--icon-xs); height: var(--icon-xs); }
|
|
102
102
|
|
|
103
103
|
.btn-sm {
|
|
104
|
-
height:
|
|
104
|
+
height: var(--control-h-sm);
|
|
105
105
|
padding: 0 var(--space-4);
|
|
106
106
|
font-size: var(--fs-md);
|
|
107
107
|
}
|
|
108
108
|
.btn-sm > i, .btn-sm > svg, .btn-sm > svg { width: var(--icon-xs); height: var(--icon-xs); }
|
|
109
109
|
|
|
110
|
-
/* default = md
|
|
110
|
+
/* default = md — .btn reads var(--control-h-md) */
|
|
111
111
|
|
|
112
112
|
.btn-lg {
|
|
113
|
-
height:
|
|
113
|
+
height: var(--control-h-lg);
|
|
114
114
|
padding: 0 var(--space-6);
|
|
115
115
|
font-size: var(--fs-base);
|
|
116
116
|
gap: var(--space-3);
|
|
@@ -122,10 +122,10 @@
|
|
|
122
122
|
3. ICON-ONLY MODIFIER
|
|
123
123
|
---------------------------------------------------------------- */
|
|
124
124
|
.btn-icon { padding: 0; aspect-ratio: 1 / 1; }
|
|
125
|
-
.btn-icon.btn-xs { width:
|
|
126
|
-
.btn-icon.btn-sm { width:
|
|
127
|
-
.btn-icon { width:
|
|
128
|
-
.btn-icon.btn-lg { width:
|
|
125
|
+
.btn-icon.btn-xs { width: var(--control-h-xs); }
|
|
126
|
+
.btn-icon.btn-sm { width: var(--control-h-sm); }
|
|
127
|
+
.btn-icon { width: var(--control-h-md); }
|
|
128
|
+
.btn-icon.btn-lg { width: var(--control-h-lg); }
|
|
129
129
|
|
|
130
130
|
|
|
131
131
|
/* ----------------------------------------------------------------
|
|
@@ -199,6 +199,21 @@
|
|
|
199
199
|
.btn-danger:focus-visible { box-shadow: var(--shadow-focus-danger); }
|
|
200
200
|
|
|
201
201
|
|
|
202
|
+
/* DANGER GHOST — destructive inside chrome (toolbars, row actions).
|
|
203
|
+
The outline variant carries a border and its own surface, which is right for
|
|
204
|
+
the primary action of a dialog but too heavy for a bar: two framed buttons on
|
|
205
|
+
a 52px toolbar outweigh the data below them. This keeps the danger colour as
|
|
206
|
+
the only signal. */
|
|
207
|
+
.btn-danger-ghost {
|
|
208
|
+
background: transparent;
|
|
209
|
+
color: var(--color-danger-500);
|
|
210
|
+
border-color: transparent;
|
|
211
|
+
}
|
|
212
|
+
.btn-danger-ghost:hover { background: var(--color-feedback-danger-background); color: var(--color-danger-600); }
|
|
213
|
+
.btn-danger-ghost:active { background: color-mix(in srgb, var(--color-danger-500) 16%, transparent); }
|
|
214
|
+
.btn-danger-ghost:focus-visible { box-shadow: var(--shadow-focus-danger); }
|
|
215
|
+
.btn-danger-ghost[disabled] { color: var(--color-foreground-disabled); background: transparent; }
|
|
216
|
+
|
|
202
217
|
/* DANGER OUTLINE — destructive contextual */
|
|
203
218
|
.btn-danger-outline {
|
|
204
219
|
background: var(--color-background-surface);
|
|
@@ -264,21 +279,37 @@
|
|
|
264
279
|
.btn.is-loading::after {
|
|
265
280
|
content: "";
|
|
266
281
|
position: absolute;
|
|
267
|
-
top:
|
|
282
|
+
/* Centred with calc, not top:50% + negative margin: a blanket `margin: 0`
|
|
283
|
+
from page or reset CSS silently kills the offset and drops the spinner
|
|
284
|
+
below centre, while the percentage still resolves. calc cannot be reset
|
|
285
|
+
away, and it avoids transform, which the spin animation owns. */
|
|
286
|
+
/* Optical, not geometric: the ring's transparent top segment shifts the
|
|
287
|
+
visible arc's mass down-left of the box centre, so the box is nudged back
|
|
288
|
+
by half a border width. box-sizing keeps the 2px border inside the
|
|
289
|
+
declared size, otherwise the circle is 18px in a 14px slot. */
|
|
290
|
+
top: calc(50% - (var(--icon-sm) / 2) - 1px);
|
|
291
|
+
left: calc(50% - (var(--icon-sm) / 2) + 1px);
|
|
292
|
+
margin: 0;
|
|
268
293
|
width: var(--icon-sm);
|
|
269
294
|
height: var(--icon-sm);
|
|
270
|
-
|
|
295
|
+
box-sizing: border-box;
|
|
271
296
|
border: 2px solid currentColor;
|
|
272
297
|
border-top-color: transparent;
|
|
273
298
|
border-radius: var(--radius-pill);
|
|
274
|
-
animation: btn-spin
|
|
275
|
-
/* Re-introduce currentColor where the variant set it; ::after inherits the parent's color BEFORE the !important override */
|
|
299
|
+
animation: btn-spin 0.6s linear infinite;
|
|
276
300
|
color: var(--color-foreground-default);
|
|
277
301
|
}
|
|
302
|
+
|
|
278
303
|
.btn-primary.is-loading::after,
|
|
279
304
|
.btn-danger.is-loading::after,
|
|
280
305
|
.btn-success.is-loading::after { color: var(--color-pure-white); }
|
|
281
306
|
.btn-tertiary.is-loading::after { color: var(--color-foreground-default); }
|
|
307
|
+
/* On a dark surface the spinner has to invert with the button: the default ring
|
|
308
|
+
is --color-foreground-default, which on an inverse-tinted fill is nearly the
|
|
309
|
+
same value and the spinner disappears. */
|
|
310
|
+
.btn.on-inverse.is-loading::after,
|
|
311
|
+
.btn-inverse.is-loading::after { color: var(--color-foreground-inverse-default); }
|
|
312
|
+
.btn-inverse.is-loading::after { color: var(--color-brand-text-on-light); }
|
|
282
313
|
|
|
283
314
|
@keyframes btn-spin {
|
|
284
315
|
to { transform: rotate(360deg); }
|
|
@@ -296,6 +327,6 @@
|
|
|
296
327
|
}
|
|
297
328
|
.btn-group .btn:first-child { border-top-left-radius: var(--radius-pill); border-bottom-left-radius: var(--radius-pill); }
|
|
298
329
|
.btn-group .btn:last-child { border-top-right-radius: var(--radius-pill); border-bottom-right-radius: var(--radius-pill); }
|
|
299
|
-
.btn-group .btn + .btn { margin-left: -
|
|
330
|
+
.btn-group .btn + .btn { margin-left: calc(var(--border-hairline) * -1); }
|
|
300
331
|
.btn-group .btn:focus-visible,
|
|
301
332
|
.btn-group .btn:hover { z-index: 1; }
|
|
@@ -44,13 +44,34 @@
|
|
|
44
44
|
gap: var(--space-3);
|
|
45
45
|
padding: var(--space-4) var(--space-5);
|
|
46
46
|
border-radius: var(--radius-md);
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
/* Quieter by default: the tint carries the tone and the hairline just closes
|
|
48
|
+
the shape. 22% read as a hard rule around a note that should recede — the
|
|
49
|
+
icon and the copy are what should be seen first. */
|
|
50
|
+
background: color-mix(in srgb, var(--callout-tint) 5%, var(--color-background-surface));
|
|
51
|
+
border: 1px solid color-mix(in srgb, var(--callout-tint) 12%, transparent);
|
|
49
52
|
}
|
|
50
53
|
|
|
54
|
+
.callout-icon > svg,
|
|
55
|
+
.callout-icon > i,
|
|
56
|
+
.callout > svg,
|
|
57
|
+
.callout > i {
|
|
58
|
+
/* Lucide emits a 24px svg with no intrinsic size; without this it overflows
|
|
59
|
+
the 18px slot whether it IS the slot or sits inside one. */
|
|
60
|
+
width: 18px;
|
|
61
|
+
height: 18px;
|
|
62
|
+
flex-shrink: 0;
|
|
63
|
+
}
|
|
64
|
+
.callout-sm .callout-icon > svg,
|
|
65
|
+
.callout-sm .callout-icon > i { width: 16px; height: 16px; }
|
|
66
|
+
|
|
51
67
|
.callout > i,
|
|
52
68
|
.callout > svg,
|
|
53
69
|
.callout-icon {
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
align-self: flex-start;
|
|
72
|
+
/* Optical: pull the 18px glyph onto the cap line of the first row of text
|
|
73
|
+
(line-height 15.6px), rather than centring it on the whole block. */
|
|
74
|
+
margin-top: -1px;
|
|
54
75
|
width: 18px;
|
|
55
76
|
height: 18px;
|
|
56
77
|
flex-shrink: 0;
|
|
@@ -112,13 +133,22 @@
|
|
|
112
133
|
.btn-ghost → picks up the tone for text/hover.
|
|
113
134
|
.btn-danger / .btn-primary stay canonical (solid) for strong/destructive. */
|
|
114
135
|
.callout-actions .btn-secondary {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
136
|
+
/* Kept in step with the box (5% / 12%): at 14% fill and a 34% border the
|
|
137
|
+
action was louder than the note holding it. The fill is what makes the
|
|
138
|
+
button findable here; the border only needs to close the shape. */
|
|
139
|
+
background: color-mix(in srgb, var(--callout-tint) 10%, var(--color-background-surface));
|
|
140
|
+
border-color: color-mix(in srgb, var(--callout-tint) 18%, transparent);
|
|
141
|
+
/* Label reads as text, not as tone. It used to inherit --callout-fg (sky by
|
|
142
|
+
default), which put a blue label at ~4.3:1 on a 10% fill — the box already
|
|
143
|
+
says what kind of note this is, so the action only has to be readable.
|
|
144
|
+
Tonal variants below re-assert their own fg, where the colour IS the message. */
|
|
145
|
+
color: var(--color-foreground-default);
|
|
118
146
|
}
|
|
119
147
|
.callout-actions .btn-secondary:hover {
|
|
120
|
-
|
|
121
|
-
|
|
148
|
+
/* Hover moves the fill, not the border — a border that jumps 18→50% reads as
|
|
149
|
+
the control changing shape rather than responding. */
|
|
150
|
+
background: color-mix(in srgb, var(--callout-tint) 16%, var(--color-background-surface));
|
|
151
|
+
border-color: color-mix(in srgb, var(--callout-tint) 28%, transparent);
|
|
122
152
|
}
|
|
123
153
|
.callout-actions .btn-ghost { color: var(--callout-fg); }
|
|
124
154
|
.callout-actions .btn-ghost:hover { background: color-mix(in srgb, var(--callout-tint) 12%, transparent); color: var(--callout-fg); }
|
|
@@ -150,6 +180,12 @@
|
|
|
150
180
|
.callout-danger { --callout-tint: var(--color-danger-500); --callout-fg: var(--color-feedback-danger-foreground); }
|
|
151
181
|
.callout-neutral { --callout-tint: var(--color-neutral-500); --callout-fg: var(--color-foreground-muted); }
|
|
152
182
|
|
|
183
|
+
/* Tonal actions: on warn / success / danger the hue is the message, so the label
|
|
184
|
+
takes the tone back. Neutral and info keep the plain text colour. */
|
|
185
|
+
.callout-warn .callout-actions .btn-secondary,
|
|
186
|
+
.callout-success .callout-actions .btn-secondary,
|
|
187
|
+
.callout-danger .callout-actions .btn-secondary { color: var(--callout-fg); }
|
|
188
|
+
|
|
153
189
|
/* ---- compact size ---- */
|
|
154
190
|
.callout-sm { padding: var(--space-3) var(--space-4); gap: var(--space-2-5); }
|
|
155
191
|
.callout-sm > i, .callout-sm > svg, .callout-sm .callout-icon { width: 16px; height: 16px; }
|
|
@@ -172,7 +208,7 @@
|
|
|
172
208
|
.callout-bar .callout-title { font-size: var(--fs-sm); }
|
|
173
209
|
|
|
174
210
|
/* rounded alert bar (when not edge-to-edge) */
|
|
175
|
-
.callout-bar.callout-rounded { border-radius: var(--radius-md); border-left-width: 3px; border-top: 1px solid color-mix(in srgb, var(--callout-tint)
|
|
211
|
+
.callout-bar.callout-rounded { border-radius: var(--radius-md); border-left-width: 3px; border-top: 1px solid color-mix(in srgb, var(--callout-tint) 12%, transparent); border-right: 1px solid color-mix(in srgb, var(--callout-tint) 12%, transparent); border-bottom: 1px solid color-mix(in srgb, var(--callout-tint) 12%, transparent); }
|
|
176
212
|
|
|
177
213
|
/* ---- Inline: content-sized, single-line (badges, hero notices) ---- */
|
|
178
214
|
.callout-inline { display: inline-flex; width: auto; align-items: center; }
|
|
@@ -194,3 +230,4 @@
|
|
|
194
230
|
.callout-danger.on-inverse { --callout-fg-inv: var(--color-danger-300); }
|
|
195
231
|
.callout-info.on-inverse { --callout-fg-inv: var(--color-brand-accent-300); }
|
|
196
232
|
.callout-neutral.on-inverse { --callout-fg-inv: var(--color-foreground-inverse-muted); }
|
|
233
|
+
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
align-items: center;
|
|
18
18
|
gap: var(--space-3);
|
|
19
19
|
width: 100%;
|
|
20
|
-
height:
|
|
20
|
+
height: var(--control-h-md);
|
|
21
21
|
padding: 0 var(--space-3) 0 var(--space-4);
|
|
22
22
|
background: var(--color-background-surface);
|
|
23
|
-
border:
|
|
24
|
-
border-radius: var(--
|
|
23
|
+
border: var(--border-hairline) solid var(--color-border-default);
|
|
24
|
+
border-radius: var(--shape-control);
|
|
25
25
|
font-family: var(--font-sans);
|
|
26
26
|
font-size: var(--fs-sm);
|
|
27
27
|
font-weight: var(--w-regular);
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
font-weight: var(--w-regular);
|
|
54
54
|
}
|
|
55
55
|
.combobox-chevron {
|
|
56
|
-
width:
|
|
56
|
+
width: var(--icon-sm); height: var(--icon-sm);
|
|
57
57
|
stroke-width: var(--icon-stroke);
|
|
58
58
|
color: var(--color-foreground-muted);
|
|
59
59
|
flex-shrink: 0;
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
|
|
64
64
|
/* Leading icon variant */
|
|
65
65
|
.combobox-trigger-icon {
|
|
66
|
-
width:
|
|
66
|
+
width: var(--icon-sm); height: var(--icon-sm);
|
|
67
67
|
stroke-width: var(--icon-stroke);
|
|
68
68
|
color: var(--color-foreground-secondary);
|
|
69
69
|
flex-shrink: 0;
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
|
|
72
72
|
/* Avatar inside trigger (counterparty selector pattern) */
|
|
73
73
|
.combobox-avatar {
|
|
74
|
-
width:
|
|
74
|
+
width: var(--control-h-xs); height: var(--control-h-xs);
|
|
75
75
|
border-radius: var(--radius-pill);
|
|
76
76
|
background: var(--color-brand-subtle-solid);
|
|
77
77
|
color: var(--color-brand-text-on-light);
|
|
@@ -87,14 +87,27 @@
|
|
|
87
87
|
position: absolute;
|
|
88
88
|
top: calc(100% + var(--space-2));
|
|
89
89
|
left: 0;
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
/* Trigger width is the FLOOR, not the ceiling. Pinning both edges made the
|
|
91
|
+
popover exactly as wide as the trigger, so a 50px chip crushed its own
|
|
92
|
+
options with nothing to rescue them. min-width keeps the form-field look
|
|
93
|
+
(popover aligns with the field) while letting content set the real width. */
|
|
94
|
+
min-width: 100%;
|
|
95
|
+
width: max-content;
|
|
96
|
+
max-width: min(420px, calc(100vw - var(--space-6)));
|
|
97
|
+
/* Layer 9200 — ABOVE the scrim (9000) and the drawer panel (9100). A combobox
|
|
98
|
+
opened inside a drawer or modal must render over that surface, not behind
|
|
99
|
+
its veil; at 7000 the popover was trapped under the scrim in exactly the
|
|
100
|
+
case where a picker is most needed. Tooltip 8000 sits below for the same
|
|
101
|
+
reason it sits below a modal: it annotates, it does not collect input. */
|
|
102
|
+
z-index: 9200;
|
|
92
103
|
background: var(--color-background-surface);
|
|
93
|
-
border:
|
|
104
|
+
border: var(--border-hairline) solid var(--color-border-default);
|
|
94
105
|
border-radius: var(--radius-md);
|
|
95
106
|
box-shadow: var(--shadow-xl);
|
|
96
107
|
display: none;
|
|
97
108
|
flex-direction: column;
|
|
109
|
+
/* ~7 rows before the list scrolls. Past that the user scans instead of
|
|
110
|
+
reading, and the popover starts covering the field it belongs to. */
|
|
98
111
|
max-height: 320px;
|
|
99
112
|
}
|
|
100
113
|
.combobox.is-open .combobox-popover { display: flex; }
|
|
@@ -103,16 +116,16 @@
|
|
|
103
116
|
.combobox-search {
|
|
104
117
|
position: relative;
|
|
105
118
|
padding: var(--space-3);
|
|
106
|
-
border-bottom:
|
|
119
|
+
border-bottom: var(--border-hairline) solid var(--color-border-subtle);
|
|
107
120
|
flex-shrink: 0;
|
|
108
121
|
}
|
|
109
122
|
.combobox-search input {
|
|
110
123
|
width: 100%;
|
|
111
124
|
box-sizing: border-box;
|
|
112
|
-
height:
|
|
125
|
+
height: var(--control-h-sm);
|
|
113
126
|
padding: 0 var(--space-3) 0 var(--space-7);
|
|
114
127
|
background: var(--color-background-canvas);
|
|
115
|
-
border:
|
|
128
|
+
border: var(--border-hairline) solid var(--color-border-default);
|
|
116
129
|
border-radius: var(--radius-xs);
|
|
117
130
|
font-family: var(--font-sans);
|
|
118
131
|
font-size: var(--fs-sm);
|
|
@@ -129,7 +142,7 @@
|
|
|
129
142
|
left: calc(var(--space-3) + var(--space-2));
|
|
130
143
|
top: 50%;
|
|
131
144
|
transform: translateY(-50%);
|
|
132
|
-
width:
|
|
145
|
+
width: var(--icon-xs); height: var(--icon-xs);
|
|
133
146
|
stroke-width: var(--icon-stroke);
|
|
134
147
|
color: var(--color-foreground-muted);
|
|
135
148
|
pointer-events: none;
|
|
@@ -203,7 +216,7 @@
|
|
|
203
216
|
}
|
|
204
217
|
|
|
205
218
|
.combobox-option-check {
|
|
206
|
-
width:
|
|
219
|
+
width: var(--icon-sm); height: var(--icon-sm);
|
|
207
220
|
stroke-width: 2.4;
|
|
208
221
|
color: var(--color-foreground-accent);
|
|
209
222
|
flex-shrink: 0;
|
|
@@ -236,7 +249,7 @@
|
|
|
236
249
|
/* Footer (e.g. "Create new …") */
|
|
237
250
|
.combobox-footer {
|
|
238
251
|
padding: var(--space-2);
|
|
239
|
-
border-top:
|
|
252
|
+
border-top: var(--border-hairline) solid var(--color-border-subtle);
|
|
240
253
|
flex-shrink: 0;
|
|
241
254
|
}
|
|
242
255
|
.combobox-footer-action {
|
|
@@ -249,7 +262,7 @@
|
|
|
249
262
|
margin-top: 0;
|
|
250
263
|
width: calc(100% - var(--space-4));
|
|
251
264
|
background: var(--color-background-surface);
|
|
252
|
-
border:
|
|
265
|
+
border: var(--border-hairline) solid var(--color-border-default);
|
|
253
266
|
border-radius: var(--radius-sm);
|
|
254
267
|
font-family: var(--font-sans);
|
|
255
268
|
font-size: var(--fs-sm);
|
|
@@ -273,13 +286,13 @@
|
|
|
273
286
|
}
|
|
274
287
|
.combobox-footer-action svg,
|
|
275
288
|
.combobox-footer-action i {
|
|
276
|
-
width:
|
|
277
|
-
height:
|
|
289
|
+
width: var(--icon-xs);
|
|
290
|
+
height: var(--icon-xs);
|
|
278
291
|
stroke-width: var(--icon-stroke);
|
|
279
292
|
color: var(--color-foreground-secondary);
|
|
280
293
|
}
|
|
281
294
|
.combobox-footer-action i, .combobox-footer-action svg {
|
|
282
|
-
width:
|
|
295
|
+
width: var(--icon-sm); height: var(--icon-sm);
|
|
283
296
|
stroke-width: var(--icon-stroke);
|
|
284
297
|
}
|
|
285
298
|
|
|
@@ -305,15 +318,15 @@
|
|
|
305
318
|
foot meta (below list). Exposed via Combobox props
|
|
306
319
|
`filters` / `meta` / `footMeta`.
|
|
307
320
|
---------------------------------------------------------------- */
|
|
308
|
-
.combobox-filters { display: flex; flex-wrap: wrap; gap: var(--space-2); padding: var(--space-3); border-bottom:
|
|
309
|
-
.combobox-filter-chip { display: inline-flex; align-items: center; gap: var(--space-2); padding:
|
|
321
|
+
.combobox-filters { display: flex; flex-wrap: wrap; gap: var(--space-2); padding: var(--space-3); border-bottom: var(--border-hairline) solid var(--color-border-subtle); }
|
|
322
|
+
.combobox-filter-chip { display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-3); background: var(--color-background-surface); border: var(--border-hairline) solid var(--color-border-default); border-radius: var(--radius-pill); font-family: var(--font-sans); font-size: var(--fs-xs); font-weight: var(--w-regular); color: var(--color-foreground-secondary); cursor: pointer; transition: var(--transition-fast); }
|
|
310
323
|
.combobox-filter-chip:hover { background: var(--color-background-hover); border-color: var(--color-border-strong); color: var(--color-foreground-default); }
|
|
311
324
|
.combobox-filter-chip.is-active { background: var(--color-background-selected); border-color: var(--color-brand-border); color: var(--color-foreground-brand); font-weight: var(--w-regular); }
|
|
312
325
|
.combobox-filter-chip .count { font-family: var(--font-mono);
|
|
313
326
|
font-size-adjust: var(--font-mono-adjust); font-size: var(--fs-xxs); color: var(--color-foreground-muted); }
|
|
314
327
|
.combobox-filter-chip.is-active .count { color: var(--color-foreground-brand); }
|
|
315
|
-
.combobox-meta { padding: var(--space-2) var(--space-3) var(--space-1); font-size: var(--fs-xxs); font-weight: var(--w-regular); letter-spacing: var(--ls-caps); text-transform: uppercase; color: var(--color-foreground-muted); border-bottom:
|
|
316
|
-
.combobox-foot-meta { padding: var(--space-2) var(--space-3); font-size: var(--fs-xs); color: var(--color-foreground-muted); border-top:
|
|
328
|
+
.combobox-meta { padding: var(--space-2) var(--space-3) var(--space-1); font-size: var(--fs-xxs); font-weight: var(--w-regular); letter-spacing: var(--ls-caps); text-transform: uppercase; color: var(--color-foreground-muted); border-bottom: var(--border-hairline) solid var(--color-border-subtle); }
|
|
329
|
+
.combobox-foot-meta { padding: var(--space-2) var(--space-3); font-size: var(--fs-xs); color: var(--color-foreground-muted); border-top: var(--border-hairline) solid var(--color-border-subtle); }
|
|
317
330
|
|
|
318
331
|
/* Reduced motion */
|
|
319
332
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
/* Box — default size = md (40px tall) */
|
|
101
101
|
width: 100%;
|
|
102
102
|
box-sizing: border-box;
|
|
103
|
-
height:
|
|
103
|
+
height: var(--control-h-md);
|
|
104
104
|
padding: 0 var(--space-4);
|
|
105
105
|
border: 1px solid var(--color-border-default);
|
|
106
106
|
border-radius: var(--shape-control);
|
|
@@ -170,13 +170,13 @@ textarea.input {
|
|
|
170
170
|
3. SIZES
|
|
171
171
|
---------------------------------------------------------------- */
|
|
172
172
|
.input-sm {
|
|
173
|
-
height:
|
|
173
|
+
height: var(--control-h-sm);
|
|
174
174
|
padding: 0 var(--space-3);
|
|
175
175
|
font-size: var(--fs-sm);
|
|
176
176
|
border-radius: var(--radius-xs);
|
|
177
177
|
}
|
|
178
178
|
.input-lg {
|
|
179
|
-
height:
|
|
179
|
+
height: var(--control-h-lg);
|
|
180
180
|
padding: 0 var(--space-5);
|
|
181
181
|
font-size: var(--fs-lg);
|
|
182
182
|
border-radius: var(--radius-md);
|
|
@@ -239,7 +239,7 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
239
239
|
display: flex;
|
|
240
240
|
align-items: stretch;
|
|
241
241
|
width: 100%;
|
|
242
|
-
height:
|
|
242
|
+
height: var(--control-h-md);
|
|
243
243
|
border: 1px solid var(--color-border-default);
|
|
244
244
|
border-radius: var(--shape-control);
|
|
245
245
|
background: var(--color-background-surface);
|
|
@@ -289,7 +289,7 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
289
289
|
display: flex;
|
|
290
290
|
align-items: center;
|
|
291
291
|
gap: var(--space-3);
|
|
292
|
-
height:
|
|
292
|
+
height: var(--control-h-md);
|
|
293
293
|
padding: 0 var(--space-4);
|
|
294
294
|
border: 1px solid var(--color-border-default);
|
|
295
295
|
border-radius: var(--radius-pill);
|
|
@@ -299,6 +299,17 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
299
299
|
box-shadow var(--transition-fast),
|
|
300
300
|
background var(--transition-fast);
|
|
301
301
|
}
|
|
302
|
+
/* Dense variant. The default 40px field is right on a page; inside a 52px
|
|
303
|
+
toolbar beside 32px buttons it reads as the odd one out, and there was no
|
|
304
|
+
modifier to reach for — so callers either lived with the mismatch or set a
|
|
305
|
+
height inline. */
|
|
306
|
+
.pill-search-sm {
|
|
307
|
+
height: var(--control-h-sm);
|
|
308
|
+
padding: 0 var(--space-3);
|
|
309
|
+
gap: var(--space-2);
|
|
310
|
+
}
|
|
311
|
+
.pill-search-sm > input { font-size: var(--fs-sm); }
|
|
312
|
+
.pill-search-sm > i, .pill-search-sm > svg { width: var(--icon-xs); height: var(--icon-xs); }
|
|
302
313
|
.pill-search:hover { border-color: var(--color-border-strong); }
|
|
303
314
|
.pill-search:focus-within {
|
|
304
315
|
border-color: var(--color-border-focus);
|
|
@@ -363,10 +374,10 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
363
374
|
.chips {
|
|
364
375
|
display: flex;
|
|
365
376
|
flex-wrap: wrap;
|
|
366
|
-
gap:
|
|
377
|
+
gap: var(--space-2-5);
|
|
367
378
|
align-items: center;
|
|
368
|
-
min-height:
|
|
369
|
-
padding:
|
|
379
|
+
min-height: var(--control-h-md);
|
|
380
|
+
padding: var(--space-2-5) var(--space-3);
|
|
370
381
|
border: 1px solid var(--color-border-default);
|
|
371
382
|
border-radius: var(--shape-control);
|
|
372
383
|
background: var(--color-background-surface);
|
|
@@ -382,7 +393,7 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
382
393
|
.chip {
|
|
383
394
|
display: inline-flex;
|
|
384
395
|
align-items: center;
|
|
385
|
-
gap:
|
|
396
|
+
gap: var(--space-2);
|
|
386
397
|
height: 26px;
|
|
387
398
|
padding: 0 4px 0 10px;
|
|
388
399
|
background: var(--color-background-subtle);
|
|
@@ -417,7 +428,7 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
417
428
|
.chip-x:hover { background: var(--color-border-default); color: var(--color-foreground-default); }
|
|
418
429
|
.chips > input {
|
|
419
430
|
flex: 1;
|
|
420
|
-
min-width:
|
|
431
|
+
min-width: calc(var(--control-h-md) * 2);
|
|
421
432
|
height: 26px;
|
|
422
433
|
border: 0;
|
|
423
434
|
outline: none;
|
|
@@ -464,8 +475,8 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
464
475
|
---------------------------------------------------------------- */
|
|
465
476
|
.otp { display: inline-flex; gap: var(--space-2); }
|
|
466
477
|
.otp > input {
|
|
467
|
-
width:
|
|
468
|
-
height:
|
|
478
|
+
width: var(--control-h-md);
|
|
479
|
+
height: var(--control-h-lg);
|
|
469
480
|
text-align: center;
|
|
470
481
|
font-family: var(--font-mono);
|
|
471
482
|
font-size-adjust: var(--font-mono-adjust);
|
|
@@ -494,6 +505,9 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
494
505
|
}
|
|
495
506
|
.switch-track {
|
|
496
507
|
position: relative;
|
|
508
|
+
/* Literal on purpose: track 20 − thumb 16 − inset 2×2 centres the knob exactly.
|
|
509
|
+
These are internal geometry, not icon sizes — mapping them to --icon-* broke
|
|
510
|
+
the vertical centring when the token did not happen to equal 20px. */
|
|
497
511
|
width: 36px;
|
|
498
512
|
height: 20px;
|
|
499
513
|
border-radius: var(--radius-pill);
|
|
@@ -553,6 +567,7 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
553
567
|
}
|
|
554
568
|
.check input { position: absolute; opacity: 0; pointer-events: none; }
|
|
555
569
|
.check-box {
|
|
570
|
+
position: relative;
|
|
556
571
|
width: 18px;
|
|
557
572
|
height: 18px;
|
|
558
573
|
border: 1px solid var(--color-border-strong);
|
|
@@ -568,12 +583,28 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
568
583
|
border-color var(--transition-fast),
|
|
569
584
|
box-shadow var(--transition-fast);
|
|
570
585
|
}
|
|
571
|
-
.check-box svg { width:
|
|
586
|
+
.check-box svg { width: var(--icon-xs); height: var(--icon-xs); stroke-width: 3; opacity: 0; transform: scale(0.8); transition: opacity var(--transition-fast), transform var(--transition-fast); }
|
|
572
587
|
.check input:checked ~ .check-box {
|
|
573
588
|
background: var(--color-brand-solid);
|
|
574
589
|
border-color: var(--color-brand-solid);
|
|
575
590
|
}
|
|
576
591
|
.check input:checked ~ .check-box svg { opacity: 1; transform: scale(1); }
|
|
592
|
+
/* CSS-drawn tick — used when no <svg> is supplied, so the component does not
|
|
593
|
+
depend on consumer markup to show its own state. */
|
|
594
|
+
.check-box::before {
|
|
595
|
+
content: '';
|
|
596
|
+
position: absolute;
|
|
597
|
+
width: 4px; height: 7.5px;
|
|
598
|
+
border: solid var(--color-foreground-on-solid);
|
|
599
|
+
border-width: 0 1.75px 1.75px 0;
|
|
600
|
+
transform: rotate(45deg) scale(0.8);
|
|
601
|
+
margin-top: -1px;
|
|
602
|
+
opacity: 0;
|
|
603
|
+
transition: opacity var(--transition-fast), transform var(--transition-fast);
|
|
604
|
+
}
|
|
605
|
+
.check input:checked ~ .check-box::before { opacity: 1; transform: rotate(45deg) scale(1); }
|
|
606
|
+
.check-box:has(svg)::before,
|
|
607
|
+
.check input:indeterminate ~ .check-box::before { content: none; }
|
|
577
608
|
.check input:indeterminate ~ .check-box {
|
|
578
609
|
background: var(--color-brand-solid);
|
|
579
610
|
border-color: var(--color-brand-solid);
|
|
@@ -668,7 +699,7 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
668
699
|
justify-content: space-between;
|
|
669
700
|
gap: var(--space-3);
|
|
670
701
|
width: 100%;
|
|
671
|
-
height:
|
|
702
|
+
height: var(--control-h-md);
|
|
672
703
|
padding: 0 var(--space-4);
|
|
673
704
|
border: 1px solid var(--color-border-default);
|
|
674
705
|
border-radius: var(--shape-control);
|
|
@@ -804,8 +835,8 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
804
835
|
|
|
805
836
|
.file-icon {
|
|
806
837
|
position: relative;
|
|
807
|
-
width:
|
|
808
|
-
height:
|
|
838
|
+
width: var(--control-h-md);
|
|
839
|
+
height: var(--control-h-lg);
|
|
809
840
|
display: flex;
|
|
810
841
|
align-items: center;
|
|
811
842
|
justify-content: center;
|
|
@@ -826,7 +857,7 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
826
857
|
transform: translateX(-50%);
|
|
827
858
|
font-family: var(--font-mono);
|
|
828
859
|
font-size-adjust: var(--font-mono-adjust);
|
|
829
|
-
font-size:
|
|
860
|
+
font-size: var(--fs-xxs);
|
|
830
861
|
font-weight: var(--w-bold);
|
|
831
862
|
letter-spacing: 0.04em;
|
|
832
863
|
line-height: 1;
|
|
@@ -840,7 +871,7 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
840
871
|
min-width: 0;
|
|
841
872
|
display: flex;
|
|
842
873
|
flex-direction: column;
|
|
843
|
-
gap:
|
|
874
|
+
gap: var(--space-1);
|
|
844
875
|
}
|
|
845
876
|
.file-name {
|
|
846
877
|
font-family: var(--font-sans);
|
|
@@ -877,7 +908,7 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
877
908
|
.file-status {
|
|
878
909
|
display: inline-flex;
|
|
879
910
|
align-items: center;
|
|
880
|
-
gap:
|
|
911
|
+
gap: var(--space-2);
|
|
881
912
|
padding: 3px var(--space-2);
|
|
882
913
|
border-radius: var(--radius-pill);
|
|
883
914
|
font-family: var(--font-mono);
|
|
@@ -906,7 +937,7 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
906
937
|
transition: background var(--transition-fast), color var(--transition-fast);
|
|
907
938
|
}
|
|
908
939
|
.file-remove:hover { background: var(--color-background-subtle); color: var(--color-foreground-default); }
|
|
909
|
-
.file-remove > svg, .file-remove > i { width:
|
|
940
|
+
.file-remove > svg, .file-remove > i { width: var(--icon-xs); height: var(--icon-xs); }
|
|
910
941
|
|
|
911
942
|
|
|
912
943
|
/* ----------------------------------------------------------------
|
|
@@ -961,8 +992,10 @@ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-
|
|
|
961
992
|
---------------------------------------------------------------- */
|
|
962
993
|
/* Field + selection controls on a permanently-dark surface (hero, onboarding).
|
|
963
994
|
Without these, every dark surface had to override colour inline. */
|
|
964
|
-
|
|
965
|
-
.field
|
|
995
|
+
/* The TSX emits a bare <label> element and a .help class — there is no
|
|
996
|
+
.field-label / .field-help vocabulary, so target what actually ships. */
|
|
997
|
+
.field.on-inverse > label { color: var(--color-foreground-inverse-default); }
|
|
998
|
+
.field.on-inverse .help { color: var(--color-foreground-inverse-muted); }
|
|
966
999
|
|
|
967
1000
|
.switch.on-inverse .switch-track {
|
|
968
1001
|
background: color-mix(in srgb, var(--color-pure-white) 18%, transparent);
|
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
/* Mobile: footer actions stack full-width (doc §Header & Footer, variant 7) */
|
|
285
285
|
@media (max-width: 560px) {
|
|
286
286
|
.drawer-footer { flex-direction: column; align-items: stretch; gap: var(--space-2); padding-bottom: max(var(--space-4), env(safe-area-inset-bottom)); }
|
|
287
|
-
.drawer-footer .btn { width: 100%; justify-content: center; }
|
|
287
|
+
.drawer-footer .btn { width: 100%; justify-content: center; flex-shrink: 0; }
|
|
288
288
|
.drawer-footer-aux { text-align: center; margin-bottom: var(--space-1); }
|
|
289
289
|
}
|
|
290
290
|
|