@aortl/admin-css 0.16.1 → 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.
Files changed (45) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/admin.css +0 -4
  3. package/dist/admin.min.css +1 -1
  4. package/dist/admin.scoped.css +0 -4
  5. package/dist/admin.scoped.min.css +0 -1
  6. package/package.json +3 -2
  7. package/src/components/accordion.css +4 -13
  8. package/src/components/alert.css +6 -21
  9. package/src/components/app-shell.css +2 -4
  10. package/src/components/badge.css +1 -5
  11. package/src/components/breadcrumbs.css +2 -11
  12. package/src/components/button-group.css +7 -19
  13. package/src/components/button.css +7 -23
  14. package/src/components/card.css +10 -26
  15. package/src/components/chart.css +26 -83
  16. package/src/components/checkbox.css +3 -9
  17. package/src/components/code-block.css +2 -4
  18. package/src/components/container.css +3 -7
  19. package/src/components/dialog.css +11 -18
  20. package/src/components/field.css +3 -7
  21. package/src/components/footer.css +2 -3
  22. package/src/components/indicator.css +9 -28
  23. package/src/components/input-group.css +3 -6
  24. package/src/components/input.css +2 -5
  25. package/src/components/kbd.css +7 -13
  26. package/src/components/link.css +2 -8
  27. package/src/components/menu.css +9 -24
  28. package/src/components/navbar.css +3 -7
  29. package/src/components/pagination.css +1 -4
  30. package/src/components/progress.css +3 -7
  31. package/src/components/property-list.css +5 -14
  32. package/src/components/prose.css +6 -24
  33. package/src/components/radio.css +3 -7
  34. package/src/components/select.css +4 -11
  35. package/src/components/sidebar.css +2 -7
  36. package/src/components/spinner.css +1 -4
  37. package/src/components/stat-card.css +5 -16
  38. package/src/components/switch.css +3 -5
  39. package/src/components/table.css +15 -34
  40. package/src/components/tabs.css +14 -32
  41. package/src/components/textarea.css +3 -7
  42. package/src/components/tooltip.css +6 -16
  43. package/src/fonts.css +8 -28
  44. package/src/theme.css +32 -100
  45. package/src/utilities.css +11 -37
@@ -24,9 +24,8 @@
24
24
  @apply inline-flex size-1.5 rounded-full bg-primary-content;
25
25
  }
26
26
 
27
- /* Native input variant: state is driven by :checked, the dot is ::after.
28
- Mirrors the Base UI span variant above so a vanilla <input class="radio">
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,12 +48,9 @@
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;
56
- /* Let a long label break and shrink beside the `shrink-0` control instead
57
- of overflowing a narrow form column. */
53
+ /* Long labels break beside the `shrink-0` control instead of overflowing. */
58
54
  overflow-wrap: break-word;
59
55
  min-width: 0;
60
56
  }
@@ -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
- /* Native <select> usage: same class, but suppress the native chevron and
39
- supply our own via background-image so the look matches the Base UI
40
- trigger. A bare native <select> is a replaced element and can't host a
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,8 +84,7 @@
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;
93
- /* Let a long option label (path, ID, email) break and shrink rather than
94
- grow the popup horizontally. The trailing indicator stays `ml-auto`. */
87
+ /* Break long option labels rather than grow the popup horizontally. */
95
88
  overflow-wrap: break-word;
96
89
  min-width: 0;
97
90
  }
@@ -7,8 +7,7 @@
7
7
  transition: width 150ms ease;
8
8
  }
9
9
 
10
- /* Hidden checkbox that drives the collapsed state via :has(). Lives inside
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
- /* Smooth panel expansion via ::details-content (modern browsers).
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
- /* Border-driven spinner — the top edge takes the host's `currentColor` while
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
- /* A KPI tile is a `.card` shell with no inner `.card-body` — label / value /
3
- detail stack directly on the card. Pair `.stat-card` with `.card` in the
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` tightens `.card-body`, but a stat card has no body — apply
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
- /* Color variants (`.card-primary`, `.card-success`, …) supply the tinted
37
- surface + border via `.card`. Here we tint the value to the matching accent
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: state is driven by :checked, thumb is ::before. */
33
+ /* Native input variant. */
34
34
  input.switch {
35
35
  @apply appearance-none bg-border-strong m-0;
36
36
  }
@@ -49,12 +49,10 @@
49
49
  @apply translate-x-4;
50
50
  }
51
51
 
52
- /* A <label> wrapping a switch + text lays out inline.
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;
56
- /* Let a long setting label break and shrink beside the `shrink-0` track
57
- instead of overflowing a narrow settings panel. */
55
+ /* Let a long label break and shrink beside the shrink-0 track instead of overflowing. */
58
56
  overflow-wrap: break-word;
59
57
  min-width: 0;
60
58
  }
@@ -3,12 +3,8 @@
3
3
  @apply w-full text-sm text-text border-collapse;
4
4
  }
5
5
 
6
- /* Default cell padding/alignment/divider applied via descendant selectors
7
- for hand-written markup AND via explicit classes for cases where the
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,27 +16,21 @@
20
16
  @apply font-medium text-text-muted bg-surface-muted border-b-border-strong whitespace-nowrap;
21
17
  }
22
18
 
23
- /* Body cells only — let a long unbreakable value (ID, hash, email, URL, file
24
- path) break inside the cell instead of widening the column and forcing the
25
- whole table into horizontal overflow. Header cells stay `whitespace-nowrap`
26
- above. */
19
+ /* Body cells only — break long unbreakable values (IDs, hashes, URLs) instead
20
+ of forcing the table into horizontal overflow. */
27
21
  .table :where(td),
28
22
  .table-cell {
29
23
  overflow-wrap: break-word;
30
24
  }
31
25
 
32
- /* Drop the divider on the very last row, whether it lives in tbody (no
33
- tfoot present) or in tfoot so the table doesn't double-border against
34
- a surrounding container. `.table > :last-child` resolves to the final
35
- section element (browsers wrap a loose `<tr>` in an implicit <tbody>). */
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). */
36
28
  .table > :last-child > tr:last-child :where(td),
37
29
  .table > :last-child > tr:last-child .table-cell {
38
30
  @apply border-b-0;
39
31
  }
40
32
 
41
- /* Alignment — `data-align` works equally for hand-written
42
- `<td data-align="right">` and for React's `<Table.Cell align="right">`,
43
- so consumers never need a Tailwind utility in their markup. */
33
+ /* `data-align` serves both hand-written markup and React's `align` prop. */
44
34
  .table :where(th, td)[data-align="right"] {
45
35
  @apply text-right;
46
36
  }
@@ -48,15 +38,12 @@
48
38
  @apply text-center;
49
39
  }
50
40
 
51
- /* Right-aligned with tabular-nums so currency/totals columns don't shimmy. */
52
41
  .table-cell-numeric {
53
42
  @apply text-right tabular-nums;
54
43
  }
55
44
 
56
- /* Narrow first-column gutter for row-level status icons. The inline icon
57
- rule normalizes Tabler webfont (`<i class="ti …">`) against React's
58
- `<IconCircleCheck size={16}>` SVG so both deliveries land at the same
59
- 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. */
60
47
  .table-cell-gutter {
61
48
  @apply w-6 px-0 text-center text-text-muted;
62
49
  }
@@ -82,10 +69,8 @@
82
69
  @apply px-4 py-3;
83
70
  }
84
71
 
85
- /* Sticky header — requires a scrolling ancestor (e.g.
86
- `<div class="overflow-auto" style="max-height: …">`). The wrapper isn't
87
- added by the component; it would have no vanilla equivalent and would
88
- 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>. */
89
74
  .table-sticky thead :where(th) {
90
75
  @apply sticky top-0 z-10 bg-surface-muted;
91
76
  }
@@ -98,9 +83,7 @@
98
83
  @apply bg-surface-muted;
99
84
  }
100
85
 
101
- /* Selection pure CSS via :has(). Fires for native checkboxes,
102
- Base UI's checkbox via [data-checked], and the explicit
103
- [data-selected] hook for programmatic selection without a checkbox. */
86
+ /* [data-selected] is the hook for programmatic selection without a checkbox. */
104
87
  .table tbody tr:has(input[type="checkbox"]:checked),
105
88
  .table tbody tr:has(.checkbox[data-checked]),
106
89
  .table tbody tr[data-selected] {
@@ -112,11 +95,9 @@
112
95
  @apply bg-primary-muted;
113
96
  }
114
97
 
115
- /* Whole-row link — the first <a> in the row gets its hit-area expanded to
116
- fill the row via ::after. Consumer still writes the actual anchor
117
- (preserves <Link>, plain <a href>, etc.). Other in-row anchors/buttons
118
- should sit above the spanning pseudo-element with `relative` + non-auto
119
- 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). */
120
101
  .table-row-link {
121
102
  @apply relative cursor-pointer;
122
103
  }
@@ -3,10 +3,8 @@
3
3
  @apply flex flex-col;
4
4
  }
5
5
 
6
- /* List + tab buttons. Same classes are produced by Base UI (React) and the
7
- vanilla radio-input markup, so styling is shared. All selectors are
8
- scoped under `.tabs` so generic `class="tab"` markup elsewhere on the
9
- page (e.g. Starlight's own <Tabs> component) doesn't pick them up. */
6
+ /* All selectors are scoped under `.tabs` so generic `class="tab"` markup
7
+ elsewhere (e.g. Starlight's own <Tabs>) doesn't pick them up. */
10
8
  .tabs .tab-list {
11
9
  @apply inline-flex items-center gap-1 border-b border-border;
12
10
  /* Containing block for the sliding ::before marker (see @supports below) */
@@ -30,14 +28,11 @@
30
28
  @apply text-text;
31
29
  }
32
30
 
33
- /* Keep a tab's leading icon at its intrinsic size when the label is long. */
34
31
  .tabs .tab > :is(i, svg) {
35
32
  flex-shrink: 0;
36
33
  }
37
34
 
38
- /* Vanilla radio-input pattern: hide the inputs visually, treat the following
39
- <label class="tab"> as the selected tab when the radio is :checked.
40
- Pairs with `:has(input:checked)` below to show the right panel. */
35
+ /* Vanilla radio pattern: hidden inputs; :checked styles the following label. */
41
36
  .tabs .tab-input {
42
37
  position: absolute;
43
38
  width: 1px;
@@ -57,9 +52,7 @@
57
52
  @apply outline-2 outline-offset-2 outline-focus;
58
53
  }
59
54
 
60
- /* Default underline marker on the active tab. The pseudo sits below the
61
- .tab, overlapping the .tab-list bottom border. Applies to any tabs root
62
- that isn't .tabs-boxed, so the bare `.tabs` default gets it for free. */
55
+ /* Default underline marker the pseudo overlaps the .tab-list bottom border. */
63
56
  .tabs:not(.tabs-boxed) .tab {
64
57
  position: relative;
65
58
  }
@@ -81,13 +74,12 @@
81
74
  transform: scaleX(1);
82
75
  }
83
76
 
84
- /* Boxed (segmented control) — adjacent tabs share borders. */
77
+ /* Boxed (segmented control). */
85
78
  .tabs-boxed .tab-list {
86
79
  @apply gap-0 p-0.5 border border-border rounded-md bg-surface-muted;
87
80
  }
88
81
 
89
- /* Sit above the sliding thumb (the anchored ::before below) so labels stay
90
- legible while it passes behind them. */
82
+ /* Sit above the sliding thumb (::before below) so labels stay legible. */
91
83
  .tabs-boxed .tab {
92
84
  @apply rounded relative z-[1];
93
85
  }
@@ -98,14 +90,11 @@
98
90
  @apply bg-surface text-text shadow-sm;
99
91
  }
100
92
 
101
- /* Sliding active marker — the default, with zero extra DOM. The selected tab
102
- becomes a CSS anchor; a single pseudo on the .tab-list tracks it via
103
- anchor() insets, and the browser interpolates those insets as the anchor
104
- moves between tabs so vanilla `:checked` markup and React both slide.
105
- `anchor-scope` confines the name to each list so multiple tab sets on a
106
- page don't cross-anchor; gating the whole block on its support keeps the
107
- effect off where it'd misbehave, leaving the per-tab marker above as the
108
- fallback. */
93
+ /* Sliding active marker, zero extra DOM: the selected tab becomes a CSS
94
+ anchor and a single .tab-list pseudo tracks it via anchor() insets, which
95
+ the browser interpolates between tabs vanilla `:checked` and React both
96
+ slide. `anchor-scope` confines the name per list so multiple tab sets
97
+ don't cross-anchor; the per-tab marker above remains the fallback. */
109
98
  @supports (anchor-scope: --x) {
110
99
  .tabs .tab-list {
111
100
  anchor-scope: --tab-thumb;
@@ -123,8 +112,7 @@
123
112
  position-anchor: --tab-thumb;
124
113
  }
125
114
 
126
- /* Bordered: a 2px bar sliding along the list's bottom edge replaces the
127
- per-tab ::after grow. */
115
+ /* Bordered: a bar sliding along the bottom edge replaces the per-tab ::after grow. */
128
116
  .tabs:not(.tabs-boxed) .tab::after {
129
117
  display: none;
130
118
  }
@@ -175,7 +163,6 @@
175
163
  }
176
164
  }
177
165
 
178
- /* Full width — list stretches across the container, tabs share space evenly. */
179
166
  .tabs-full-width:not([data-orientation="vertical"]) .tab-list {
180
167
  @apply flex w-full;
181
168
  }
@@ -184,7 +171,6 @@
184
171
  @apply flex-1 justify-center;
185
172
  }
186
173
 
187
- /* Sizes — cascade to nested tabs. */
188
174
  .tabs-sm .tab {
189
175
  @apply px-2 h-7 text-xs;
190
176
  }
@@ -198,8 +184,7 @@
198
184
  @apply pt-3 outline-none;
199
185
  }
200
186
 
201
- /* Vanilla radio-input pattern: only the panel whose data-value matches the
202
- checked input is shown. Each .tabs root scopes its own selectors. */
187
+ /* Vanilla pattern: show only the panel whose data-value matches the checked input. */
203
188
  .tabs .tab-panel {
204
189
  display: none;
205
190
  }
@@ -213,14 +198,11 @@
213
198
  display: block;
214
199
  }
215
200
 
216
- /* Base UI path: it renders the panel and applies `hidden`, so the rule
217
- above is a no-op there. Reinstate display when the panel is shown by
218
- Base UI. */
201
+ /* Base UI applies `hidden` itself reinstate display for its panels. */
219
202
  .tabs .tab-panel:not([hidden]):not([data-value]) {
220
203
  display: block;
221
204
  }
222
205
 
223
- /* Vertical orientation. */
224
206
  .tabs[data-orientation="vertical"] {
225
207
  @apply flex-row gap-3;
226
208
  }
@@ -18,8 +18,7 @@
18
18
  @apply border-danger hover:border-danger focus-visible:outline-danger;
19
19
  }
20
20
 
21
- /* Status variants border + focus outline only; text stays `text-text`, so
22
- warning's yellow accent (which fails AA as text) is fine here. */
21
+ /* Status variants color border + outline only warning's yellow fails AA as text. */
23
22
  .textarea-info {
24
23
  @apply border-info hover:border-info focus-visible:outline-info;
25
24
  }
@@ -32,16 +31,13 @@
32
31
  @apply border-warning hover:border-warning focus-visible:outline-warning;
33
32
  }
34
33
 
35
- /* Grow with content (Chromium 123+; degrades to a fixed resizable box
36
- elsewhere). The floor is the larger of the base `min-h-*` and the `rows`
37
- attribute; cap growth with a consumer `max-height`. Manual drag-resize is
38
- off — height is content-driven. */
34
+ /* Grow with content (Chromium 123+; fixed box elsewhere). Floor is the larger
35
+ of `min-h-*` and `rows`; cap with a consumer `max-height`. */
39
36
  .textarea-autosize {
40
37
  field-sizing: content;
41
38
  resize: none;
42
39
  }
43
40
 
44
- /* Sizes */
45
41
  .textarea-sm {
46
42
  @apply text-xs px-2.5 py-1.5 min-h-16;
47
43
  }
@@ -1,17 +1,9 @@
1
1
  @layer components {
2
- /*
3
- * Two paths, one class.
4
- *
5
- * React: Base UI portals the popup to <body> and anchors it with floating-ui;
6
- * the `.tooltip` class only carries paint + per-side enter/exit transitions
7
- * driven by Base UI's `data-side` / `data-starting-style` / `data-ending-style`
8
- * attributes.
9
- *
10
- * Vanilla: `.tooltip-wrap` is a `position: relative` parent that reveals a
11
- * nested `.tooltip` on `:hover` / `:focus-within`. No portal, no JS — works in
12
- * every modern browser today. The platform-native future is `interesttarget` +
13
- * `popover="hint"`, but cross-browser support is still rolling out.
14
- */
2
+ /* Two paths, one class. React: Base UI portals and positions the popup;
3
+ `.tooltip` carries paint + per-side transitions via Base UI's data attrs.
4
+ Vanilla: `.tooltip-wrap` reveals a nested `.tooltip` on :hover /
5
+ :focus-within no JS. Replace with `interesttarget` + `popover="hint"`
6
+ once cross-browser support lands. */
15
7
  .tooltip {
16
8
  @apply inline-block max-w-xs px-2 py-1
17
9
  rounded-md
@@ -22,13 +14,11 @@
22
14
  text-wrap: balance;
23
15
  }
24
16
 
25
- /* Sizes */
26
17
  .tooltip-sm {
27
18
  @apply px-1.5 py-0.5;
28
19
  }
29
20
 
30
- /* React (portalled) enter/exit — Base UI sets these data attrs on Popup.
31
- 150ms ease, matching Dialog. */
21
+ /* React enter/exit — Base UI sets these data attrs; 150ms matches Dialog. */
32
22
  .tooltip {
33
23
  transition:
34
24
  opacity 150ms ease-out,
package/src/fonts.css CHANGED
@@ -1,22 +1,10 @@
1
1
  /*
2
- * IBM Plex Sans + Mono the default UI typeface for this design system.
3
- *
4
- * Hosted on Google's CDN (fonts.gstatic.com), latin + latin-ext subsets only.
5
- * Plex Sans is served as a variable font (one file covers weights 400–600 via
6
- * the wght axis); Plex Mono ships as discrete weight files.
7
- *
8
- * `font-display: swap` paints text immediately. Layout shift on swap is
9
- * neutralised by the metric-matched fallback faces below ("IBM Plex Sans
10
- * Fallback" / "IBM Plex Mono Fallback"): they alias a local system font but
11
- * override its metrics to occupy Plex's exact box, so the swap changes glyph
12
- * shapes without moving anything. The fallback families are wired into
13
- * `--font-sans` / `--font-mono` in theme.css, ahead of the generic stack.
14
- *
15
- * Opt out by overriding `--font-sans` / `--font-mono`, or by importing
16
- * admin-css's source files individually and skipping this one.
2
+ * IBM Plex Sans + Mono from Google's CDN (latin + latin-ext only).
3
+ * `font-display: swap` paints text immediately; the metric-matched fallback
4
+ * faces below occupy Plex's exact box, so the swap changes glyph shapes
5
+ * without layout shift. Opt out by overriding `--font-sans` / `--font-mono`.
17
6
  */
18
7
 
19
- /* IBM Plex Sans — variable, weights 400–600 */
20
8
  @font-face {
21
9
  font-family: "IBM Plex Sans";
22
10
  font-style: normal;
@@ -41,7 +29,6 @@
41
29
  U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
42
30
  }
43
31
 
44
- /* IBM Plex Mono — discrete weights 400, 500 */
45
32
  @font-face {
46
33
  font-family: "IBM Plex Mono";
47
34
  font-style: normal;
@@ -89,16 +76,10 @@
89
76
  U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
90
77
  }
91
78
 
92
- /*
93
- * Metric-matched fallbacks a local system font rewritten to Plex's box so
94
- * the swap above causes no layout shift. The override numbers were computed
95
- * once from font metrics with @capsizecss/core (`createFontStack`); the project
96
- * doesn't depend on capsize, so if the primary font ever changes, recompute
97
- * them with capsize rather than hand-editing. size-adjust holds in every
98
- * engine; the ascent/descent overrides are ignored by Safari/WebKit, but
99
- * line-gap is 0 so the residual vertical shift there is negligible.
100
- */
101
- /* IBM Plex Sans -> Arial */
79
+ /* Metric-matched fallbacks — override numbers computed with @capsizecss/core
80
+ `createFontStack` (not a dependency); if the primary font changes, recompute
81
+ there rather than hand-editing. Safari/WebKit ignores the ascent/descent
82
+ overrides, but line-gap is 0 so the residual shift there is negligible. */
102
83
  @font-face {
103
84
  font-family: "IBM Plex Sans Fallback";
104
85
  src: local("Arial"), local("ArialMT");
@@ -107,7 +88,6 @@
107
88
  line-gap-override: 0%;
108
89
  size-adjust: 101.1663%;
109
90
  }
110
- /* IBM Plex Mono -> Courier New */
111
91
  @font-face {
112
92
  font-family: "IBM Plex Mono Fallback";
113
93
  src: local("Courier New"), local("CourierNewPSMT");