@livenetworks/ashlar 2.1.12 → 2.2.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 +25 -2
- package/components/COMPONENTS.md +29 -25
- package/components/ln-api-connector/ln-api-connector.schema.json +105 -105
- package/components/ln-chart/ln-chart-dev.scss +0 -4
- package/components/ln-core/README.md +16 -60
- package/components/ln-core/index.js +1 -1
- package/components/ln-core/ln-core.schema.json +120 -120
- package/components/ln-core/query.js +113 -113
- package/components/ln-core/reactive.js +0 -59
- package/components/ln-data-coordinator/README.md +2 -0
- package/components/ln-data-coordinator/ln-data-coordinator.js +1 -1
- package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -196
- package/components/ln-data-coordinator/src/ln-data-coordinator.js +457 -432
- package/components/ln-data-store/ln-data-store.schema.json +92 -92
- package/components/ln-date/ln-date.js +1 -1
- package/components/ln-date/src/ln-date.js +1 -1
- package/components/ln-debug/ln-debug.js +1 -1
- package/components/ln-debug/ln-debug.schema.json +28 -28
- package/components/ln-debug/src/generated-attributes.js +304 -302
- package/components/ln-dropdown/ln-dropdown.schema.json +90 -90
- package/components/ln-external-links/README.md +19 -19
- package/components/ln-external-links/ln-external-links.js +1 -1
- package/components/ln-external-links/ln-external-links.schema.json +9 -0
- package/components/ln-external-links/ln-external-links.scss +12 -0
- package/components/ln-external-links/src/ln-external-links.js +3 -2
- package/components/ln-filter/README.md +2 -1
- package/components/ln-filter/ln-filter.js +1 -1
- package/components/ln-filter/ln-filter.schema.json +157 -169
- package/components/ln-filter/src/ln-filter.js +4 -4
- package/components/ln-form/ln-form.schema.json +57 -57
- package/components/ln-key/ln-key.js +1 -1
- package/components/ln-key/ln-key.schema.json +8 -0
- package/components/ln-key/src/ln-key.js +8 -5
- package/components/ln-obfuscator/ln-obfuscator.js +1 -1
- package/components/ln-obfuscator/ln-obfuscator.schema.json +6 -0
- package/components/ln-obfuscator/src/ln-obfuscator.js +2 -2
- package/components/ln-options/ln-options.schema.json +47 -47
- package/components/ln-popover/ln-popover.schema.json +82 -82
- package/components/ln-progress/ln-progress.schema.json +0 -13
- package/components/ln-search/ln-search.schema.json +111 -111
- package/components/ln-sort/README.md +1 -0
- package/components/ln-sort/ln-sort.js +1 -1
- package/components/ln-sort/ln-sort.schema.json +100 -112
- package/components/ln-sort/src/sort-model.js +1 -6
- package/components/ln-sortable/ln-sortable.schema.json +38 -38
- package/components/ln-sortable/ln-sortable.scss +25 -25
- package/components/ln-table/README.md +8 -1
- package/components/ln-table/ln-table.js +1 -1
- package/components/ln-table/ln-table.schema.json +195 -195
- package/components/ln-table/src/ln-table.js +43 -7
- package/components/ln-table-coordinator/ln-table-coordinator.js +1 -1
- package/components/ln-table-coordinator/ln-table-coordinator.schema.json +18 -0
- package/components/ln-table-coordinator/src/ln-table-coordinator.js +104 -2
- package/components/ln-toast/README.md +1 -1
- package/components/ln-toast/ln-toast.js +1 -1
- package/components/ln-toast/ln-toast.schema.json +6 -0
- package/components/ln-toast/src/ln-toast.js +1 -1
- package/components/ln-validate/README.md +6 -6
- package/components/ln-validate/ln-validate-dev.scss +0 -5
- package/components/ln-validate/ln-validate.js +1 -1
- package/components/ln-validate/ln-validate.schema.json +0 -6
- package/components/ln-validate/src/ln-validate.js +48 -29
- package/ln-ashlar.html-data.json +1824 -1814
- package/package.json +15 -5
- package/theme/README.md +74 -0
- package/theme/base/_global.scss +2 -2
- package/theme/base/_reset.scss +11 -0
- package/theme/components/_app-shell.scss +0 -8
- package/theme/components/_form.scss +6 -6
- package/theme/components/_ln-table.scss +6 -3
- package/theme/components/_scrollbar.scss +5 -4
- package/theme/components/_sortable.scss +19 -19
- package/theme/components/_stat-card.scss +1 -0
- package/theme/components/_table.scss +11 -35
- package/theme/config/_density.scss +110 -65
- package/theme/config/_palette.scss +71 -49
- package/theme/config/_theme.scss +27 -31
- package/theme/config/_tokens.scss +28 -26
- package/theme/config/mixins/_app-shell.scss +17 -21
- package/theme/config/mixins/_avatar.scss +2 -2
- package/theme/config/mixins/_btn.scss +4 -2
- package/theme/config/mixins/_card.scss +88 -71
- package/theme/config/mixins/_colors.scss +2 -1
- package/theme/config/mixins/_display.scss +12 -0
- package/theme/config/mixins/_footer.scss +3 -1
- package/theme/config/mixins/_form.scss +74 -55
- package/theme/config/mixins/_layout.scss +9 -0
- package/theme/config/mixins/_ln-table.scss +22 -15
- package/theme/config/mixins/_modal.scss +3 -0
- package/theme/config/mixins/_nav.scss +31 -8
- package/theme/config/mixins/_sidebar.scss +2 -1
- package/theme/config/mixins/_spacing.scss +4 -4
- package/theme/config/mixins/_stat-card.scss +33 -2
- package/theme/config/mixins/_status-badge.scss +1 -0
- package/theme/config/mixins/_table.scss +87 -69
- package/theme/config/mixins/_tabs.scss +31 -0
- package/theme/config/mixins/_toast.scss +0 -1
- package/theme/designs/relaxed/_card.scss +12 -0
- package/theme/designs/relaxed/_layout.scss +24 -0
- package/theme/ln-ashlar-core.scss +1 -0
- package/theme/ln-ashlar-dev.scss +0 -1
- package/theme/ln-ashlar-relaxed.scss +67 -0
- package/theme/tailwind/components.css +2198 -0
- package/theme/tailwind/entry.css +3 -0
- package/theme/tailwind/theme.css +23 -0
- package/theme/utilities/_utilities.scss +1 -11
- package/web-types.json +2351 -2337
- package/components/ln-progress/ln-progress-dev.scss +0 -8
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
@use 'shadows' as *;
|
|
8
8
|
@use 'transitions' as *;
|
|
9
9
|
@use 'position' as *;
|
|
10
|
+
@use 'focus' as *;
|
|
10
11
|
|
|
11
12
|
// Card & Section — composite mixins
|
|
12
13
|
|
|
@@ -18,31 +19,25 @@
|
|
|
18
19
|
@include border;
|
|
19
20
|
@include w-full;
|
|
20
21
|
@include flex-col;
|
|
21
|
-
border-radius: var(--radius);
|
|
22
|
-
|
|
22
|
+
border-radius: var(--panel-radius, var(--radius));
|
|
23
|
+
// `clip`, not `hidden`: no scroll container, so a table's sticky thead
|
|
24
|
+
// binds to the page scroller. min-inline-size keeps the grid/flex shrink
|
|
25
|
+
// that `hidden` gave for free.
|
|
26
|
+
overflow: clip;
|
|
27
|
+
min-inline-size: 0;
|
|
23
28
|
@include transition;
|
|
24
29
|
box-shadow: var(--shadow), var(--surface-highlight, inset 0 0 0 0 transparent);
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
// Only whole-card links/buttons signal interaction. Passive containers
|
|
32
|
+
// must not respond merely because the pointer crosses their content.
|
|
33
|
+
&:is(a, button):hover:not(:disabled),
|
|
34
|
+
&:has(> a:hover) {
|
|
27
35
|
--color-border: color-mix(in srgb, var(--color-accent) 35%, transparent);
|
|
28
|
-
--shadow: var(--nested-card-shadow-hover, var(--shadow-floating));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Nesting ladder: when nested inside a section-card or another card,
|
|
32
|
-
// climb to elevated surface to preserve visual depth.
|
|
33
|
-
.section-card &,
|
|
34
|
-
.section &,
|
|
35
|
-
.card & {
|
|
36
|
-
--color-bg: var(--bg-elevated);
|
|
37
|
-
--shadow: var(--shadow-floating);
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
--nested-card-bg: var(--bg-elevated);
|
|
44
|
-
--nested-card-shadow: var(--shadow-floating);
|
|
45
|
-
--nested-card-shadow-hover: var(--shadow-floating);
|
|
38
|
+
&:is(a, button):focus-visible,
|
|
39
|
+
&:has(> a:focus-visible) {
|
|
40
|
+
@include focus-ring;
|
|
46
41
|
}
|
|
47
42
|
|
|
48
43
|
// Direct-child selectors only — nested <header>/<main>/<footer>
|
|
@@ -81,37 +76,65 @@
|
|
|
81
76
|
// without a <header> wrapper, the global h3 cascade (heading-sm)
|
|
82
77
|
// is too large for tile context. Tighten to tile-title sizing.
|
|
83
78
|
> main h3 {
|
|
84
|
-
@include typography(
|
|
79
|
+
@include typography(title-sm);
|
|
85
80
|
@include font-semibold;
|
|
86
81
|
color: inherit;
|
|
87
82
|
margin: 0;
|
|
88
83
|
}
|
|
89
84
|
}
|
|
90
85
|
|
|
91
|
-
|
|
86
|
+
// $group: true — title row of a transparent group: no inline padding, no
|
|
87
|
+
// divider, no surface (it sits on the canvas).
|
|
88
|
+
@mixin panel-header($group: false) {
|
|
92
89
|
@include flex;
|
|
93
90
|
@include items-center;
|
|
94
91
|
@include justify-between;
|
|
95
|
-
|
|
92
|
+
@if $group {
|
|
93
|
+
padding: var(--card-padding-y) 0;
|
|
94
|
+
} @else {
|
|
95
|
+
padding: var(--card-padding-y) var(--card-padding-x);
|
|
96
|
+
}
|
|
96
97
|
min-height: 2.5rem;
|
|
97
|
-
@
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
@if $group {
|
|
99
|
+
background: transparent;
|
|
100
|
+
} @else {
|
|
101
|
+
@include border-b;
|
|
102
|
+
// Header shares the panel surface (inherited --color-bg) — a tinted
|
|
103
|
+
// caption bar reads as legacy window chrome. Themes may opt in to a
|
|
104
|
+
// tint via --panel-header-bg. Opaque, so sticky toolbars stay solid.
|
|
105
|
+
background: var(--panel-header-bg, var(--color-bg));
|
|
106
|
+
}
|
|
100
107
|
@include transition;
|
|
101
108
|
margin-bottom: 0;
|
|
102
109
|
|
|
103
110
|
:is(h1, h2, h3, h4, h5, h6) {
|
|
104
|
-
@include typography(
|
|
105
|
-
@include font-
|
|
111
|
+
@include typography(title-sm);
|
|
112
|
+
@include font-semibold;
|
|
106
113
|
color: var(--color-fg);
|
|
107
114
|
margin: 0;
|
|
108
115
|
}
|
|
109
116
|
|
|
117
|
+
// Title + description: <hgroup><h3>…</h3><p>…</p></hgroup>
|
|
118
|
+
hgroup {
|
|
119
|
+
@include flex-col;
|
|
120
|
+
gap: var(--size-2xs);
|
|
121
|
+
min-inline-size: 0;
|
|
122
|
+
margin: 0;
|
|
123
|
+
|
|
124
|
+
> p {
|
|
125
|
+
@include typography(caption);
|
|
126
|
+
color: var(--fg-muted);
|
|
127
|
+
margin: 0;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
110
131
|
label {
|
|
111
132
|
--padding-y: var(--size-xs);
|
|
133
|
+
min-height: calc(var(--control-line-height) + 2 * var(--padding-y) + 2 * var(--border-width));
|
|
112
134
|
}
|
|
113
135
|
|
|
114
|
-
// Header actions
|
|
136
|
+
// Header actions lock to the control height: --control-line-height +
|
|
137
|
+
// 2 × --padding-y + 2 × border-width. This is a DESCENDANT selector,
|
|
115
138
|
// so it also reaches a button that belongs to a nested field — that button
|
|
116
139
|
// is the field's own chrome, not a header action, and form-field-group
|
|
117
140
|
// stretches it to the field's full height. An explicit height beats
|
|
@@ -135,19 +158,9 @@
|
|
|
135
158
|
// rule to (0,1,3) and silently outrank consumers that override it
|
|
136
159
|
// at equal weight by source order.
|
|
137
160
|
// Wrapped, the rule stays exactly (0,1,2); only its match set narrows.
|
|
138
|
-
//
|
|
139
|
-
// NOT written as [class*="group"]: that spelling means "field group" in
|
|
140
|
-
// components/_form.scss only because it is scoped under .form-element there.
|
|
141
|
-
// Unscoped it would also catch .btn-group and stop compacting a segmented
|
|
142
|
-
// group in a header — a regression, and those buttons already outrank this.
|
|
143
161
|
button:where(:not(:where([data-ln-date-field]) > button)) {
|
|
144
|
-
--btn-padding-y: var(--size-
|
|
145
|
-
--
|
|
146
|
-
font-size: var(--text-xs);
|
|
147
|
-
height: 1.75rem;
|
|
148
|
-
display: inline-flex;
|
|
149
|
-
align-items: center;
|
|
150
|
-
justify-content: center;
|
|
162
|
+
--btn-padding-y: var(--size-xs);
|
|
163
|
+
min-height: calc(var(--control-line-height) + 2 * var(--padding-y) + 2 * var(--border-width));
|
|
151
164
|
}
|
|
152
165
|
|
|
153
166
|
// Close/dismiss and toggle buttons inside the header should be tight and flat.
|
|
@@ -157,24 +170,21 @@
|
|
|
157
170
|
--btn-padding-y: var(--size-2xs);
|
|
158
171
|
--btn-padding-x: var(--size-2xs);
|
|
159
172
|
--shadow: none;
|
|
173
|
+
min-height: 0;
|
|
160
174
|
height: 1.75rem;
|
|
161
175
|
width: 1.75rem;
|
|
162
176
|
}
|
|
163
177
|
}
|
|
164
178
|
|
|
165
179
|
@mixin panel-body {
|
|
166
|
-
padding: var(--padding-y) var(--padding-x);
|
|
180
|
+
padding: var(--card-padding-y) var(--card-padding-x);
|
|
167
181
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
max-width: 100%;
|
|
172
|
-
-webkit-overflow-scrolling: touch;
|
|
173
|
-
}
|
|
182
|
+
// A card in a panel body sits on a surface — climb to elevated.
|
|
183
|
+
--nested-card-bg: var(--bg-elevated);
|
|
184
|
+
--nested-card-shadow: none;
|
|
174
185
|
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
&:has(> [data-ln-table]) {
|
|
186
|
+
// `clip`, not `auto` — a scrolling cell would trap the sticky thead.
|
|
187
|
+
&:has(> :is(table, .table-container, [data-ln-table])) {
|
|
178
188
|
overflow-x: clip;
|
|
179
189
|
max-width: 100%;
|
|
180
190
|
}
|
|
@@ -192,9 +202,7 @@
|
|
|
192
202
|
// If main has mixed content (intro <p>, demo-split, <h4>,
|
|
193
203
|
// then <table>), :only-child doesn't match, padding stays,
|
|
194
204
|
// and the table keeps its own inset.
|
|
195
|
-
&:has(> table:only-child)
|
|
196
|
-
&:has(> .table-container:only-child),
|
|
197
|
-
&:has(> [data-ln-table]:only-child) {
|
|
205
|
+
&:has(> :is(table, .table-container, [data-ln-table]):only-child) {
|
|
198
206
|
padding: 0;
|
|
199
207
|
|
|
200
208
|
> table,
|
|
@@ -208,10 +216,10 @@
|
|
|
208
216
|
}
|
|
209
217
|
|
|
210
218
|
@mixin panel-footer {
|
|
211
|
-
padding: var(--padding-y) var(--padding-x);
|
|
219
|
+
padding: var(--card-padding-y) var(--card-padding-x);
|
|
212
220
|
@include border-t;
|
|
213
|
-
|
|
214
|
-
background: var(--color-bg);
|
|
221
|
+
// Same surface as the panel; opt-in tint via --panel-footer-bg.
|
|
222
|
+
background: var(--panel-footer-bg, var(--color-bg));
|
|
215
223
|
@include flex;
|
|
216
224
|
@include justify-end;
|
|
217
225
|
gap: var(--gap);
|
|
@@ -254,7 +262,8 @@
|
|
|
254
262
|
// Usage: #my-card { @include card; @include card-accent-top; }
|
|
255
263
|
|
|
256
264
|
@mixin _card-accent-base {
|
|
257
|
-
&:hover
|
|
265
|
+
&:is(a, button):hover:not(:disabled),
|
|
266
|
+
&:has(> a:hover) {
|
|
258
267
|
background-color: color-mix(in srgb, var(--color-accent) var(--tint-hover), transparent);
|
|
259
268
|
}
|
|
260
269
|
}
|
|
@@ -312,27 +321,35 @@
|
|
|
312
321
|
}
|
|
313
322
|
}
|
|
314
323
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
324
|
+
// $group: true — a transparent titled group: no surface, border, shadow or
|
|
325
|
+
// radius; content sits directly on the canvas.
|
|
326
|
+
@mixin section-card($group: false) {
|
|
327
|
+
@if not $group {
|
|
328
|
+
background: var(--color-bg);
|
|
329
|
+
@include border;
|
|
330
|
+
border-radius: var(--panel-radius, var(--radius));
|
|
331
|
+
// `overflow: clip` not `overflow: hidden` — clips at rounded corners
|
|
332
|
+
// without establishing a scroll container, so sticky descendants
|
|
333
|
+
// (e.g. ln-table's <thead>) bind to the outer .app-main > section
|
|
334
|
+
// scroll surface instead of being trapped inside this card.
|
|
335
|
+
overflow: clip;
|
|
336
|
+
@include transition;
|
|
337
|
+
--shadow: var(--shadow-resting);
|
|
338
|
+
box-shadow: var(--shadow);
|
|
339
|
+
}
|
|
340
|
+
--margin-block: var(--panel-margin-block, var(--size-md));
|
|
328
341
|
margin-bottom: var(--margin-block);
|
|
329
342
|
|
|
330
343
|
// Direct-child selectors only — nested <header>/<main>/<footer>
|
|
331
344
|
// elements inside the card's content are consumer-owned and
|
|
332
345
|
// must not inherit the card's own chrome styles.
|
|
333
|
-
> header { @include panel-header; }
|
|
346
|
+
> header { @include panel-header($group); }
|
|
334
347
|
> main { @include panel-body; }
|
|
335
348
|
> footer { @include panel-footer; }
|
|
349
|
+
|
|
350
|
+
@if $group {
|
|
351
|
+
> main { padding: 0; }
|
|
352
|
+
}
|
|
336
353
|
}
|
|
337
354
|
|
|
338
355
|
// ─── Frosted Glass ──────────────────────────────────────
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
// Re-declare accent vocabulary at this scope so var() resolves
|
|
34
34
|
// here instead of staying frozen at an ancestor scope. Without this, descendants
|
|
35
35
|
// reading --color-accent get the ancestor's primary blue regardless of the
|
|
36
|
-
// semantic class. --color-accent-fg
|
|
36
|
+
// semantic class. --color-accent-fg stays inherited — white in light
|
|
37
|
+
// polarity, ink in the dark lift.
|
|
37
38
|
--color-accent: hsl(var(--color-primary));
|
|
38
39
|
--color-accent-tint: hsl(var(--color-#{$family}-lighter));
|
|
39
40
|
--color-accent-tint-strong: hsl(var(--color-#{$family}-light));
|
|
@@ -7,6 +7,18 @@
|
|
|
7
7
|
@mixin hidden { display: none; }
|
|
8
8
|
@mixin invisible { visibility: hidden; }
|
|
9
9
|
|
|
10
|
+
@mixin sr-only {
|
|
11
|
+
position: absolute;
|
|
12
|
+
width: 1px;
|
|
13
|
+
height: 1px;
|
|
14
|
+
padding: 0;
|
|
15
|
+
margin: -1px;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
clip: rect(0, 0, 0, 0);
|
|
18
|
+
white-space: nowrap;
|
|
19
|
+
border-width: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
@mixin flex-col { display: flex; flex-direction: column; }
|
|
11
23
|
@mixin flex-row { display: flex; flex-direction: row; }
|
|
12
24
|
@mixin flex-wrap { flex-wrap: wrap; }
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
--color-border: var(--border-subtle);
|
|
25
25
|
padding: var(--padding-y) var(--padding-x);
|
|
26
26
|
border-block-start: var(--border-block-start, var(--border-width) solid var(--color-border));
|
|
27
|
-
|
|
27
|
+
// Sits on the surrounding canvas — an opaque white strip pinned to the
|
|
28
|
+
// bottom edge reads as a desktop status bar. Opt in via --app-footer-bg.
|
|
29
|
+
background: var(--app-footer-bg, transparent);
|
|
28
30
|
@include typography(body-sm);
|
|
29
31
|
color: var(--color-fg);
|
|
30
32
|
@include flex;
|
|
@@ -17,19 +17,18 @@
|
|
|
17
17
|
|
|
18
18
|
@mixin form-label {
|
|
19
19
|
@include block;
|
|
20
|
-
|
|
20
|
+
// label-md / medium — a 12px semibold caption over every field reads
|
|
21
|
+
// as dialog-box chrome; field labels sit one step under body text.
|
|
22
|
+
@include typography(label-md);
|
|
21
23
|
@include font-medium;
|
|
22
24
|
color: var(--color-fg);
|
|
23
25
|
// Structural label-to-input gap (not shell rhythm).
|
|
24
26
|
--margin-block: 0.375rem;
|
|
25
27
|
margin-bottom: var(--margin-block);
|
|
26
28
|
|
|
27
|
-
// Required indicator — red *
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
:is(.form-element, .form-group, .form-field):has([required]) > &,
|
|
31
|
-
label:has([required]) &,
|
|
32
|
-
&:has(~ :is(input, select, textarea)[required]) {
|
|
29
|
+
// Required indicator — red * when any control under the label's parent is required
|
|
30
|
+
// (siblings, nested field shells).
|
|
31
|
+
:has([required]) > & {
|
|
33
32
|
&::after {
|
|
34
33
|
content: ' *';
|
|
35
34
|
color: hsl(var(--color-error));
|
|
@@ -38,6 +37,21 @@
|
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
|
|
40
|
+
// Field wrapper — zero-specificity rules so form-grid's `> * { margin: 0 }` wins.
|
|
41
|
+
@mixin form-element {
|
|
42
|
+
// Stacked outside a form-grid (modal bodies, settings panels).
|
|
43
|
+
:where(&) + :where(&) {
|
|
44
|
+
--margin-block: var(--size-md);
|
|
45
|
+
margin-top: var(--margin-block);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Trailing feedback after the control gets slot treatment.
|
|
49
|
+
:where(& > :is(input, select, textarea, [data-ln-date-field], search) ~ :not(input)) {
|
|
50
|
+
@include form-slot;
|
|
51
|
+
color: var(--fg-muted);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
41
55
|
@mixin form-grid {
|
|
42
56
|
--gap: var(--size-md);
|
|
43
57
|
container-type: inline-size;
|
|
@@ -83,28 +97,42 @@
|
|
|
83
97
|
|
|
84
98
|
@mixin _form-focus-style {
|
|
85
99
|
border-color: var(--color-accent);
|
|
86
|
-
|
|
100
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 20%, transparent);
|
|
87
101
|
}
|
|
88
102
|
|
|
89
103
|
// ─── Text inputs, textareas, selects ────────────────────────────
|
|
90
104
|
|
|
91
105
|
@mixin form-input {
|
|
92
106
|
@include w-full;
|
|
93
|
-
--padding-y: var(--input-padding-y, var(--size-
|
|
94
|
-
--padding-x: var(--size-
|
|
107
|
+
--padding-y: var(--input-padding-y, var(--size-xs-up));
|
|
108
|
+
--padding-x: var(--size-sm-up);
|
|
95
109
|
padding: var(--padding-y) var(--padding-x);
|
|
96
110
|
font-size: var(--font-size);
|
|
97
|
-
line
|
|
111
|
+
// Shared control line box — same height as a sibling button.
|
|
112
|
+
line-height: var(--control-line-height);
|
|
98
113
|
color: var(--color-fg);
|
|
99
|
-
|
|
114
|
+
// Enabled fields read as writable surfaces: base fill, hairline shadow.
|
|
115
|
+
// A grey fill is reserved for :disabled.
|
|
116
|
+
--color-bg: var(--input-bg, var(--bg-base));
|
|
100
117
|
background: var(--color-bg);
|
|
101
118
|
// Control boundary, not a divider — 1.4.11 wants 3:1 here.
|
|
102
|
-
--color-border: var(--border-ui);
|
|
119
|
+
--color-border: var(--input-border, var(--border-ui));
|
|
103
120
|
@include border;
|
|
104
121
|
border-radius: var(--radius);
|
|
105
|
-
|
|
122
|
+
box-shadow: var(--input-shadow, var(--shadow-xs));
|
|
123
|
+
// Colour + ring only — `all` would animate padding/font-size when the
|
|
124
|
+
// density tier switches, making fields visibly "breathe".
|
|
125
|
+
transition:
|
|
126
|
+
color var(--transition-base),
|
|
127
|
+
background-color var(--transition-base),
|
|
128
|
+
border-color var(--transition-base),
|
|
129
|
+
box-shadow var(--transition-base);
|
|
106
130
|
outline: none;
|
|
107
131
|
|
|
132
|
+
&:hover:not(:disabled):not(:focus-visible) {
|
|
133
|
+
--color-border: var(--input-border-hover, var(--border-strong-hover));
|
|
134
|
+
}
|
|
135
|
+
|
|
108
136
|
&:focus-visible {
|
|
109
137
|
@include _form-focus-style;
|
|
110
138
|
}
|
|
@@ -119,6 +147,7 @@
|
|
|
119
147
|
@include cursor-not-allowed;
|
|
120
148
|
--color-bg: var(--bg-sunken);
|
|
121
149
|
background: var(--color-bg);
|
|
150
|
+
box-shadow: none;
|
|
122
151
|
}
|
|
123
152
|
|
|
124
153
|
// Hide native UA clear button on <input type="search"> — we never use it;
|
|
@@ -130,6 +159,8 @@
|
|
|
130
159
|
}
|
|
131
160
|
|
|
132
161
|
@mixin form-textarea {
|
|
162
|
+
// Multi-line text keeps the reading line-height, not the control box.
|
|
163
|
+
line-height: var(--line-height);
|
|
133
164
|
min-height: 6rem;
|
|
134
165
|
resize: vertical;
|
|
135
166
|
|
|
@@ -205,9 +236,7 @@
|
|
|
205
236
|
}
|
|
206
237
|
|
|
207
238
|
// Propagate disabled states to the parent visual shell
|
|
208
|
-
&:has(> input:disabled)
|
|
209
|
-
&:has(> select:disabled),
|
|
210
|
-
&:has(> button:disabled) {
|
|
239
|
+
&:has(> :is(input, select, button):disabled) {
|
|
211
240
|
@include opacity-50;
|
|
212
241
|
@include cursor-not-allowed;
|
|
213
242
|
--color-bg: var(--bg-sunken);
|
|
@@ -442,12 +471,13 @@
|
|
|
442
471
|
@include flex;
|
|
443
472
|
@include items-center;
|
|
444
473
|
gap: var(--gap);
|
|
445
|
-
--padding-y: var(--input-padding-y, var(--size-
|
|
474
|
+
--padding-y: var(--input-padding-y, var(--size-xs-up));
|
|
446
475
|
--padding-x: var(--size-md);
|
|
447
476
|
padding: var(--padding-y) var(--padding-x);
|
|
448
477
|
@include cursor-pointer;
|
|
449
478
|
@include transition;
|
|
450
479
|
@include text-sm;
|
|
480
|
+
line-height: var(--control-line-height);
|
|
451
481
|
@include font-medium;
|
|
452
482
|
margin-bottom: 0;
|
|
453
483
|
border-radius: var(--radius);
|
|
@@ -461,16 +491,16 @@
|
|
|
461
491
|
// pill's `display: none` when pill-outline is applied as an override.
|
|
462
492
|
> input { display: revert; }
|
|
463
493
|
|
|
464
|
-
&:
|
|
494
|
+
&:hover {
|
|
465
495
|
border-color: var(--color-accent);
|
|
466
|
-
color: var(--color-accent);
|
|
467
|
-
// Reset pill's accent fill on checked — outline stays transparent.
|
|
468
|
-
background: transparent;
|
|
469
496
|
z-index: 1;
|
|
470
497
|
}
|
|
471
498
|
|
|
472
|
-
&:
|
|
499
|
+
&:has(> input:checked) {
|
|
473
500
|
border-color: var(--color-accent);
|
|
501
|
+
color: var(--color-accent);
|
|
502
|
+
// Reset pill's accent fill on checked — outline stays transparent.
|
|
503
|
+
background: transparent;
|
|
474
504
|
z-index: 1;
|
|
475
505
|
}
|
|
476
506
|
}
|
|
@@ -484,24 +514,24 @@
|
|
|
484
514
|
// Lets .error/.success/.warning/.info parents cascade to the checked-
|
|
485
515
|
// state hover via the .success { --color-primary } chain. See
|
|
486
516
|
// _btn.scss for the syntax rationale (number form, not percentage).
|
|
487
|
-
--color-accent-hover: hsl(from var(--color-accent) h s calc(l -
|
|
517
|
+
--color-accent-hover: hsl(from var(--color-accent) h s calc(l + var(--accent-hover-shift)));
|
|
488
518
|
|
|
489
519
|
background-color: var(--color-bg);
|
|
490
520
|
border-color: transparent;
|
|
491
521
|
|
|
492
522
|
> input { display: none; }
|
|
493
523
|
|
|
524
|
+
&:hover {
|
|
525
|
+
border-color: var(--color-border);
|
|
526
|
+
}
|
|
527
|
+
|
|
494
528
|
&:has(> input:checked) {
|
|
495
529
|
background-color: var(--color-accent);
|
|
496
530
|
color: var(--color-accent-fg);
|
|
497
|
-
}
|
|
498
531
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
&:hover:has(> input:checked) {
|
|
504
|
-
background-color: var(--color-accent-hover);
|
|
532
|
+
&:hover {
|
|
533
|
+
background-color: var(--color-accent-hover);
|
|
534
|
+
}
|
|
505
535
|
}
|
|
506
536
|
}
|
|
507
537
|
|
|
@@ -549,24 +579,20 @@
|
|
|
549
579
|
@mixin pills {
|
|
550
580
|
@include inline-flex;
|
|
551
581
|
|
|
552
|
-
> li > label:has(> input[type="checkbox"])
|
|
553
|
-
> li > label:has(> input[type="radio"]) {
|
|
582
|
+
> li > label:has(> input:is([type="checkbox"], [type="radio"])) {
|
|
554
583
|
@include pill;
|
|
555
584
|
border-radius: 0;
|
|
556
585
|
}
|
|
557
586
|
|
|
558
|
-
> li:first-child > label:has(> input[type="checkbox"])
|
|
559
|
-
> li:first-child > label:has(> input[type="radio"]) {
|
|
587
|
+
> li:first-child > label:has(> input:is([type="checkbox"], [type="radio"])) {
|
|
560
588
|
border-radius: var(--radius) 0 0 var(--radius);
|
|
561
589
|
}
|
|
562
590
|
|
|
563
|
-
> li:last-child > label:has(> input[type="checkbox"])
|
|
564
|
-
> li:last-child > label:has(> input[type="radio"]) {
|
|
591
|
+
> li:last-child > label:has(> input:is([type="checkbox"], [type="radio"])) {
|
|
565
592
|
border-radius: 0 var(--radius) var(--radius) 0;
|
|
566
593
|
}
|
|
567
594
|
|
|
568
|
-
> li:only-child > label:has(> input[type="checkbox"])
|
|
569
|
-
> li:only-child > label:has(> input[type="radio"]) {
|
|
595
|
+
> li:only-child > label:has(> input:is([type="checkbox"], [type="radio"])) {
|
|
570
596
|
border-radius: var(--radius);
|
|
571
597
|
}
|
|
572
598
|
}
|
|
@@ -587,24 +613,20 @@
|
|
|
587
613
|
}
|
|
588
614
|
}
|
|
589
615
|
|
|
590
|
-
> li > label:has(> input[type="checkbox"])
|
|
591
|
-
> li > label:has(> input[type="radio"]) {
|
|
616
|
+
> li > label:has(> input:is([type="checkbox"], [type="radio"])) {
|
|
592
617
|
@include pill-outline;
|
|
593
618
|
border-radius: 0;
|
|
594
619
|
}
|
|
595
620
|
|
|
596
|
-
> li:first-child > label:has(> input[type="checkbox"])
|
|
597
|
-
> li:first-child > label:has(> input[type="radio"]) {
|
|
621
|
+
> li:first-child > label:has(> input:is([type="checkbox"], [type="radio"])) {
|
|
598
622
|
border-radius: var(--radius) 0 0 var(--radius);
|
|
599
623
|
}
|
|
600
624
|
|
|
601
|
-
> li:last-child > label:has(> input[type="checkbox"])
|
|
602
|
-
> li:last-child > label:has(> input[type="radio"]) {
|
|
625
|
+
> li:last-child > label:has(> input:is([type="checkbox"], [type="radio"])) {
|
|
603
626
|
border-radius: 0 var(--radius) var(--radius) 0;
|
|
604
627
|
}
|
|
605
628
|
|
|
606
|
-
> li:only-child > label:has(> input[type="checkbox"])
|
|
607
|
-
> li:only-child > label:has(> input[type="radio"]) {
|
|
629
|
+
> li:only-child > label:has(> input:is([type="checkbox"], [type="radio"])) {
|
|
608
630
|
border-radius: var(--radius);
|
|
609
631
|
}
|
|
610
632
|
}
|
|
@@ -628,8 +650,7 @@
|
|
|
628
650
|
display: block;
|
|
629
651
|
}
|
|
630
652
|
|
|
631
|
-
> li > label:has(> input[type="checkbox"])
|
|
632
|
-
> li > label:has(> input[type="radio"]) {
|
|
653
|
+
> li > label:has(> input:is([type="checkbox"], [type="radio"])) {
|
|
633
654
|
@include flex;
|
|
634
655
|
@include items-center;
|
|
635
656
|
@include justify-center;
|
|
@@ -799,8 +820,7 @@
|
|
|
799
820
|
--padding-y: var(--size-xs-up);
|
|
800
821
|
padding: var(--padding-y) var(--padding-x);
|
|
801
822
|
|
|
802
|
-
|
|
803
|
-
&:hover:not(:has(> input:checked)) {
|
|
823
|
+
&:hover {
|
|
804
824
|
background-color: color-mix(in srgb, var(--color-accent) var(--tint-hover), transparent);
|
|
805
825
|
}
|
|
806
826
|
|
|
@@ -809,11 +829,10 @@
|
|
|
809
829
|
background-color: var(--color-accent-tint-strong);
|
|
810
830
|
color: var(--color-accent);
|
|
811
831
|
@include font-semibold;
|
|
812
|
-
}
|
|
813
832
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
833
|
+
&:hover {
|
|
834
|
+
background-color: var(--color-accent-tint-strong);
|
|
835
|
+
}
|
|
817
836
|
}
|
|
818
837
|
}
|
|
819
838
|
}
|
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
// Layout — grid, stack, container
|
|
6
6
|
|
|
7
|
+
// Respond to available parent width, including sidebars and panel gutters.
|
|
8
|
+
// The min() allows a single item to shrink below its preferred minimum.
|
|
9
|
+
@mixin grid-auto($min: var(--grid-item-min-width)) {
|
|
10
|
+
display: grid;
|
|
11
|
+
gap: var(--gap);
|
|
12
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, #{$min}), 1fr));
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
@mixin _grid-base {
|
|
8
16
|
display: grid;
|
|
9
17
|
--gap: var(--size-lg);
|
|
@@ -110,6 +118,7 @@
|
|
|
110
118
|
|
|
111
119
|
> * {
|
|
112
120
|
--radius: 0;
|
|
121
|
+
--panel-radius: 0;
|
|
113
122
|
--shadow: none;
|
|
114
123
|
}
|
|
115
124
|
|