@aortl/admin-css 0.16.0 → 0.16.2
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/CHANGELOG.md +54 -0
- package/dist/admin.css +25 -4
- package/dist/admin.min.css +1 -1
- package/dist/admin.scoped.css +25 -4
- package/dist/admin.scoped.min.css +18 -15
- package/package.json +3 -2
- package/src/components/accordion.css +7 -10
- package/src/components/alert.css +6 -21
- package/src/components/app-shell.css +2 -4
- package/src/components/badge.css +1 -5
- package/src/components/breadcrumbs.css +3 -8
- package/src/components/button-group.css +7 -19
- package/src/components/button.css +7 -23
- package/src/components/card.css +13 -20
- package/src/components/chart.css +26 -83
- package/src/components/checkbox.css +5 -7
- package/src/components/code-block.css +2 -4
- package/src/components/container.css +3 -7
- package/src/components/dialog.css +13 -13
- package/src/components/field.css +4 -4
- package/src/components/footer.css +2 -3
- package/src/components/indicator.css +9 -28
- package/src/components/input-group.css +3 -6
- package/src/components/input.css +2 -5
- package/src/components/kbd.css +7 -13
- package/src/components/link.css +3 -5
- package/src/components/menu.css +11 -21
- package/src/components/navbar.css +3 -7
- package/src/components/pagination.css +1 -4
- package/src/components/progress.css +3 -7
- package/src/components/property-list.css +5 -14
- package/src/components/prose.css +7 -21
- package/src/components/radio.css +5 -5
- package/src/components/select.css +6 -9
- package/src/components/sidebar.css +2 -7
- package/src/components/spinner.css +1 -4
- package/src/components/stat-card.css +5 -16
- package/src/components/switch.css +5 -3
- package/src/components/table.css +20 -30
- package/src/components/tabs.css +14 -32
- package/src/components/textarea.css +3 -7
- package/src/components/tooltip.css +6 -16
- package/src/fonts.css +8 -28
- package/src/theme.css +32 -100
- package/src/utilities.css +11 -37
package/src/components/menu.css
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
@layer components {
|
|
2
|
-
/* Native <details> drives open state — no JS
|
|
3
|
-
position: absolute child of the relative .menu root. */
|
|
2
|
+
/* Native <details> drives open state — no JS. */
|
|
4
3
|
.menu {
|
|
5
4
|
@apply relative inline-block;
|
|
6
5
|
}
|
|
7
6
|
|
|
8
|
-
/* Behaviour —
|
|
9
|
-
additional visual classes (e.g. `btn btn-primary` for a split button). */
|
|
7
|
+
/* Behaviour only — visual classes (e.g. `btn btn-primary`) may stack on top. */
|
|
10
8
|
.menu-trigger {
|
|
11
9
|
@apply inline-flex items-center gap-1.5
|
|
12
10
|
cursor-pointer select-none
|
|
@@ -19,17 +17,14 @@
|
|
|
19
17
|
display: none;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
/* Default appearance — bows out when `.btn`
|
|
23
|
-
button classes (bg, hover, padding, text colour) take over cleanly. */
|
|
20
|
+
/* Default appearance — bows out when `.btn` supplies its own. */
|
|
24
21
|
.menu-trigger:not(.btn) {
|
|
25
22
|
@apply px-2.5 py-1.5 rounded-md text-sm leading-none
|
|
26
23
|
text-text bg-transparent
|
|
27
24
|
hover:bg-surface-strong;
|
|
28
25
|
}
|
|
29
26
|
|
|
30
|
-
/*
|
|
31
|
-
muted text on the default trigger, content colour on a btn-primary
|
|
32
|
-
split button, etc. Sized in em so it scales with btn-sm / btn-lg. */
|
|
27
|
+
/* em-sized so the chevron scales with btn-sm / btn-lg. */
|
|
33
28
|
.menu-trigger::after {
|
|
34
29
|
content: "";
|
|
35
30
|
width: 0.5em;
|
|
@@ -41,8 +36,7 @@
|
|
|
41
36
|
flex-shrink: 0;
|
|
42
37
|
}
|
|
43
38
|
|
|
44
|
-
/*
|
|
45
|
-
their chevron instead of letting it sit at the start. */
|
|
39
|
+
/* A text-less trigger (split-button dropdown half) centers its chevron. */
|
|
46
40
|
.menu-trigger:empty {
|
|
47
41
|
@apply justify-center;
|
|
48
42
|
}
|
|
@@ -59,15 +53,9 @@
|
|
|
59
53
|
border border-border rounded-lg shadow-md;
|
|
60
54
|
}
|
|
61
55
|
|
|
62
|
-
/*
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
scroll-clipped surface like a card body. `anchor-scope` keeps each
|
|
66
|
-
menu's anchor lookups isolated so multiple menus on the page don't
|
|
67
|
-
cross-target, and `position-try-fallbacks` flips the popup above the
|
|
68
|
-
trigger when there isn't room below. Browsers without anchor
|
|
69
|
-
positioning (Firefox, as of early 2026) fall back to the absolute
|
|
70
|
-
rules above and clip as before. */
|
|
56
|
+
/* Anchor positioning makes the popup fixed so it escapes ancestor overflow
|
|
57
|
+
clipping (e.g. inside a <dialog>); browsers without it (Firefox, early
|
|
58
|
+
2026) fall back to the absolute rules above and clip as before. */
|
|
71
59
|
@supports (anchor-name: --x) {
|
|
72
60
|
.menu {
|
|
73
61
|
anchor-scope: --menu-trigger;
|
|
@@ -100,13 +88,15 @@
|
|
|
100
88
|
hover:bg-surface-muted
|
|
101
89
|
focus-visible:bg-surface-muted focus-visible:outline-none
|
|
102
90
|
disabled:opacity-50 disabled:cursor-not-allowed;
|
|
91
|
+
/* The popup has no max-width — break long labels instead of growing it. */
|
|
92
|
+
overflow-wrap: break-word;
|
|
93
|
+
min-width: 0;
|
|
103
94
|
}
|
|
104
95
|
|
|
105
96
|
.menu-item[aria-disabled="true"] {
|
|
106
97
|
@apply opacity-50 cursor-not-allowed;
|
|
107
98
|
}
|
|
108
99
|
|
|
109
|
-
/* Don't let a constrained menu width squish a leading/trailing icon. */
|
|
110
100
|
.menu-trigger > :is(i, svg),
|
|
111
101
|
.menu-item > :is(i, svg) {
|
|
112
102
|
flex-shrink: 0;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
@layer components {
|
|
2
|
-
/*
|
|
3
|
-
`--color-system-accent`
|
|
4
|
-
that variable at :root (see also `.footer` and `.brand-tile`). */
|
|
2
|
+
/* The bottom stripe brand-shifts when the consuming app overrides
|
|
3
|
+
`--color-system-accent` at :root (see also `.footer` and `.brand-tile`). */
|
|
5
4
|
.navbar {
|
|
6
5
|
@apply flex items-center gap-3 px-4 h-12
|
|
7
6
|
bg-surface-muted text-text
|
|
@@ -31,19 +30,16 @@
|
|
|
31
30
|
@apply bg-primary-muted text-primary;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
/* Keep brand/item icons at their intrinsic size in a crowded bar. */
|
|
35
33
|
.navbar-brand > :is(i, svg),
|
|
36
34
|
.navbar-item > :is(i, svg) {
|
|
37
35
|
flex-shrink: 0;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
/* Right-aligned slot for actions: shop selector, user menu, etc. */
|
|
41
38
|
.navbar-actions {
|
|
42
39
|
@apply flex items-center gap-2 ml-auto;
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
/* Hamburger
|
|
46
|
-
with one element + two box-shadow strokes — no SVG needed. */
|
|
42
|
+
/* Hamburger — three lines from one element + two box-shadow strokes. */
|
|
47
43
|
.navbar-mobile-toggle {
|
|
48
44
|
@apply inline-flex items-center justify-center size-9 -ml-2
|
|
49
45
|
rounded-md text-text bg-transparent
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
@apply inline-flex;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
/* Square button shared by number, prev/next, and ellipsis containers. */
|
|
12
11
|
.page-link {
|
|
13
12
|
@apply inline-flex items-center justify-center
|
|
14
13
|
min-w-8 h-8 px-2
|
|
@@ -23,8 +22,7 @@
|
|
|
23
22
|
disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
/*
|
|
27
|
-
metaphor is consistent. */
|
|
25
|
+
/* Same surface as a sidebar's active item. */
|
|
28
26
|
.page-link.active,
|
|
29
27
|
.page-link[aria-current="page"] {
|
|
30
28
|
@apply bg-primary-muted text-primary border-primary-muted;
|
|
@@ -34,7 +32,6 @@
|
|
|
34
32
|
@apply opacity-50 cursor-not-allowed pointer-events-none;
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
/* Ellipsis — non-interactive placeholder between groups of pages. */
|
|
38
35
|
.page-ellipsis {
|
|
39
36
|
@apply inline-flex items-center justify-center
|
|
40
37
|
min-w-8 h-8 px-2
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
@layer components {
|
|
2
|
-
/*
|
|
3
|
-
platform default; the track is the element itself, the fill comes from
|
|
4
|
-
::-webkit-progress-value / ::-moz-progress-bar. */
|
|
2
|
+
/* The track is the element itself; the fill is the engine-specific value pseudo. */
|
|
5
3
|
.progress {
|
|
6
4
|
appearance: none;
|
|
7
5
|
-webkit-appearance: none;
|
|
@@ -32,7 +30,6 @@
|
|
|
32
30
|
transition: inline-size 200ms ease;
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
/* Sizes */
|
|
36
33
|
.progress-sm {
|
|
37
34
|
height: 0.25rem;
|
|
38
35
|
}
|
|
@@ -54,9 +51,8 @@
|
|
|
54
51
|
color: var(--color-danger);
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
/*
|
|
58
|
-
|
|
59
|
-
bar itself. */
|
|
54
|
+
/* When indeterminate, WebKit hides the value pseudo and Firefox draws it
|
|
55
|
+
full-width — blank both and animate a gradient on the bar itself. */
|
|
60
56
|
.progress:indeterminate {
|
|
61
57
|
background-image: linear-gradient(
|
|
62
58
|
90deg,
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
@layer components {
|
|
2
|
-
/* Outer section — owns striped, hide-if-empty modifiers. Title
|
|
3
|
-
(optional) and the items grid stack vertically. */
|
|
4
2
|
.property-list {
|
|
5
3
|
@apply flex flex-col text-sm text-text;
|
|
6
4
|
}
|
|
@@ -9,8 +7,7 @@
|
|
|
9
7
|
@apply text-sm font-bold text-text mb-2;
|
|
10
8
|
}
|
|
11
9
|
|
|
12
|
-
/* The <dl>
|
|
13
|
-
and <dd> into column 2. */
|
|
10
|
+
/* The <dl> is the grid; <dt>/<dd> auto-flow into the two tracks. */
|
|
14
11
|
.property-list-items {
|
|
15
12
|
display: grid;
|
|
16
13
|
grid-template-columns: max-content 1fr;
|
|
@@ -25,15 +22,12 @@
|
|
|
25
22
|
@apply text-text-muted;
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
/* `min-w-0` lets the
|
|
29
|
-
|
|
30
|
-
file paths) break instead of overflowing the column. */
|
|
25
|
+
/* `min-w-0` lets the track shrink below its content so long unbreakable
|
|
26
|
+
values break instead of overflowing the column. */
|
|
31
27
|
.property-list-value {
|
|
32
28
|
@apply gap-2 min-w-0 break-words;
|
|
33
29
|
}
|
|
34
30
|
|
|
35
|
-
/* Compact density — tighter rows for sidebar info blocks or very many
|
|
36
|
-
short attributes. */
|
|
37
31
|
.property-list-compact .property-list-title {
|
|
38
32
|
@apply mb-1;
|
|
39
33
|
}
|
|
@@ -43,7 +37,6 @@
|
|
|
43
37
|
@apply px-2 py-0.5 min-h-6;
|
|
44
38
|
}
|
|
45
39
|
|
|
46
|
-
/* Numeric — right-align + tabular-nums on the value. */
|
|
47
40
|
.property-list-value-numeric {
|
|
48
41
|
justify-content: flex-end;
|
|
49
42
|
@apply tabular-nums;
|
|
@@ -60,8 +53,7 @@
|
|
|
60
53
|
display: none;
|
|
61
54
|
}
|
|
62
55
|
|
|
63
|
-
/*
|
|
64
|
-
unless this specific value opts in. */
|
|
56
|
+
/* Always emitted by the Value; kept out of layout unless the value opts in. */
|
|
65
57
|
.property-list-copy {
|
|
66
58
|
@apply inline-flex items-center justify-center
|
|
67
59
|
ml-auto rounded
|
|
@@ -82,8 +74,7 @@
|
|
|
82
74
|
pointer-events: none;
|
|
83
75
|
}
|
|
84
76
|
|
|
85
|
-
/* Reveal on hover of
|
|
86
|
-
button focus, or while feedback is active. */
|
|
77
|
+
/* Reveal on hover of the row's label or value, on focus, or during feedback. */
|
|
87
78
|
.property-list-label:has(+ .property-list-value-copyable):hover
|
|
88
79
|
+ .property-list-value
|
|
89
80
|
.property-list-copy,
|
package/src/components/prose.css
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
@layer components {
|
|
2
|
-
/*
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* margins, list markers, and link styling from bare elements (so admin chrome
|
|
6
|
-
* stays neutral); `.prose` re-establishes them for its descendants only, using
|
|
7
|
-
* the semantic tokens so it follows dark mode and `--color-*` overrides.
|
|
8
|
-
*
|
|
9
|
-
* Descendant rules use `:where()` to stay at specificity 0 — a consumer's own
|
|
10
|
-
* `.prose a { … }` wins without `!important`.
|
|
11
|
-
*/
|
|
2
|
+
/* Styles rendered HTML (markdown, CMS bodies) that can't carry the system's
|
|
3
|
+
class names — re-establishes what the global reset strips. Descendant rules
|
|
4
|
+
use `:where()` so a consumer's own `.prose a { … }` wins without `!important`. */
|
|
12
5
|
.prose {
|
|
13
6
|
@apply text-sm leading-normal text-text;
|
|
7
|
+
/* Break long URLs/tokens; the scrolling `<pre>` keeps its own `white-space: pre`. */
|
|
8
|
+
overflow-wrap: break-word;
|
|
14
9
|
}
|
|
15
10
|
|
|
16
|
-
/* Vertical rhythm — space between flow blocks, none at the container edges. */
|
|
17
11
|
.prose :where(p, ul, ol, blockquote, pre, table, figure) {
|
|
18
12
|
@apply my-3;
|
|
19
13
|
}
|
|
@@ -24,8 +18,7 @@
|
|
|
24
18
|
margin-bottom: 0;
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
/*
|
|
28
|
-
sectioning rhythm. The edge reset above zeroes a leading heading's margin. */
|
|
21
|
+
/* base.css already sizes h1–h3; add h4–h6 and the sectioning rhythm. */
|
|
29
22
|
.prose :where(h1, h2, h3, h4, h5, h6) {
|
|
30
23
|
@apply mt-6 mb-2 font-semibold leading-tight;
|
|
31
24
|
}
|
|
@@ -36,7 +29,6 @@
|
|
|
36
29
|
@apply text-xs uppercase tracking-wide text-text-muted;
|
|
37
30
|
}
|
|
38
31
|
|
|
39
|
-
/* Lists — restore the markers the reset removes. */
|
|
40
32
|
.prose :where(ul) {
|
|
41
33
|
@apply list-disc ps-5;
|
|
42
34
|
}
|
|
@@ -50,7 +42,6 @@
|
|
|
50
42
|
.prose :where(li) :where(p) {
|
|
51
43
|
@apply my-1;
|
|
52
44
|
}
|
|
53
|
-
/* Nested lists hug their parent item rather than taking full block spacing. */
|
|
54
45
|
.prose :where(li > ul, li > ol) {
|
|
55
46
|
@apply my-1;
|
|
56
47
|
}
|
|
@@ -62,15 +53,13 @@
|
|
|
62
53
|
focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus;
|
|
63
54
|
}
|
|
64
55
|
|
|
65
|
-
/* Inline code — a tinted chip. */
|
|
66
56
|
.prose :where(code) {
|
|
67
57
|
@apply rounded border border-border bg-code-surface px-1 py-0.5
|
|
68
58
|
font-mono text-code-text;
|
|
69
59
|
font-size: 0.85em;
|
|
70
60
|
}
|
|
71
61
|
|
|
72
|
-
/*
|
|
73
|
-
<code> so a highlighter's own tokens show through. */
|
|
62
|
+
/* Mirrors .code-block; nested <code> sheds the chip so highlighter tokens show. */
|
|
74
63
|
.prose :where(pre) {
|
|
75
64
|
@apply rounded-lg bg-code-surface p-3 font-mono text-sm text-code-text;
|
|
76
65
|
white-space: pre;
|
|
@@ -81,12 +70,10 @@
|
|
|
81
70
|
font-size: inherit;
|
|
82
71
|
}
|
|
83
72
|
|
|
84
|
-
/* Blockquote — a quiet inline-start rule. */
|
|
85
73
|
.prose :where(blockquote) {
|
|
86
74
|
@apply border-s-2 border-border-strong ps-3 text-text-muted;
|
|
87
75
|
}
|
|
88
76
|
|
|
89
|
-
/* Thematic break. */
|
|
90
77
|
.prose :where(hr) {
|
|
91
78
|
@apply my-6 border-0 border-t border-border;
|
|
92
79
|
}
|
|
@@ -102,7 +89,6 @@
|
|
|
102
89
|
@apply font-semibold text-text-muted;
|
|
103
90
|
}
|
|
104
91
|
|
|
105
|
-
/* Media + emphasis. */
|
|
106
92
|
.prose :where(img) {
|
|
107
93
|
@apply h-auto max-w-full rounded-md;
|
|
108
94
|
}
|
package/src/components/radio.css
CHANGED
|
@@ -24,9 +24,8 @@
|
|
|
24
24
|
@apply inline-flex size-1.5 rounded-full bg-primary-content;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/*
|
|
28
|
-
|
|
29
|
-
renders identically (the dot matches .radio-indicator's size + colour). */
|
|
27
|
+
/* Mirrors the Base UI span variant above — the ::after dot must match
|
|
28
|
+
.radio-indicator's size and colour. */
|
|
30
29
|
input.radio {
|
|
31
30
|
@apply appearance-none border-border-strong hover:border-text-muted;
|
|
32
31
|
}
|
|
@@ -49,10 +48,11 @@
|
|
|
49
48
|
@apply flex-col gap-2 items-start;
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
/* A <label> wrapping a radio + text lays out inline with a small gap.
|
|
53
|
-
Covers both the vanilla input.radio and Base UI's span.radio. */
|
|
54
51
|
label:has(> .radio) {
|
|
55
52
|
@apply inline-flex items-center gap-2 cursor-pointer;
|
|
53
|
+
/* Long labels break beside the `shrink-0` control instead of overflowing. */
|
|
54
|
+
overflow-wrap: break-word;
|
|
55
|
+
min-width: 0;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
label:has(> .radio:disabled),
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
@apply border-danger hover:border-danger focus-visible:outline-danger;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
/* Sizes */
|
|
30
29
|
.select-sm {
|
|
31
30
|
@apply text-xs px-2.5 py-1.5;
|
|
32
31
|
}
|
|
@@ -35,14 +34,9 @@
|
|
|
35
34
|
@apply text-base px-4 py-2.5;
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
/*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
tinted `::after` (the React trigger uses a real `.select-icon` SVG), and
|
|
42
|
-
data URIs can't read CSS variables — so the chevron can't ride the
|
|
43
|
-
`--color-text-muted` token directly. The stroke is Flexoki `base-500`
|
|
44
|
-
(#848484), the same mid-gray as the token's dark-mode value, which reads
|
|
45
|
-
as a neutral chevron in both light and dark mode. */
|
|
37
|
+
/* A replaced <select> can't host a tinted ::after and data URIs can't read
|
|
38
|
+
CSS variables, so the chevron is a background-image with a hard-coded
|
|
39
|
+
stroke: Flexoki base-500 (#848484), which reads neutral in both modes. */
|
|
46
40
|
select.select {
|
|
47
41
|
appearance: none;
|
|
48
42
|
padding-right: 2rem;
|
|
@@ -90,6 +84,9 @@
|
|
|
90
84
|
.select-item {
|
|
91
85
|
@apply flex items-center gap-2 px-3 py-1.5 text-sm
|
|
92
86
|
cursor-pointer select-none outline-none;
|
|
87
|
+
/* Break long option labels rather than grow the popup horizontally. */
|
|
88
|
+
overflow-wrap: break-word;
|
|
89
|
+
min-width: 0;
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
.select-item[data-highlighted] {
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
transition: width 150ms ease;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
/* Hidden checkbox
|
|
11
|
-
<Sidebar.CollapseToggle>, but :has() finds it from anywhere in .sidebar. */
|
|
10
|
+
/* Hidden checkbox; :has() reads the collapsed state from anywhere in .sidebar. */
|
|
12
11
|
.sidebar-toggle {
|
|
13
12
|
position: absolute;
|
|
14
13
|
width: 1px;
|
|
@@ -24,7 +23,6 @@
|
|
|
24
23
|
width: var(--app-shell-sidebar-w-collapsed, 56px);
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
/* Hide labels, group headings, badges, and tree panels in collapsed mode. */
|
|
28
26
|
.sidebar:has(.sidebar-toggle:checked) .sidebar-label,
|
|
29
27
|
.sidebar:has(.sidebar-toggle:checked) .sidebar-group-label,
|
|
30
28
|
.sidebar:has(.sidebar-toggle:checked) .sidebar-badge,
|
|
@@ -33,7 +31,6 @@
|
|
|
33
31
|
display: none;
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
/* Center the icon in collapsed mode. */
|
|
37
34
|
.sidebar:has(.sidebar-toggle:checked) .sidebar-item,
|
|
38
35
|
.sidebar:has(.sidebar-toggle:checked) .sidebar-subitem,
|
|
39
36
|
.sidebar:has(.sidebar-toggle:checked) .sidebar-collapsible-trigger {
|
|
@@ -93,7 +90,6 @@
|
|
|
93
90
|
bg-surface-strong text-text-muted;
|
|
94
91
|
}
|
|
95
92
|
|
|
96
|
-
/* Native <details> for tree groups. */
|
|
97
93
|
.sidebar-collapsible {
|
|
98
94
|
@apply flex flex-col;
|
|
99
95
|
interpolate-size: allow-keywords;
|
|
@@ -136,8 +132,7 @@
|
|
|
136
132
|
overflow: hidden;
|
|
137
133
|
}
|
|
138
134
|
|
|
139
|
-
/*
|
|
140
|
-
Older browsers degrade to the native instant toggle. */
|
|
135
|
+
/* Browsers without ::details-content degrade to the instant native toggle. */
|
|
141
136
|
.sidebar-collapsible::details-content {
|
|
142
137
|
opacity: 0;
|
|
143
138
|
height: 0;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
@layer components {
|
|
2
|
-
/*
|
|
3
|
-
the rest stays muted, producing a visible rotating arc. Inline-block so it
|
|
4
|
-
drops into `.btn`, `.field-label`, etc., without disturbing flow layout. */
|
|
2
|
+
/* The currentColor top edge over a muted ring reads as a rotating arc. */
|
|
5
3
|
.spinner {
|
|
6
4
|
display: inline-block;
|
|
7
5
|
width: 1rem;
|
|
@@ -25,7 +23,6 @@
|
|
|
25
23
|
border-width: 3px;
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
/* Honour reduced-motion — pause rather than spin frantically. */
|
|
29
26
|
@media (prefers-reduced-motion: reduce) {
|
|
30
27
|
.spinner {
|
|
31
28
|
animation-duration: 2s;
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
@layer components {
|
|
2
|
-
/*
|
|
3
|
-
|
|
4
|
-
markup so it inherits the surface, border, radius, and shadow, plus every
|
|
5
|
-
card modifier: `.card-bordered`, `.card-compact`, and the color variants
|
|
6
|
-
(`.card-primary`, `.card-muted`, …). `.stat-card` only adds the flat inner
|
|
7
|
-
padding/gap and the inverted hierarchy (the value dominates, the label is a
|
|
8
|
-
small annotation). */
|
|
2
|
+
/* Pairs with `.card` in the markup (no inner `.card-body`) — inherits its
|
|
3
|
+
surface and modifiers; this class only adds the flat padding/gap. */
|
|
9
4
|
.stat-card {
|
|
10
5
|
@apply gap-1 p-4;
|
|
11
6
|
}
|
|
@@ -14,7 +9,6 @@
|
|
|
14
9
|
@apply flex items-center gap-2 text-sm text-text-muted font-medium;
|
|
15
10
|
}
|
|
16
11
|
|
|
17
|
-
/* Keep the label's leading icon at its intrinsic size. */
|
|
18
12
|
.stat-card-label > :is(i, svg) {
|
|
19
13
|
flex-shrink: 0;
|
|
20
14
|
}
|
|
@@ -27,18 +21,13 @@
|
|
|
27
21
|
@apply text-sm text-text-muted;
|
|
28
22
|
}
|
|
29
23
|
|
|
30
|
-
/* `.card-compact`
|
|
31
|
-
the same padding step to the stat-card root, with a tighter gap to match. */
|
|
24
|
+
/* `.card-compact` targets `.card-body`; with no body, step the root instead. */
|
|
32
25
|
.card-compact.stat-card {
|
|
33
26
|
@apply p-3 gap-0.5;
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
/*
|
|
37
|
-
|
|
38
|
-
— the headline metric is a stat card's analog of `.card-title`, so a status
|
|
39
|
-
KPI reads its colour at a glance. `warning` is skipped for the same reason
|
|
40
|
-
`.card-warning` skips its title: yellow-400 fails AA on the muted yellow
|
|
41
|
-
surface. The tinted surface still carries the warning signal. */
|
|
29
|
+
/* Tint the value to the card variant's accent. `warning` is skipped like
|
|
30
|
+
`.card-warning`'s title: yellow-400 fails AA on the muted yellow surface. */
|
|
42
31
|
.card-primary .stat-card-value {
|
|
43
32
|
@apply text-primary;
|
|
44
33
|
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
@apply translate-x-4;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/* Native input variant
|
|
33
|
+
/* Native input variant. */
|
|
34
34
|
input.switch {
|
|
35
35
|
@apply appearance-none bg-border-strong m-0;
|
|
36
36
|
}
|
|
@@ -49,10 +49,12 @@
|
|
|
49
49
|
@apply translate-x-4;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
/*
|
|
53
|
-
Switches are wider than checkboxes/radios so the gap is a touch larger. */
|
|
52
|
+
/* Switches are wider than checkboxes/radios, so the label gap is larger. */
|
|
54
53
|
label:has(> .switch) {
|
|
55
54
|
@apply inline-flex items-center gap-3 cursor-pointer;
|
|
55
|
+
/* Let a long label break and shrink beside the shrink-0 track instead of overflowing. */
|
|
56
|
+
overflow-wrap: break-word;
|
|
57
|
+
min-width: 0;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
label:has(> .switch:disabled),
|
package/src/components/table.css
CHANGED
|
@@ -3,12 +3,8 @@
|
|
|
3
3
|
@apply w-full text-sm text-text border-collapse;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
/*
|
|
7
|
-
|
|
8
|
-
consumer renders a non-<td>/<th> element. :where() keeps specificity at 0
|
|
9
|
-
so authored utilities and modifiers can win without !important. Covers
|
|
10
|
-
thead/tbody/tfoot uniformly so a hand-written `<tfoot> <td>` lands on
|
|
11
|
-
the same padding as the React `.table-cell`. */
|
|
6
|
+
/* Descendant selectors cover hand-written markup; explicit classes cover
|
|
7
|
+
non-<td>/<th> renders. :where() keeps specificity 0 so authored utilities win. */
|
|
12
8
|
.table :where(th, td),
|
|
13
9
|
.table-cell,
|
|
14
10
|
.table-header-cell {
|
|
@@ -20,18 +16,21 @@
|
|
|
20
16
|
@apply font-medium text-text-muted bg-surface-muted border-b-border-strong whitespace-nowrap;
|
|
21
17
|
}
|
|
22
18
|
|
|
23
|
-
/*
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
/* Body cells only — break long unbreakable values (IDs, hashes, URLs) instead
|
|
20
|
+
of forcing the table into horizontal overflow. */
|
|
21
|
+
.table :where(td),
|
|
22
|
+
.table-cell {
|
|
23
|
+
overflow-wrap: break-word;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* No divider on the final row — `> :last-child` is the last section, tbody or
|
|
27
|
+
tfoot (browsers wrap a loose <tr> in an implicit tbody). */
|
|
27
28
|
.table > :last-child > tr:last-child :where(td),
|
|
28
29
|
.table > :last-child > tr:last-child .table-cell {
|
|
29
30
|
@apply border-b-0;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
/*
|
|
33
|
-
`<td data-align="right">` and for React's `<Table.Cell align="right">`,
|
|
34
|
-
so consumers never need a Tailwind utility in their markup. */
|
|
33
|
+
/* `data-align` serves both hand-written markup and React's `align` prop. */
|
|
35
34
|
.table :where(th, td)[data-align="right"] {
|
|
36
35
|
@apply text-right;
|
|
37
36
|
}
|
|
@@ -39,15 +38,12 @@
|
|
|
39
38
|
@apply text-center;
|
|
40
39
|
}
|
|
41
40
|
|
|
42
|
-
/* Right-aligned with tabular-nums so currency/totals columns don't shimmy. */
|
|
43
41
|
.table-cell-numeric {
|
|
44
42
|
@apply text-right tabular-nums;
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
/*
|
|
48
|
-
|
|
49
|
-
`<IconCircleCheck size={16}>` SVG so both deliveries land at the same
|
|
50
|
-
16px box and the same vertical baseline. */
|
|
45
|
+
/* Status-icon gutter; the child rule normalizes Tabler webfont <i> and React
|
|
46
|
+
SVG icons to the same 16px box and baseline. */
|
|
51
47
|
.table-cell-gutter {
|
|
52
48
|
@apply w-6 px-0 text-center text-text-muted;
|
|
53
49
|
}
|
|
@@ -73,10 +69,8 @@
|
|
|
73
69
|
@apply px-4 py-3;
|
|
74
70
|
}
|
|
75
71
|
|
|
76
|
-
/* Sticky header — requires a scrolling ancestor
|
|
77
|
-
|
|
78
|
-
added by the component; it would have no vanilla equivalent and would
|
|
79
|
-
break compositions like `<details><table>…`. */
|
|
72
|
+
/* Sticky header — requires a scrolling ancestor. The component adds no wrapper:
|
|
73
|
+
it'd have no vanilla equivalent and breaks compositions like <details><table>. */
|
|
80
74
|
.table-sticky thead :where(th) {
|
|
81
75
|
@apply sticky top-0 z-10 bg-surface-muted;
|
|
82
76
|
}
|
|
@@ -89,9 +83,7 @@
|
|
|
89
83
|
@apply bg-surface-muted;
|
|
90
84
|
}
|
|
91
85
|
|
|
92
|
-
/*
|
|
93
|
-
Base UI's checkbox via [data-checked], and the explicit
|
|
94
|
-
[data-selected] hook for programmatic selection without a checkbox. */
|
|
86
|
+
/* [data-selected] is the hook for programmatic selection without a checkbox. */
|
|
95
87
|
.table tbody tr:has(input[type="checkbox"]:checked),
|
|
96
88
|
.table tbody tr:has(.checkbox[data-checked]),
|
|
97
89
|
.table tbody tr[data-selected] {
|
|
@@ -103,11 +95,9 @@
|
|
|
103
95
|
@apply bg-primary-muted;
|
|
104
96
|
}
|
|
105
97
|
|
|
106
|
-
/*
|
|
107
|
-
|
|
108
|
-
(
|
|
109
|
-
should sit above the spanning pseudo-element with `relative` + non-auto
|
|
110
|
-
z-index; `.btn` already qualifies. */
|
|
98
|
+
/* The row's first <a> gets its hit-area expanded over the row via ::after;
|
|
99
|
+
other in-row anchors/buttons need `relative` + non-auto z-index to stay
|
|
100
|
+
clickable (`.btn` already qualifies). */
|
|
111
101
|
.table-row-link {
|
|
112
102
|
@apply relative cursor-pointer;
|
|
113
103
|
}
|