@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/admin.css +25 -4
  3. package/dist/admin.min.css +1 -1
  4. package/dist/admin.scoped.css +25 -4
  5. package/dist/admin.scoped.min.css +18 -15
  6. package/package.json +3 -2
  7. package/src/components/accordion.css +7 -10
  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 +3 -8
  12. package/src/components/button-group.css +7 -19
  13. package/src/components/button.css +7 -23
  14. package/src/components/card.css +13 -20
  15. package/src/components/chart.css +26 -83
  16. package/src/components/checkbox.css +5 -7
  17. package/src/components/code-block.css +2 -4
  18. package/src/components/container.css +3 -7
  19. package/src/components/dialog.css +13 -13
  20. package/src/components/field.css +4 -4
  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 +3 -5
  27. package/src/components/menu.css +11 -21
  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 +7 -21
  33. package/src/components/radio.css +5 -5
  34. package/src/components/select.css +6 -9
  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 +5 -3
  39. package/src/components/table.css +20 -30
  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
@@ -5,6 +5,9 @@
5
5
  border border-border
6
6
  rounded-xl
7
7
  shadow-xs;
8
+ /* min-width: 0 lets the card shrink inside a flex/grid track instead of forcing it wide. */
9
+ overflow-wrap: break-word;
10
+ min-width: 0;
8
11
  }
9
12
 
10
13
  .card-body {
@@ -13,25 +16,22 @@
13
16
 
14
17
  .card-title {
15
18
  @apply flex items-center gap-2 text-lg font-semibold leading-tight;
19
+ /* Let a long title wrap instead of widening the card. */
20
+ min-width: 0;
16
21
  }
17
22
 
18
- /* Header row: title on the left, toolbar pushed to the trailing edge. */
19
23
  .card-header {
20
24
  @apply flex items-center gap-2;
21
25
  }
22
26
 
23
- /* Trailing cluster of header controls (close, edit, …). The title's bold,
24
- descender-less type sits visually high in its line box, so geometric
25
- centering reads slightly low — nudge the controls up to the optical centre.
26
- `card-title` is always `text-lg`, so this offset is constant. */
27
+ /* -mt-0.5 nudges the controls up to the optical centre — the bold title type
28
+ sits high in its line box. */
27
29
  .card-toolbar {
28
30
  @apply flex items-center gap-1 ml-auto -mt-0.5;
29
31
  }
30
32
 
31
- /* Toolbar buttons run small (usually `btn-sm`, whose icons inherit `text-xs`);
32
- bump the glyph to 1rem so the controls stay easy to scan and hit. Both
33
- bundles size icons off `font-size` — the Tabler webfont (`<i>`) and React's
34
- `size="1em"` SVG — so this single rule enlarges both identically. */
33
+ /* font-size enlarges both icon flavours identically the Tabler webfont and
34
+ React's `size="1em"` SVG. */
35
35
  .card-toolbar :is(i, svg) {
36
36
  font-size: 1.25rem;
37
37
  }
@@ -44,7 +44,6 @@
44
44
  @apply flex flex-wrap items-center gap-2 mt-auto pt-2;
45
45
  }
46
46
 
47
- /* Modifiers */
48
47
  .card-compact .card-body {
49
48
  @apply p-3 gap-2;
50
49
  }
@@ -53,17 +52,12 @@
53
52
  @apply shadow-none border-border-strong;
54
53
  }
55
54
 
56
- /* Neutral variant sits flush with the page (`--color-surface`, the same
57
- fill as `html`/`.app-shell`) instead of the raised `surface-muted` of a
58
- default card. For de-emphasised or grouped panels that shouldn't pop off
59
- the background. No accent, no status meaning. */
55
+ /* Fills with the page background so the panel sits flush instead of raised. */
60
56
  .card-muted {
61
57
  @apply bg-surface;
62
58
  }
63
59
 
64
- /* Color variants — tinted surface + matching border, mirroring Alert. The
65
- title (and any leading icon, via `currentColor`) picks up the accent for
66
- fast scanning. */
60
+ /* Color variants — tinted surface + accent title, mirroring Alert. */
67
61
  .card-primary {
68
62
  @apply bg-primary-muted border-primary-muted;
69
63
  }
@@ -100,7 +94,6 @@
100
94
  @apply text-danger;
101
95
  }
102
96
 
103
- /* `card-warning` keeps its title at `text-text`: the warning accent is
104
- yellow-400 in both modes and fails AA contrast on the muted yellow
105
- surface. The tinted bg still carries the warning signal. */
97
+ /* No `.card-warning .card-title` rule the yellow-400 accent fails AA on
98
+ the muted yellow surface. */
106
99
  }
@@ -1,30 +1,14 @@
1
1
  @layer components {
2
2
  /*
3
- * Charts — three pure-CSS primitives driven entirely by inline custom
4
- * properties (never data-* attributes: CSS can't read an attribute as a
5
- * number for calc() cross-browser yet). No JS, no axes/ticks/gridlines.
6
- * They serve two altitudes: dense inline micro-viz (a swatch in a table
7
- * cell) and compact dashboard cards.
8
- *
9
- * Data contract:
10
- * - bars: container `--chart-max` (default 100), each bar `--value`;
11
- * the fill sizes to `var(--value) / var(--chart-max) * 100%`.
12
- * - stack: each segment `--value`, sized by `flex` (no max — the ratios
13
- * are intrinsic to the flex grow factors).
14
- * - donut: one pre-built cumulative conic-gradient stop string in
15
- * `--donut-segments` (CSS can't sum a variable-length sibling
16
- * list, so the string arrives whole).
17
- *
18
- * Colour: single-series follows `currentColor` (default `--color-info`,
19
- * recoloured by the `.chart-success`/etc. variants like `.progress`).
20
- * Multi-series colours are set inline per bar/segment (`--bar-color` /
21
- * `--segment-color` / legend `--legend-color`); there is no chart token
22
- * layer. role="img" + aria-label carries the semantics; a native `title`
23
- * on each bar/segment gives a hover read-out.
3
+ * Pure-CSS charts driven by inline custom properties — not data-*
4
+ * attributes (CSS can't read an attribute as a number for calc()).
5
+ * Contract: bars container `--chart-max` (default 100), each bar
6
+ * `--value`; stack each segment `--value` (flex-grow ratios, no max);
7
+ * donut `--donut-segments` holds a pre-built cumulative conic-gradient
8
+ * stop string (CSS can't sum a variable-length sibling list).
9
+ * Single-series colour rides currentColor; multi-series is set inline via
10
+ * `--bar-color` / `--segment-color` / `--legend-color`.
24
11
  */
25
-
26
- /* Shared base — sizing knobs + the single-series fill colour. Any chart
27
- type reads these; size modifiers below just remap them. */
28
12
  .chart {
29
13
  --chart-max: 100;
30
14
  --chart-height: 8rem; /* bar track cross-axis size (md) */
@@ -33,9 +17,7 @@
33
17
  color: var(--color-info);
34
18
  }
35
19
 
36
- /* Single-series semantic variants recolour currentColor, like progress.
37
- `info` is the base colour (`.chart` above), so it needs no class — the
38
- default `variant="info"` simply emits no modifier. */
20
+ /* `info` is the base colour (`.chart`), so the default variant emits no class. */
39
21
  .chart-success {
40
22
  color: var(--color-success);
41
23
  }
@@ -46,16 +28,9 @@
46
28
  color: var(--color-danger);
47
29
  }
48
30
 
49
- /* --------------------------------------------------------------------- *
50
- * BAR CHART horizontal (default) *
51
- * --------------------------------------------------------------------- *
52
- * Three-column grid: [label | track (1fr) | value]. Each `.chart-bar` is
53
- * a `subgrid` spanning all three, so the label gutter and the value column
54
- * stay aligned across every row. The fill lives in the `1fr` track — a
55
- * definite width once the grid resolves, which is exactly what lets its
56
- * `inline-size: %` transition (see the transition note). Values sit in the
57
- * trailing column, so a short bar never clips its number and a full bar
58
- * never overflows. */
31
+ /* Bar chart (horizontal) — grid [label | track (1fr) | value]; each bar is
32
+ a subgrid row so columns align across rows. The 1fr track gives the fill
33
+ a definite width, which is what lets its % inline-size transition. */
59
34
  .chart-bars {
60
35
  display: grid;
61
36
  grid-template-columns: auto 1fr auto;
@@ -71,8 +46,7 @@
71
46
  align-items: center;
72
47
  }
73
48
 
74
- /* Category label only emitted when the datum carries one. The empty
75
- `auto` column collapses to 0 when no bar has a label. */
49
+ /* The auto column collapses to 0 when no bar has a label. */
76
50
  .chart-bar-label {
77
51
  @apply text-xs text-text-muted;
78
52
  grid-column: 1;
@@ -91,15 +65,13 @@
91
65
  inline-size: calc(var(--value) / var(--chart-max) * 100%);
92
66
  background: var(--bar-color, currentColor);
93
67
  border-radius: 2px;
94
- /* Transition the resolved length (NOT --value, which isn't registered):
95
- a percentage length transitions because the track has a definite size.
96
- Both axes are listed so the vertical fill animates too. */
68
+ /* Transition the resolved length, not --value (unregistered — won't
69
+ animate); both axes so the vertical fill animates too. */
97
70
  transition:
98
71
  inline-size 200ms ease,
99
72
  block-size 200ms ease;
100
73
  }
101
74
 
102
- /* Value label — trailing aligned column. Opt-in via `.chart-values`. */
103
75
  .chart-bar-value {
104
76
  @apply text-xs text-text-muted tabular-nums;
105
77
  grid-column: 3;
@@ -111,13 +83,9 @@
111
83
  display: none;
112
84
  }
113
85
 
114
- /* --------------------------------------------------------------------- *
115
- * BAR CHART vertical modifier *
116
- * --------------------------------------------------------------------- *
117
- * Flex row of equal columns inside a fixed-height box. Each column is a
118
- * flex column [value | track | label]; the track (`flex: 1`) gets a
119
- * definite block-size so the fill's `block-size: %` resolves and animates.
120
- * Bars grow up from the floor (`justify-content: end`). */
86
+ /* Bar chart (vertical) — flex row of columns in a fixed-height box; the
87
+ flex:1 track gets a definite block-size so the fill's % block-size
88
+ resolves and animates. */
121
89
  .chart-bars-vertical {
122
90
  display: flex;
123
91
  flex-direction: row;
@@ -163,12 +131,7 @@
163
131
  padding: 0;
164
132
  }
165
133
 
166
- /* --------------------------------------------------------------------- *
167
- * STACKED / PROPORTION BAR *
168
- * --------------------------------------------------------------------- *
169
- * A single rounded track partitioned by `flex: var(--value)`. No max
170
- * needed — proportions are the flex-grow ratios. A hairline in the
171
- * surface colour separates adjacent segments. */
134
+ /* Stacked / proportion bar */
172
135
  .chart-stack {
173
136
  display: flex;
174
137
  flex-direction: row;
@@ -189,18 +152,10 @@
189
152
  box-shadow: -1px 0 0 0 var(--color-surface);
190
153
  }
191
154
 
192
- /* --------------------------------------------------------------------- *
193
- * DONUT / PIE *
194
- * --------------------------------------------------------------------- *
195
- * A conic-gradient circle with the centre punched out by a radial-gradient
196
- * mask. `--donut-thickness` is the ring width as a % of the diameter; the
197
- * hole's inner radius is `50% - thickness`, so thickness=50% leaves no hole
198
- * (a solid pie). The same stop position twice gives a crisp ring edge.
199
- *
200
- * The centre label must NOT be a child of the ring — `mask` clips the whole
201
- * subtree, so a child sitting in the hole would be invisible. It's a sibling
202
- * overlay: the figure is an inline-grid and both the ring and the label
203
- * occupy the same cell. */
155
+ /* Donut / pie — conic-gradient ring, centre punched out by a radial-gradient
156
+ mask. `--donut-thickness` is the ring width as a % of the diameter (50% =
157
+ solid pie). The centre label must be a sibling overlay, never a child —
158
+ `mask` clips the whole subtree. */
204
159
  .chart-donut-figure {
205
160
  position: relative;
206
161
  inline-size: var(--chart-size);
@@ -232,7 +187,6 @@
232
187
  inline-size: 100%;
233
188
  }
234
189
 
235
- /* Solid pie — no hole. */
236
190
  .chart-donut-pie {
237
191
  --donut-thickness: 50%;
238
192
  }
@@ -244,9 +198,7 @@
244
198
  pointer-events: none;
245
199
  }
246
200
 
247
- /* --------------------------------------------------------------------- *
248
- * LEGEND (donut + stack) *
249
- * --------------------------------------------------------------------- */
201
+ /* Legend (donut + stack) */
250
202
  .chart-legend {
251
203
  @apply flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-muted;
252
204
  list-style: none;
@@ -266,9 +218,7 @@
266
218
  flex-shrink: 0;
267
219
  }
268
220
 
269
- /* --------------------------------------------------------------------- *
270
- * SIZES — remap the cross-axis / diameter knobs *
271
- * --------------------------------------------------------------------- */
221
+ /* Sizes */
272
222
  .chart-sm {
273
223
  --chart-height: 4rem;
274
224
  --chart-size: 4rem;
@@ -278,11 +228,7 @@
278
228
  --chart-size: 12rem;
279
229
  }
280
230
 
281
- /* --------------------------------------------------------------------- *
282
- * INLINE — micro-viz that sits in running text / a table cell *
283
- * --------------------------------------------------------------------- *
284
- * Inline-flex, baseline-nudged, and em-relative so the swatch tracks the
285
- * surrounding font size. */
231
+ /* Inline micro-viz — em-relative so it tracks the surrounding font size. */
286
232
  .chart-inline {
287
233
  display: inline-flex;
288
234
  vertical-align: middle;
@@ -298,9 +244,6 @@
298
244
  inline-size: 6em;
299
245
  }
300
246
 
301
- /* --------------------------------------------------------------------- *
302
- * REDUCED MOTION *
303
- * --------------------------------------------------------------------- */
304
247
  @media (prefers-reduced-motion: reduce) {
305
248
  .chart-bar-fill,
306
249
  .chart-segment {
@@ -25,11 +25,8 @@
25
25
  @apply inline-flex items-center justify-center size-3 text-primary-content;
26
26
  }
27
27
 
28
- /* Native input variant: state is driven by :checked / :indeterminate, the
29
- glyph is masked onto ::after. Mirrors the Base UI button variant above so a
30
- vanilla <input class="checkbox"> renders identically (indeterminate reads
31
- the same as checked, matching the data-* rule). The mask is the same
32
- checkmark polyline the React indicator draws. */
28
+ /* Native input variant must render identically to the Base UI button rules
29
+ above (same checkmark polyline; indeterminate reads as checked). */
33
30
  input.checkbox {
34
31
  @apply appearance-none border-border-strong hover:border-text-muted;
35
32
  --checkbox-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
@@ -49,10 +46,11 @@
49
46
  mask: var(--checkbox-check) center / contain no-repeat;
50
47
  }
51
48
 
52
- /* A <label> wrapping a checkbox + text lays out inline with a small gap.
53
- Covers both the vanilla input.checkbox and Base UI's button.checkbox. */
54
49
  label:has(> .checkbox) {
55
50
  @apply inline-flex items-center gap-2 cursor-pointer;
51
+ /* Long labels wrap beside the shrink-0 box instead of overflowing. */
52
+ overflow-wrap: break-word;
53
+ min-width: 0;
56
54
  }
57
55
 
58
56
  label:has(> .checkbox:disabled),
@@ -9,10 +9,8 @@
9
9
  overflow: auto;
10
10
  }
11
11
 
12
- /* Opt out of wrapping for log-style content where long lines should
13
- scroll horizontally instead of breaking. Pair with an inline
14
- `max-height` to clamp vertical growth — `overflow: auto` above
15
- turns into a scrollbar automatically. */
12
+ /* Long lines scroll horizontally instead of breaking; pair with an inline
13
+ `max-height` to clamp vertical growth. */
16
14
  .code-block-nowrap {
17
15
  white-space: pre;
18
16
  word-wrap: normal;
@@ -1,8 +1,6 @@
1
1
  @layer components {
2
- /* Page content region a centered, max-width column that also owns the
3
- vertical rhythm between stacked page sections. Drop it inside the bare
4
- `.app-shell-main` (which has no padding of its own). Width presets just
5
- set `--container-max`; override the var per-instance to deviate. */
2
+ /* Goes inside the bare `.app-shell-main` (no padding of its own); override
3
+ `--container-max` per-instance to deviate from the presets. */
6
4
  .container {
7
5
  @apply flex flex-col gap-6 w-full mx-auto px-6 py-6;
8
6
  max-width: var(--container-max, 90rem);
@@ -16,17 +14,15 @@
16
14
  --container-max: 115rem;
17
15
  }
18
16
 
19
- /* Full-bleed: edge-to-edge within `main`, padding retained. */
20
17
  .container-fluid {
21
18
  --container-max: none;
22
19
  }
23
20
 
24
- /* Denser vertical rhythm + tighter block padding for packed screens. */
25
21
  .container-compact {
26
22
  @apply gap-4 py-4;
27
23
  }
28
24
 
29
- /* Less horizontal padding on small viewports — reuses the shell breakpoint. */
25
+ /* Reuses the shell breakpoint. */
30
26
  @media (max-width: 48rem) {
31
27
  .container {
32
28
  @apply px-4;
@@ -9,6 +9,9 @@
9
9
  width: calc(100% - 2rem);
10
10
  max-width: 32rem;
11
11
  max-height: calc(100dvh - 2rem);
12
+ /* Long tokens (filenames, IDs) break instead of pushing past max-width;
13
+ inherited by title + description. */
14
+ overflow-wrap: break-word;
12
15
  opacity: 1;
13
16
  transform: translateY(0) scale(1);
14
17
  transition:
@@ -18,9 +21,8 @@
18
21
  transform 150ms ease-out;
19
22
  }
20
23
 
21
- /* `display: flex` only when open — without this gate, our flex rule would
22
- override the UA `dialog:not([open]) { display: none }` and the dialog
23
- would render in-flow before showModal() was ever called. */
24
+ /* Gate on [open]an unconditional flex would override the UA's
25
+ `dialog:not([open]) { display: none }` and render the dialog in-flow. */
24
26
  .dialog[open] {
25
27
  display: flex;
26
28
  }
@@ -33,7 +35,6 @@
33
35
  background 150ms ease-out;
34
36
  }
35
37
 
36
- /* Entry — animate from these values up to the open state. */
37
38
  @starting-style {
38
39
  .dialog[open] {
39
40
  opacity: 0;
@@ -60,22 +61,22 @@
60
61
 
61
62
  .dialog-title {
62
63
  @apply flex-1 flex items-center gap-2 text-lg font-semibold leading-tight m-0;
64
+ /* Let a long title shrink and wrap rather than widen the header. */
65
+ min-width: 0;
63
66
  }
64
67
 
65
68
  .dialog-description {
66
69
  @apply px-5 -mt-2 mb-3 text-sm text-text-muted;
67
70
  }
68
71
 
69
- /* `flex-1 min-h-0` lets the body fill the remaining height and shrink below
70
- its content (a flex item's default `min-height: auto` would refuse to), so
71
- tall content scrolls here while the header/footer stay pinned. */
72
+ /* min-h-0 beats the flex default `min-height: auto` so tall content scrolls
73
+ here while the header/footer stay pinned. */
72
74
  .dialog-body {
73
75
  @apply flex-1 min-h-0 px-5 py-3 overflow-y-auto flex flex-col gap-3;
74
76
  }
75
77
 
76
- /* When the body is wrapped in a `<form>` (the form-dialog pattern), the form
77
- becomes the dialog's sole flex child make it the flex column so the body's
78
- `flex-1 min-h-0` still resolves and scrolls. */
78
+ /* In the form-dialog pattern the form is the sole flex child; it must carry
79
+ the flex column so the body's `flex-1 min-h-0` still scrolls. */
79
80
  .dialog > form {
80
81
  @apply flex flex-col min-h-0 flex-1;
81
82
  }
@@ -85,9 +86,8 @@
85
86
  px-5 py-3
86
87
  border-t border-border
87
88
  bg-surface-muted;
88
- /* Inherits the dialog's bottom-corner radius so the muted background
89
- doesn't square off past the parent's rounded corners previously
90
- handled by `overflow: hidden` on `.dialog`, which clipped popups. */
89
+ /* Keep the muted background inside the parent's rounded corners don't
90
+ use `overflow: hidden` on `.dialog` for this; it clips popups. */
91
91
  border-bottom-left-radius: inherit;
92
92
  border-bottom-right-radius: inherit;
93
93
  }
@@ -1,10 +1,11 @@
1
1
  @layer components {
2
2
  .field {
3
3
  @apply flex flex-col gap-1.5;
4
+ /* Long operator-/validation-supplied tokens break instead of overflowing;
5
+ inherited by label, description, and error. */
6
+ overflow-wrap: break-word;
4
7
  }
5
8
 
6
- /* Horizontal layout — pairs a control (typically a switch) with its label
7
- on one row. Use when the label belongs beside the control, not above it. */
8
9
  .field-row {
9
10
  @apply flex-row items-center gap-3;
10
11
  }
@@ -30,8 +31,7 @@
30
31
  @apply text-xs text-danger leading-relaxed;
31
32
  }
32
33
 
33
- /* When the field is in an invalid state, tint the contained control.
34
- Base UI applies data-invalid on the Field root once validation fails. */
34
+ /* Base UI sets data-invalid on the Field root when validation fails. */
35
35
  .field[data-invalid] .input,
36
36
  .field[data-invalid] .textarea,
37
37
  .field[data-invalid] .select,
@@ -1,7 +1,6 @@
1
1
  @layer components {
2
- /* 2px top stripe mirrors the navbar's bottom stripe both default to
3
- a neutral gray via `--color-system-accent` and brand-shift together
4
- when the consuming app overrides that variable at :root. */
2
+ /* The top stripe mirrors the navbar's bottom stripe; both brand-shift
3
+ together when the app overrides `--color-system-accent` at :root. */
5
4
  .footer {
6
5
  @apply flex flex-wrap items-center justify-between gap-3 px-4 py-3
7
6
  bg-surface-muted text-text-muted
@@ -1,23 +1,13 @@
1
1
  @layer components {
2
- /*
3
- * Wrapper for placing a small floating element (badge, count, status dot)
4
- * at a corner or edge of another element — notification badges on icon
5
- * buttons, status dots on avatars, "new" markers on tiles.
6
- *
7
- * Composition is driven by CSS custom properties so a horizontal modifier
8
- * (start/center/end) and a vertical modifier (top/middle/bottom) compose
9
- * naturally without needing a 3×3 grid of explicit selectors.
10
- */
2
+ /* Floats a small element (badge, dot) at a corner/edge of an anchor.
3
+ Placement is var-driven so a horizontal and a vertical modifier compose
4
+ without a 3×3 grid of selectors. */
11
5
  .indicator {
12
6
  @apply relative inline-flex w-max;
13
7
  }
14
8
 
15
- /*
16
- * Auto-offset for common rounded anchors so the indicator aligns with the
17
- * visual corner instead of the geometric box corner. Roughly half the
18
- * anchor's border-radius. Override per-instance by passing the `offset`
19
- * prop or setting `--indicator-offset` inline.
20
- */
9
+ /* Auto-offset (~half the anchor's border-radius) aligns with the visual
10
+ corner of rounded anchors; override `--indicator-offset` inline. */
21
11
  .indicator:has(> .btn),
22
12
  .indicator:has(> .input) {
23
13
  --indicator-offset: 2px;
@@ -32,14 +22,9 @@
32
22
  bottom: var(--indicator-bottom, auto);
33
23
  inset-inline-start: var(--indicator-start, auto);
34
24
  inset-inline-end: var(--indicator-end, 0);
35
- /*
36
- * Two composed translates: the first half-overlaps the corner; the second
37
- * pulls the item back toward the anchor center by `--indicator-offset`
38
- * (default 0). The pull direction is set per-modifier so it tracks the
39
- * placement automatically — e.g. `top-end` pulls left-and-down. Useful
40
- * for anchors with rounded corners: try `--indicator-offset: 4px` for a
41
- * `rounded-md` button.
42
- */
25
+ /* The first translate half-overlaps the corner; the second pulls back
26
+ toward the anchor centre by `--indicator-offset`, direction set
27
+ per-modifier. */
43
28
  transform: translate(var(--indicator-tx, 50%), var(--indicator-ty, -50%))
44
29
  translate(
45
30
  var(--indicator-offset-x, calc(-1 * var(--indicator-offset, 0px))),
@@ -87,11 +72,7 @@
87
72
  --indicator-offset-y: calc(-1 * var(--indicator-offset, 0px));
88
73
  }
89
74
 
90
- /*
91
- * Status dot — a small saturated circle for label-less presence/status.
92
- * A `.badge` with no content is still a pill (fixed height, horizontal
93
- * padding); a dot needs to actually be round.
94
- */
75
+ /* Status dot — an empty `.badge` is still a pill; a dot must be round. */
95
76
  .indicator-dot {
96
77
  @apply inline-block w-2 h-2 rounded-full bg-text-muted;
97
78
  }
@@ -3,9 +3,8 @@
3
3
  @apply inline-flex w-full;
4
4
  }
5
5
 
6
- /* Clear all corners, then re-apply the outer ones on the first/last
7
- child. Equal-specificity rules win because input-group.css imports
8
- after input.css. */
6
+ /* Equal-specificity override of `.input`'s radius works only because
7
+ input-group.css imports after input.css. */
9
8
  .input-group > * {
10
9
  @apply rounded-none;
11
10
  }
@@ -18,13 +17,11 @@
18
17
  @apply rounded-r-lg;
19
18
  }
20
19
 
21
- /* Collapse the shared edge between adjacent children. */
22
20
  .input-group > :not(:first-child) {
23
21
  margin-left: -1px;
24
22
  }
25
23
 
26
- /* Float the focused element so its outline ring and accented border
27
- can't be clipped by its neighbour's overlapping edge. */
24
+ /* Lift above the neighbour's overlapping edge so the focus ring isn't clipped. */
28
25
  .input-group > :focus,
29
26
  .input-group > :focus-within {
30
27
  @apply relative z-10;
@@ -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 tint border + outline only warning's yellow fails AA as text. */
23
22
  .input-info {
24
23
  @apply border-info hover:border-info focus-visible:outline-info;
25
24
  }
@@ -41,9 +40,7 @@
41
40
  @apply text-base px-4 py-2.5;
42
41
  }
43
42
 
44
- /* Native date/time picker glyph. The browser handles dark-mode coloring
45
- via the inherited `color-scheme` set on [data-theme]; we just tone the
46
- glyph down at rest and brighten it on hover so it reads as interactive. */
43
+ /* Dark-mode glyph coloring comes free via the inherited `color-scheme`. */
47
44
  .input::-webkit-calendar-picker-indicator {
48
45
  @apply opacity-60 cursor-pointer hover:opacity-100;
49
46
  }
@@ -1,6 +1,5 @@
1
1
  @layer components {
2
- /* A single keyboard chip one modifier or key. Stack inside a `.kbd-group`
3
- for multi-key chords. */
2
+ /* One key per chip; stack inside `.kbd-group` for chords. */
4
3
  .kbd {
5
4
  @apply inline-flex items-center justify-center
6
5
  min-w-[1.25em] h-[1.4em] px-[0.35em]
@@ -10,15 +9,12 @@
10
9
  whitespace-nowrap tabular-nums;
11
10
  }
12
11
 
13
- /* Cluster of `.kbd` chips. Always emitted by the React `<Kbd>` component so
14
- selectors can target the whole binding as a unit. */
12
+ /* Always emitted by the React `<Kbd>` so selectors can target the whole binding. */
15
13
  .kbd-group {
16
14
  @apply inline-flex items-center gap-1 align-middle;
17
15
  }
18
16
 
19
- /* Inside an action surface (button, menu item), the chip derives its
20
- colour from the host so it stays legible on any variant without
21
- fighting the surrounding chrome. */
17
+ /* Derive the chip colour from the host so it stays legible on any variant. */
22
18
  .btn .kbd,
23
19
  .menu-item .kbd {
24
20
  color: currentColor;
@@ -26,20 +22,18 @@
26
22
  border-color: color-mix(in srgb, currentColor 22%, transparent);
27
23
  }
28
24
 
29
- /* Subtle dimming for the leading cluster on a button so the label keeps
30
- visual priority. The cluster, not each chip, so a `Ctrl S` reads as
31
- a single unit. */
25
+ /* Dim the cluster (not each chip, so the chord reads as one unit) to keep
26
+ the label's visual priority. */
32
27
  .btn > .kbd-group {
33
28
  opacity: 0.85;
34
29
  }
35
30
 
36
- /* Right-pin the binding inside menu items — matches native menu convention. */
31
+ /* Matches native menu convention. */
37
32
  .menu-item > .kbd-group {
38
33
  margin-inline-start: auto;
39
34
  }
40
35
 
41
- /* Right-pin inside vertical button groups only. Standalone buttons keep the
42
- inline-trailing layout (chip sits next to the label). */
36
+ /* Vertical groups only standalone buttons keep the chip beside the label. */
43
37
  .btn-group-vertical > .btn > .kbd-group {
44
38
  margin-inline-start: auto;
45
39
  }
@@ -5,17 +5,15 @@
5
5
  rounded-sm transition-colors duration-150
6
6
  hover:text-link-hover
7
7
  focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus;
8
+ /* Links are often the URL itself — break instead of overflowing a narrow cell. */
9
+ overflow-wrap: break-word;
8
10
  }
9
11
 
10
- /* Keep a leading icon from squishing when the link text wraps. The trailing
11
- external arrow is already `flex: none` below. */
12
12
  .link > :is(i, svg) {
13
13
  flex-shrink: 0;
14
14
  }
15
15
 
16
- /* External affordance: a trailing rendered from CSS so it ships identically
17
- in both bundles — no JS, no icon-font dependency. The arrow is a masked box
18
- tinted with `currentColor`, so it tracks the link's color (incl. hover). */
16
+ /* Trailing drawn in CSS no JS or icon-font dependency in either bundle. */
19
17
  .link-external {
20
18
  --link-external-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M17 7 7 17'/%3E%3Cpath d='M8 7h9v9'/%3E%3C/svg%3E");
21
19
  }