@webority/theme 0.3.0 → 0.5.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 +18 -29
- package/dist/webority-theme.css +1 -1
- package/package.json +2 -2
- package/scss/_components.scss +42 -0
- package/scss/_tokens.scss +15 -1
- package/scss/_variables.scss +17 -0
- package/scss/_wui-aliases.scss +16 -0
- package/scss/_wui-components.scss +19 -3723
- package/scss/wui/_card.scss +37 -0
- package/scss/wui/_choice.scss +746 -0
- package/scss/wui/_controls.scss +810 -0
- package/scss/wui/_data.scss +614 -0
- package/scss/wui/_disclosure.scss +113 -0
- package/scss/wui/_display.scss +508 -0
- package/scss/wui/_flow.scss +410 -0
- package/scss/wui/_media.scss +569 -0
- package/scss/wui/_navigation.scss +284 -0
- package/scss/wui/_overlays.scss +350 -0
- package/scss/wui/_shell.scss +474 -0
- package/scss/wui/_typography.scss +275 -0
- package/scss/wui/_utilities.scss +340 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
// Empty state, base table, ledger table + pagination.
|
|
2
|
+
//
|
|
3
|
+
// Part of the wui-components layer — see _wui-components.scss for the whole
|
|
4
|
+
// picture and the load order. Order within the layer is LOAD-BEARING: these
|
|
5
|
+
// partials are a split of one stylesheet, not independent modules, so a rule
|
|
6
|
+
// here may be overridden by a later partial. Never reorder the imports.
|
|
7
|
+
|
|
8
|
+
// ── Empty state ──
|
|
9
|
+
.empty-state {
|
|
10
|
+
text-align: center;
|
|
11
|
+
padding: 48px 24px;
|
|
12
|
+
color: var(--color-text-subtle);
|
|
13
|
+
}
|
|
14
|
+
.empty-state-title {
|
|
15
|
+
font-size: 16px;
|
|
16
|
+
font-weight: 600;
|
|
17
|
+
line-height: 1.35;
|
|
18
|
+
color: var(--color-text);
|
|
19
|
+
}
|
|
20
|
+
.empty-state-message {
|
|
21
|
+
margin: 6px 0 0;
|
|
22
|
+
font-size: 14px;
|
|
23
|
+
line-height: 1.5;
|
|
24
|
+
color: var(--color-text-muted);
|
|
25
|
+
}
|
|
26
|
+
.empty-state-action {
|
|
27
|
+
margin-top: 16px;
|
|
28
|
+
}
|
|
29
|
+
// Tighter scale for a widget/tile, as opposed to a full card or page.
|
|
30
|
+
.empty-state-sm {
|
|
31
|
+
padding: 28px 16px;
|
|
32
|
+
}
|
|
33
|
+
.empty-state-sm .empty-state-title {
|
|
34
|
+
font-size: 14px;
|
|
35
|
+
}
|
|
36
|
+
.empty-state-sm .empty-state-message {
|
|
37
|
+
font-size: 12px;
|
|
38
|
+
}
|
|
39
|
+
.empty-state-icon {
|
|
40
|
+
width: 48px;
|
|
41
|
+
height: 48px;
|
|
42
|
+
border-radius: var(--radius-lg);
|
|
43
|
+
background: var(--color-primary-soft);
|
|
44
|
+
color: var(--color-primary);
|
|
45
|
+
display: inline-flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
margin-bottom: 14px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// The empty state a DataTable renders in place of its rows. Distinct from
|
|
52
|
+
// the card-level .empty-state above: a 50px primary-tinted disc, then a 32px
|
|
53
|
+
// gap to the title and 12px to the subtitle.
|
|
54
|
+
.tbl-empty {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
align-items: center;
|
|
58
|
+
text-align: center;
|
|
59
|
+
padding: 40px 24px;
|
|
60
|
+
}
|
|
61
|
+
.tbl-empty-icon {
|
|
62
|
+
display: inline-grid;
|
|
63
|
+
place-items: center;
|
|
64
|
+
flex: none;
|
|
65
|
+
width: 50px;
|
|
66
|
+
height: 50px;
|
|
67
|
+
border-radius: 50%;
|
|
68
|
+
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
69
|
+
color: var(--color-primary);
|
|
70
|
+
margin-bottom: 16px;
|
|
71
|
+
}
|
|
72
|
+
.tbl-empty-title {
|
|
73
|
+
font-size: 18px;
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
line-height: 1.35;
|
|
76
|
+
color: var(--color-text);
|
|
77
|
+
}
|
|
78
|
+
.tbl-empty-text {
|
|
79
|
+
margin-top: 8px;
|
|
80
|
+
font-size: 16px;
|
|
81
|
+
font-weight: 400;
|
|
82
|
+
line-height: 1.5;
|
|
83
|
+
color: var(--color-text-muted);
|
|
84
|
+
}
|
|
85
|
+
// An action (e.g. "New assessment") sits under the subtitle when the caller
|
|
86
|
+
// supplies one.
|
|
87
|
+
.tbl-empty-action {
|
|
88
|
+
margin-top: 20px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.lx-table-empty {
|
|
92
|
+
padding: 36px 24px;
|
|
93
|
+
text-align: center;
|
|
94
|
+
color: var(--color-text-muted);
|
|
95
|
+
}
|
|
96
|
+
// The empty state owns its own padding and is not a row you can act on — no
|
|
97
|
+
// cell padding, no border, and no hover fill from .table / .table-ledger.
|
|
98
|
+
.table td.lx-table-empty:has(> .tbl-empty) {
|
|
99
|
+
padding: 0;
|
|
100
|
+
border-bottom: 0;
|
|
101
|
+
}
|
|
102
|
+
.table tbody tr:hover td.lx-table-empty:has(> .tbl-empty),
|
|
103
|
+
.table-ledger tbody tr:hover td.lx-table-empty:has(> .tbl-empty) {
|
|
104
|
+
background: transparent;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── Table (base) ──
|
|
108
|
+
// The shared data-table look, on the plain `.table` class Bootstrap already
|
|
109
|
+
// gives every table. Without this a product's tables fall back to stock
|
|
110
|
+
// Bootstrap and drift from the design system; `.table-ledger` below is the
|
|
111
|
+
// denser, card-inset variant, not a replacement for having a base.
|
|
112
|
+
.table {
|
|
113
|
+
--bs-table-bg: transparent;
|
|
114
|
+
--bs-table-color: var(--color-text);
|
|
115
|
+
width: 100%;
|
|
116
|
+
margin-bottom: 0;
|
|
117
|
+
color: var(--color-text);
|
|
118
|
+
vertical-align: middle;
|
|
119
|
+
border-color: var(--color-border);
|
|
120
|
+
// `separate` is required for a cell to render a border-radius at all —
|
|
121
|
+
// Bootstrap reboot sets `collapse`, under which .table-ledger's rounded
|
|
122
|
+
// header corners silently do nothing. border-spacing 0 keeps cells flush.
|
|
123
|
+
border-collapse: separate;
|
|
124
|
+
border-spacing: 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// The cell rules are wrapped in :where() so they carry ZERO class specificity
|
|
128
|
+
// — `:where(.table) > thead > tr > th` is (0,0,3), which any single-class
|
|
129
|
+
// variant selector such as `.table-ledger thead th` (0,1,2) outranks.
|
|
130
|
+
//
|
|
131
|
+
// Without this the base out-ranks its own variant: written plainly,
|
|
132
|
+
// `.table > thead > tr > th` is (0,1,3) and beats `.table-ledger thead th`,
|
|
133
|
+
// so the ledger lost its own border-bottom/line-height/font-family to the
|
|
134
|
+
// base. A base layer must always sit *under* the variants built on it.
|
|
135
|
+
//
|
|
136
|
+
// The catch: zero class specificity also loses to BOOTSTRAP. Its
|
|
137
|
+
// `.table > :not(caption) > * > *` is (0,1,1) and sets `padding`, `color` and
|
|
138
|
+
// `background-color` on every cell, so declaring those three here would be dead
|
|
139
|
+
// CSS — measured: they never applied. Each therefore goes through the channel
|
|
140
|
+
// Bootstrap itself reads:
|
|
141
|
+
// padding -> $table-cell-padding-y/x, overridden before bootstrap loads
|
|
142
|
+
// colour -> --bs-table-color (its fallback in the color-state/type chain)
|
|
143
|
+
// background -> --bs-table-bg
|
|
144
|
+
// A custom property declared on the cell beats the one `.table` passes down to it,
|
|
145
|
+
// so these land without out-ranking anything — the same move as the
|
|
146
|
+
// `--bs-table-bg-state` hover below, and the reason that one already works.
|
|
147
|
+
:where(.table) > thead > tr > th {
|
|
148
|
+
--bs-table-bg: var(--color-bg-muted);
|
|
149
|
+
--bs-table-color: var(--color-text-subtle);
|
|
150
|
+
height: 42px;
|
|
151
|
+
// Only the horizontal inset differs from the shared cell padding.
|
|
152
|
+
padding-inline: 12px;
|
|
153
|
+
// `height` is only a MINIMUM on a table cell, so the header lands on 42px only
|
|
154
|
+
// while its content box stays under that; left to the font's own metrics it
|
|
155
|
+
// drifted to 43.6px. Pinning the line box to the font size keeps
|
|
156
|
+
// 12 + 12 + 12 + 1px border comfortably under 42, so the minimum governs and the
|
|
157
|
+
// band is exactly 42px — and it stays 42px if the shared cell padding changes,
|
|
158
|
+
// which a hardcoded line-height would not. Bootstrap's cell rule does not set
|
|
159
|
+
// line-height, so this one still reaches the element.
|
|
160
|
+
line-height: 1;
|
|
161
|
+
border-bottom: 1px solid var(--color-border);
|
|
162
|
+
font-size: 12px;
|
|
163
|
+
font-weight: 500;
|
|
164
|
+
letter-spacing: 0.04em;
|
|
165
|
+
text-transform: uppercase;
|
|
166
|
+
white-space: nowrap;
|
|
167
|
+
vertical-align: middle;
|
|
168
|
+
transition:
|
|
169
|
+
background-color 0.12s ease,
|
|
170
|
+
color 0.12s ease;
|
|
171
|
+
}
|
|
172
|
+
:where(.table) > tbody > tr > td {
|
|
173
|
+
--bs-table-color: var(--color-text-muted);
|
|
174
|
+
border-bottom: 1px solid var(--color-border);
|
|
175
|
+
font-size: 14px;
|
|
176
|
+
vertical-align: middle;
|
|
177
|
+
transition: background-color 0.12s ease;
|
|
178
|
+
}
|
|
179
|
+
:where(.table) > tbody > tr:last-child > td {
|
|
180
|
+
border-bottom: 0;
|
|
181
|
+
}
|
|
182
|
+
:where(.table) > tbody > tr:hover {
|
|
183
|
+
--bs-table-bg-state: var(--color-bg-subtle);
|
|
184
|
+
}
|
|
185
|
+
@media (prefers-reduced-motion: reduce) {
|
|
186
|
+
:where(.table) > thead > tr > th,
|
|
187
|
+
:where(.table) > tbody > tr > td {
|
|
188
|
+
transition: none;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Numeric / action columns align to the trailing edge.
|
|
192
|
+
.table .cell-end,
|
|
193
|
+
.table thead th.cell-end {
|
|
194
|
+
text-align: right;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── Table + pagination ──
|
|
198
|
+
// DataTable cell helpers
|
|
199
|
+
.table-ledger .lx-cell-end,
|
|
200
|
+
.table-ledger thead th.lx-cell-end {
|
|
201
|
+
text-align: right;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Ledger chrome — the shared table look. Applies to every .table-ledger: a
|
|
205
|
+
// floating 42px header band, airy rows on a hairline rule, and an inset from
|
|
206
|
+
// the card edge. Column geometry is NOT set here.
|
|
207
|
+
.table-wrap:has(> .table-ledger) {
|
|
208
|
+
padding: 20px 20px 0;
|
|
209
|
+
}
|
|
210
|
+
.table-ledger {
|
|
211
|
+
border-spacing: 0;
|
|
212
|
+
}
|
|
213
|
+
.table-ledger thead th {
|
|
214
|
+
height: 42px;
|
|
215
|
+
padding: 0 12px;
|
|
216
|
+
background: var(--color-bg-muted);
|
|
217
|
+
border-bottom: none;
|
|
218
|
+
font-family: "Inter", var(--font-sans, sans-serif);
|
|
219
|
+
font-size: 12px;
|
|
220
|
+
font-weight: 500;
|
|
221
|
+
line-height: 17px;
|
|
222
|
+
letter-spacing: 0.04em;
|
|
223
|
+
text-transform: uppercase;
|
|
224
|
+
// --color-text-subtle (#78829D) is ~3.5:1 on the muted band, below WCAG AA's 4.5:1
|
|
225
|
+
// for 12px text. A column header names the data under it — that is not decorative
|
|
226
|
+
// text, so it takes the muted ramp (~7.5:1). Caught by the axe pass, on both surfaces.
|
|
227
|
+
color: var(--color-text-muted);
|
|
228
|
+
vertical-align: middle;
|
|
229
|
+
}
|
|
230
|
+
.table-ledger thead th:first-child {
|
|
231
|
+
padding-left: 20px;
|
|
232
|
+
border-radius: var(--radius-sm) 0 0 var(--radius-sm);
|
|
233
|
+
}
|
|
234
|
+
.table-ledger thead th:last-child {
|
|
235
|
+
padding-right: 20px;
|
|
236
|
+
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
|
237
|
+
}
|
|
238
|
+
.table-ledger tbody td {
|
|
239
|
+
height: 54px;
|
|
240
|
+
padding: 12px;
|
|
241
|
+
border-bottom: 0.5px solid var(--color-border);
|
|
242
|
+
font-family: "Inter", var(--font-sans, sans-serif);
|
|
243
|
+
font-size: 12px;
|
|
244
|
+
font-weight: 500;
|
|
245
|
+
line-height: 15px;
|
|
246
|
+
color: var(--color-text-muted);
|
|
247
|
+
}
|
|
248
|
+
.table-ledger tbody td:first-child {
|
|
249
|
+
padding-left: 20px;
|
|
250
|
+
}
|
|
251
|
+
.table-ledger tbody td:last-child {
|
|
252
|
+
padding-right: 20px;
|
|
253
|
+
}
|
|
254
|
+
.table-ledger tbody tr:last-child td {
|
|
255
|
+
border-bottom: none;
|
|
256
|
+
}
|
|
257
|
+
.table-ledger tbody tr:hover {
|
|
258
|
+
--bs-table-bg-state: var(--color-bg-subtle);
|
|
259
|
+
}
|
|
260
|
+
.table-ledger .u-meta {
|
|
261
|
+
gap: 2px;
|
|
262
|
+
}
|
|
263
|
+
.table-ledger .u-name {
|
|
264
|
+
font-size: 14px;
|
|
265
|
+
font-weight: 500;
|
|
266
|
+
line-height: 19px;
|
|
267
|
+
color: var(--color-text);
|
|
268
|
+
}
|
|
269
|
+
@media (max-width: 575.98px) {
|
|
270
|
+
.table-ledger .u-name {
|
|
271
|
+
font-size: 12px;
|
|
272
|
+
line-height: normal;
|
|
273
|
+
margin-bottom: 2px;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// An identity-cell name is often a link, so it inherits Bootstrap's
|
|
277
|
+
// underline-on-hover. The colour shift is the affordance here.
|
|
278
|
+
.table-ledger a.u-name,
|
|
279
|
+
.table-ledger a.u-name:hover {
|
|
280
|
+
text-decoration: none;
|
|
281
|
+
}
|
|
282
|
+
// Identity-cell subline. `.u-email` is in the group because an identity cell
|
|
283
|
+
// renders the secondary line as either a generic .lx-text-xs or a .u-email.
|
|
284
|
+
.table-ledger .u-meta > .u-email,
|
|
285
|
+
.table-ledger .u-meta > .lx-text-xs {
|
|
286
|
+
font-size: 12px;
|
|
287
|
+
font-weight: 400;
|
|
288
|
+
line-height: 15px;
|
|
289
|
+
color: var(--color-text-subtle);
|
|
290
|
+
}
|
|
291
|
+
.table-ledger .u-row-actions {
|
|
292
|
+
gap: 6px;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Ledger tables: 20px edge padding so header labels + first/last cells align
|
|
296
|
+
// with the card edge — shared across every .table-ledger surface.
|
|
297
|
+
.table-ledger thead th:first-child,
|
|
298
|
+
.table-ledger tbody td:first-child {
|
|
299
|
+
padding-left: 20px;
|
|
300
|
+
}
|
|
301
|
+
.table-ledger thead th:last-child,
|
|
302
|
+
.table-ledger tbody td:last-child {
|
|
303
|
+
padding-right: 20px;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Pagination footer — rows-per-page + range + prev/next, sits flush under a
|
|
307
|
+
// table.
|
|
308
|
+
.table-pagination {
|
|
309
|
+
display: flex;
|
|
310
|
+
align-items: center;
|
|
311
|
+
justify-content: flex-end;
|
|
312
|
+
gap: 22px;
|
|
313
|
+
padding: 12px 16px;
|
|
314
|
+
border-top: 1px solid var(--color-border);
|
|
315
|
+
font-size: 13px;
|
|
316
|
+
color: var(--color-text-muted);
|
|
317
|
+
flex-wrap: wrap;
|
|
318
|
+
}
|
|
319
|
+
// Rows-per-page sits at the leading edge; range + nav stay grouped on the
|
|
320
|
+
// right.
|
|
321
|
+
.tpg-rows {
|
|
322
|
+
display: inline-flex;
|
|
323
|
+
align-items: center;
|
|
324
|
+
gap: 8px;
|
|
325
|
+
white-space: nowrap;
|
|
326
|
+
margin-right: auto;
|
|
327
|
+
font-size: 12px;
|
|
328
|
+
font-weight: 500;
|
|
329
|
+
text-transform: uppercase;
|
|
330
|
+
color: var(--color-text-subtle);
|
|
331
|
+
}
|
|
332
|
+
.select.tpg-select {
|
|
333
|
+
width: auto;
|
|
334
|
+
min-width: 64px;
|
|
335
|
+
height: 32px;
|
|
336
|
+
min-height: 32px;
|
|
337
|
+
padding: 0 28px 0 12px;
|
|
338
|
+
font-size: 13px;
|
|
339
|
+
font-weight: 600;
|
|
340
|
+
border-radius: var(--radius);
|
|
341
|
+
background-position: right 8px center;
|
|
342
|
+
background-size: 14px;
|
|
343
|
+
}
|
|
344
|
+
.tpg-range {
|
|
345
|
+
font-variant-numeric: tabular-nums;
|
|
346
|
+
white-space: nowrap;
|
|
347
|
+
font-size: 14px;
|
|
348
|
+
font-weight: 500;
|
|
349
|
+
color: var(--color-text-muted);
|
|
350
|
+
}
|
|
351
|
+
.tpg-nav {
|
|
352
|
+
display: inline-flex;
|
|
353
|
+
align-items: center;
|
|
354
|
+
gap: 4px;
|
|
355
|
+
}
|
|
356
|
+
.tpg-arrow {
|
|
357
|
+
display: grid;
|
|
358
|
+
place-items: center;
|
|
359
|
+
width: 32px;
|
|
360
|
+
height: 32px;
|
|
361
|
+
border: none;
|
|
362
|
+
background: transparent;
|
|
363
|
+
color: var(--color-text-muted);
|
|
364
|
+
border-radius: 50%;
|
|
365
|
+
cursor: pointer;
|
|
366
|
+
transition:
|
|
367
|
+
background 0.14s ease,
|
|
368
|
+
color 0.14s ease;
|
|
369
|
+
}
|
|
370
|
+
.tpg-arrow:hover:not(:disabled) {
|
|
371
|
+
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
372
|
+
color: var(--color-primary);
|
|
373
|
+
}
|
|
374
|
+
// Text-5 already reads as "unavailable" — no opacity dimming on top of it.
|
|
375
|
+
.tpg-arrow:disabled {
|
|
376
|
+
color: var(--color-border-strong);
|
|
377
|
+
cursor: not-allowed;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Horizontal scroll guard + flush-fit inside a card. Wrapping the table here
|
|
381
|
+
// also stops `.card > .table` header-corner rounding from applying mid-card.
|
|
382
|
+
.table-wrap {
|
|
383
|
+
overflow-x: auto;
|
|
384
|
+
max-width: 100%;
|
|
385
|
+
// Keep the horizontal swipe inside the table — don't let it drag the
|
|
386
|
+
// page — and give touch devices momentum scrolling. Scrollbar is
|
|
387
|
+
// thinned/hidden so the scroll feels native.
|
|
388
|
+
overscroll-behavior-x: contain;
|
|
389
|
+
-webkit-overflow-scrolling: touch;
|
|
390
|
+
scrollbar-width: thin;
|
|
391
|
+
scrollbar-color: var(--color-border-strong) transparent;
|
|
392
|
+
}
|
|
393
|
+
.table-wrap::-webkit-scrollbar {
|
|
394
|
+
height: 6px;
|
|
395
|
+
}
|
|
396
|
+
.table-wrap::-webkit-scrollbar-track {
|
|
397
|
+
background: transparent;
|
|
398
|
+
}
|
|
399
|
+
.table-wrap::-webkit-scrollbar-thumb {
|
|
400
|
+
background: var(--color-border-strong);
|
|
401
|
+
border-radius: 999px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Row-selection checkbox column.
|
|
405
|
+
.table-ledger th.lx-select-cell,
|
|
406
|
+
.table-ledger td.lx-select-cell {
|
|
407
|
+
width: 44px;
|
|
408
|
+
min-width: 44px;
|
|
409
|
+
max-width: 44px;
|
|
410
|
+
text-align: center;
|
|
411
|
+
padding-left: 12px;
|
|
412
|
+
padding-right: 0;
|
|
413
|
+
}
|
|
414
|
+
// Row state goes through Bootstrap's own --bs-table-bg-state, NOT `background`.
|
|
415
|
+
// Bootstrap 5.3 paints cell state with `box-shadow: inset 0 0 0 9999px
|
|
416
|
+
// var(--bs-table-bg-state, …)`, which covers whatever background the cell has —
|
|
417
|
+
// so setting `background` on the <tr> OR the <td> is invisible. Setting the
|
|
418
|
+
// variable on the row is what `.table-active` itself does, and it inherits to
|
|
419
|
+
// every cell. Brand tint, not grey: grey reads as "disabled", not "chosen".
|
|
420
|
+
.table-ledger tbody tr.is-selected {
|
|
421
|
+
--bs-table-bg-state: var(--color-primary-soft);
|
|
422
|
+
}
|
|
423
|
+
.table-ledger tbody tr.is-selected:hover {
|
|
424
|
+
--bs-table-bg-state: color-mix(in srgb, var(--color-primary) 14%, transparent);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Row status affordance — a colour bar on the leading edge, so a row's state is
|
|
428
|
+
// legible without relying on a tint alone.
|
|
429
|
+
.table-ledger tbody tr[data-status] > td:first-child {
|
|
430
|
+
box-shadow: inset 3px 0 0 0 var(--color-border-strong);
|
|
431
|
+
}
|
|
432
|
+
.table-ledger tbody tr[data-status="success"] > td:first-child {
|
|
433
|
+
box-shadow: inset 3px 0 0 0 var(--color-success);
|
|
434
|
+
}
|
|
435
|
+
.table-ledger tbody tr[data-status="warning"] > td:first-child {
|
|
436
|
+
box-shadow: inset 3px 0 0 0 var(--color-warning);
|
|
437
|
+
}
|
|
438
|
+
.table-ledger tbody tr[data-status="danger"] > td:first-child {
|
|
439
|
+
box-shadow: inset 3px 0 0 0 var(--color-danger);
|
|
440
|
+
}
|
|
441
|
+
.table-ledger tbody tr[data-status="info"] > td:first-child {
|
|
442
|
+
box-shadow: inset 3px 0 0 0 var(--color-info);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Row actions — icon-only buttons replacing a kebab dropdown.
|
|
446
|
+
.u-row-actions {
|
|
447
|
+
display: flex;
|
|
448
|
+
align-items: center;
|
|
449
|
+
justify-content: flex-end;
|
|
450
|
+
gap: 6px;
|
|
451
|
+
// Action cells never wrap — a status hint stays on one line.
|
|
452
|
+
white-space: nowrap;
|
|
453
|
+
}
|
|
454
|
+
.u-action {
|
|
455
|
+
display: inline-grid;
|
|
456
|
+
place-items: center;
|
|
457
|
+
flex: none;
|
|
458
|
+
width: 36px;
|
|
459
|
+
height: 36px;
|
|
460
|
+
padding: 0;
|
|
461
|
+
border: none;
|
|
462
|
+
background: var(--color-bg-subtle);
|
|
463
|
+
border-radius: 999px;
|
|
464
|
+
color: var(--color-text-subtle);
|
|
465
|
+
cursor: pointer;
|
|
466
|
+
transition:
|
|
467
|
+
background 0.12s ease,
|
|
468
|
+
color 0.12s ease;
|
|
469
|
+
position: relative;
|
|
470
|
+
}
|
|
471
|
+
// Touch pointers need a 44px minimum target. Grow the hit area, not the
|
|
472
|
+
// ink, so row density and alignment are untouched.
|
|
473
|
+
@media (pointer: coarse) {
|
|
474
|
+
.u-action::after {
|
|
475
|
+
content: "";
|
|
476
|
+
position: absolute;
|
|
477
|
+
top: 50%;
|
|
478
|
+
left: 50%;
|
|
479
|
+
transform: translate(-50%, -50%);
|
|
480
|
+
width: 44px;
|
|
481
|
+
height: 44px;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
.u-action svg {
|
|
485
|
+
flex: none;
|
|
486
|
+
}
|
|
487
|
+
.u-action:hover:not(:disabled) {
|
|
488
|
+
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
489
|
+
color: var(--color-primary);
|
|
490
|
+
}
|
|
491
|
+
.u-action:disabled {
|
|
492
|
+
opacity: 0.4;
|
|
493
|
+
cursor: not-allowed;
|
|
494
|
+
}
|
|
495
|
+
.u-action.danger {
|
|
496
|
+
color: var(--color-danger-text);
|
|
497
|
+
}
|
|
498
|
+
.u-action.danger:hover:not(:disabled) {
|
|
499
|
+
background: color-mix(in srgb, var(--color-danger) 10%, transparent);
|
|
500
|
+
color: var(--color-danger-text);
|
|
501
|
+
}
|
|
502
|
+
.u-action.u-action-preview {
|
|
503
|
+
background: var(--color-primary-soft);
|
|
504
|
+
color: var(--color-primary);
|
|
505
|
+
}
|
|
506
|
+
.u-action.u-action-download {
|
|
507
|
+
background: var(--color-success-soft);
|
|
508
|
+
color: var(--color-success-text);
|
|
509
|
+
}
|
|
510
|
+
.u-action.u-action-share {
|
|
511
|
+
background: var(--color-warning-soft);
|
|
512
|
+
color: var(--color-warning-text);
|
|
513
|
+
}
|
|
514
|
+
.u-action.u-action-preview:hover:not(:disabled) {
|
|
515
|
+
background: var(--color-primary-soft);
|
|
516
|
+
color: var(--color-primary);
|
|
517
|
+
}
|
|
518
|
+
.u-action.u-action-download:hover:not(:disabled) {
|
|
519
|
+
background: var(--color-success-soft);
|
|
520
|
+
color: var(--color-success-text);
|
|
521
|
+
}
|
|
522
|
+
.u-action.u-action-share:hover:not(:disabled) {
|
|
523
|
+
background: var(--color-warning-soft);
|
|
524
|
+
color: var(--color-warning-text);
|
|
525
|
+
}
|
|
526
|
+
.u-action.u-action-danger:hover {
|
|
527
|
+
color: var(--color-danger-text);
|
|
528
|
+
background: color-mix(in srgb, var(--color-danger) 10%, transparent);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Identity cell — avatar + stacked name/email.
|
|
532
|
+
.u-cell {
|
|
533
|
+
display: flex;
|
|
534
|
+
align-items: center;
|
|
535
|
+
gap: 14px;
|
|
536
|
+
}
|
|
537
|
+
.u-avatar {
|
|
538
|
+
width: 40px;
|
|
539
|
+
height: 40px;
|
|
540
|
+
border-radius: 50%;
|
|
541
|
+
flex: none;
|
|
542
|
+
display: grid;
|
|
543
|
+
place-items: center;
|
|
544
|
+
font-size: 16px;
|
|
545
|
+
font-weight: 600;
|
|
546
|
+
color: var(--color-primary);
|
|
547
|
+
background: color-mix(in srgb, var(--color-primary) 20%, transparent);
|
|
548
|
+
}
|
|
549
|
+
.u-meta {
|
|
550
|
+
display: flex;
|
|
551
|
+
flex-direction: column;
|
|
552
|
+
min-width: 0;
|
|
553
|
+
}
|
|
554
|
+
.u-name {
|
|
555
|
+
display: inline-flex;
|
|
556
|
+
align-items: center;
|
|
557
|
+
gap: 8px;
|
|
558
|
+
font-size: 14px;
|
|
559
|
+
font-weight: 600;
|
|
560
|
+
color: var(--color-text);
|
|
561
|
+
}
|
|
562
|
+
.u-email {
|
|
563
|
+
font-size: 14px;
|
|
564
|
+
font-weight: 400;
|
|
565
|
+
color: var(--color-text-muted);
|
|
566
|
+
}
|
|
567
|
+
.u-you {
|
|
568
|
+
padding: 1px 7px;
|
|
569
|
+
font-size: 10.5px;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Table extras — a sortable header and numeric cells. These sit AFTER the
|
|
573
|
+
// .table-ledger rules above on purpose: they are opt-in additions on top of
|
|
574
|
+
// whichever table variant is in use, not part of the base a variant overrides.
|
|
575
|
+
.table th.sortable {
|
|
576
|
+
display: flex;
|
|
577
|
+
align-items: center;
|
|
578
|
+
gap: 6px;
|
|
579
|
+
cursor: pointer;
|
|
580
|
+
user-select: none;
|
|
581
|
+
}
|
|
582
|
+
.table th.sortable .sort-icon {
|
|
583
|
+
// Reserved space so the header does not jump width when the arrow appears.
|
|
584
|
+
width: 12px;
|
|
585
|
+
flex: none;
|
|
586
|
+
color: var(--color-text-subtle);
|
|
587
|
+
opacity: 0;
|
|
588
|
+
transition: opacity 0.12s ease;
|
|
589
|
+
}
|
|
590
|
+
.table th.sortable:hover .sort-icon {
|
|
591
|
+
opacity: 0.6;
|
|
592
|
+
}
|
|
593
|
+
.table th.sortable[aria-sort="ascending"] .sort-icon,
|
|
594
|
+
.table th.sortable[aria-sort="descending"] .sort-icon {
|
|
595
|
+
opacity: 1;
|
|
596
|
+
color: var(--color-primary);
|
|
597
|
+
}
|
|
598
|
+
.table th.sortable[aria-sort="descending"] .sort-icon {
|
|
599
|
+
transform: rotate(180deg);
|
|
600
|
+
}
|
|
601
|
+
.table th.sortable[aria-sort="ascending"],
|
|
602
|
+
.table th.sortable[aria-sort="descending"] {
|
|
603
|
+
color: var(--color-text);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Numeric cells: right-aligned with tabular figures, so digits line up in a column
|
|
607
|
+
// and a total sits directly under the values it sums.
|
|
608
|
+
.table th.num,
|
|
609
|
+
.table td.num {
|
|
610
|
+
text-align: right;
|
|
611
|
+
font-variant-numeric: tabular-nums;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// ============================================================================
|