@ponchia/ui 0.3.2 → 0.3.4
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 +10 -3
- package/behaviors/index.d.ts +8 -1
- package/behaviors/index.js +26 -2
- package/classes/index.d.ts +24 -4
- package/classes/index.js +16 -2
- package/classes/vscode.css-custom-data.json +13 -1
- package/css/app.css +22 -11
- package/css/dots.css +5 -1
- package/css/feedback.css +12 -0
- package/css/overlay.css +10 -0
- package/css/primitives.css +45 -0
- package/css/tokens.css +8 -1
- package/dist/bronto.css +1 -1
- package/dist/css/app.css +1 -1
- package/dist/css/dots.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/overlay.css +1 -1
- package/dist/css/primitives.css +1 -1
- package/dist/css/tokens.css +1 -1
- package/docs/contrast.md +100 -0
- package/docs/reference.md +950 -0
- package/docs/theming.md +209 -0
- package/docs/usage.md +113 -0
- package/llms.txt +91 -0
- package/package.json +19 -4
- package/tokens/index.d.ts +5 -5
- package/tokens/index.js +6 -1
- package/tokens/index.json +12 -2
- package/tokens/resolved.json +131 -0
- package/tokens/tokens.dtcg.json +36 -9
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@ponchia/ui)
|
|
4
4
|
[](https://www.npmjs.com/package/@ponchia/ui#provenance)
|
|
5
5
|
[](package.json)
|
|
6
|
-
[](scripts/check-dist.mjs)
|
|
7
7
|
[](https://github.com/Ponchia/bronto-ui/actions/workflows/ci.yml)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
|
|
@@ -12,7 +12,8 @@ monochrome surfaces, a single red accent, dot-matrix display type (Doto),
|
|
|
12
12
|
flat hairline borders, restrained motion. CSS-first and framework-agnostic.
|
|
13
13
|
|
|
14
14
|
**New here?** → [Getting started](#getting-started) ·
|
|
15
|
-
[Reference](docs/reference.md) · [
|
|
15
|
+
[Reference](docs/reference.md) · [Usage](docs/usage.md) ·
|
|
16
|
+
[Theming](docs/theming.md) · [Contrast](docs/contrast.md) ·
|
|
16
17
|
[Roadmap](ROADMAP.md) · [Contributing](CONTRIBUTING.md)
|
|
17
18
|
|
|
18
19
|
> **Editor IntelliSense.** The package ships a VS Code CSS Custom Data
|
|
@@ -25,6 +26,12 @@ flat hairline borders, restrained motion. CSS-first and framework-agnostic.
|
|
|
25
26
|
> }
|
|
26
27
|
> ```
|
|
27
28
|
|
|
29
|
+
> **For AI agents / LLMs.** The package ships `llms.txt` at its root —
|
|
30
|
+
> point a coding agent at `node_modules/@ponchia/ui/llms.txt` for a
|
|
31
|
+
> self-contained orientation. The full class catalog
|
|
32
|
+
> (`docs/reference.md`) and the token contract (`docs/theming.md`) also
|
|
33
|
+
> ship in the tarball, so an offline agent never has to guess at the API.
|
|
34
|
+
|
|
28
35
|
**[Live demo →](https://ponchia.github.io/bronto-ui/)** — the kitchen
|
|
29
36
|
sink (every component, light/dark, RTL, theming) deployed from `demo/`.
|
|
30
37
|
|
|
@@ -59,7 +66,7 @@ chain:
|
|
|
59
66
|
@import '@ponchia/ui'; /* → dist/bronto.css, the whole framework */
|
|
60
67
|
```
|
|
61
68
|
|
|
62
|
-
~
|
|
69
|
+
~64 kB raw / ~11 kB gzip, one request, same `@layer bronto`. (The
|
|
63
70
|
enforced ceiling lives in `scripts/check-dist.mjs`, not this prose —
|
|
64
71
|
treat these figures as indicative.) Source CSS, tokens/classes/behaviors
|
|
65
72
|
entrypoints are unchanged — use whichever fits.
|
package/behaviors/index.d.ts
CHANGED
|
@@ -108,11 +108,18 @@ export declare function initDialog(opts?: DelegateOpts): Cleanup;
|
|
|
108
108
|
|
|
109
109
|
export interface ToastOpts {
|
|
110
110
|
/** Status tone — maps to `ui-toast--<tone>`. */
|
|
111
|
-
tone?: 'accent' | 'success' | 'warning' | 'danger';
|
|
111
|
+
tone?: 'accent' | 'success' | 'warning' | 'danger' | 'info';
|
|
112
112
|
/** Optional uppercase label rendered above the message. */
|
|
113
113
|
title?: string;
|
|
114
114
|
/** Auto-dismiss delay in ms. 0 keeps it until dismissed. Default: 4000. */
|
|
115
115
|
duration?: number;
|
|
116
|
+
/**
|
|
117
|
+
* Route to the assertive live region so AT interrupts immediately.
|
|
118
|
+
* Defaults to `true` when `tone === 'danger'`.
|
|
119
|
+
*/
|
|
120
|
+
assertive?: boolean;
|
|
121
|
+
/** Render a dismiss button on the toast. */
|
|
122
|
+
closable?: boolean;
|
|
116
123
|
}
|
|
117
124
|
|
|
118
125
|
/**
|
package/behaviors/index.js
CHANGED
|
@@ -319,7 +319,7 @@ export function initDialog({ root } = {}) {
|
|
|
319
319
|
* that receives its first child in the same tick is not reliably
|
|
320
320
|
* announced by VoiceOver/NVDA). On first creation the empty region is
|
|
321
321
|
* inserted and the toast is appended on the next frame for the same
|
|
322
|
-
* reason. `tone` is accent/success/warning/danger; `title` is an
|
|
322
|
+
* reason. `tone` is accent/success/warning/danger/info; `title` is an
|
|
323
323
|
* optional uppercase label; `duration` ms before auto-dismiss (0 keeps
|
|
324
324
|
* it until dismissed). Returns a function that dismisses the toast
|
|
325
325
|
* early. SSR-safe (no-op).
|
|
@@ -607,11 +607,29 @@ export function initFormValidation({ root } = {}) {
|
|
|
607
607
|
};
|
|
608
608
|
|
|
609
609
|
return bindOnce(host, 'formValidation', () => {
|
|
610
|
+
// Suppress native bubbles UP FRONT for forms present at init. The
|
|
611
|
+
// in-handler `noValidate = true` only fires after the first
|
|
612
|
+
// submit/blur, so the very first invalid real-browser submit would
|
|
613
|
+
// otherwise show the native UA bubble instead of the Bronto
|
|
614
|
+
// summary — contradicting the documented contract. (Forms added
|
|
615
|
+
// after init are still covered by the in-handler set.)
|
|
616
|
+
// Feature-detect rather than `instanceof Element` — `Element` is not
|
|
617
|
+
// a guaranteed global (SSR / the no-DOM test env), and `host` is
|
|
618
|
+
// either `document` (no `.matches`) or a root Element.
|
|
619
|
+
const selfForm =
|
|
620
|
+
typeof host.matches === 'function' && host.matches('[data-bronto-validate]') ? [host] : [];
|
|
621
|
+
const forms = [...selfForm, ...(host.querySelectorAll?.('[data-bronto-validate]') ?? [])];
|
|
622
|
+
const priorNoValidate = new Map();
|
|
623
|
+
for (const f of forms) {
|
|
624
|
+
priorNoValidate.set(f, f.noValidate);
|
|
625
|
+
f.noValidate = true;
|
|
626
|
+
}
|
|
610
627
|
host.addEventListener('submit', onSubmit, true);
|
|
611
628
|
host.addEventListener('focusout', onBlur);
|
|
612
629
|
return () => {
|
|
613
630
|
host.removeEventListener('submit', onSubmit, true);
|
|
614
631
|
host.removeEventListener('focusout', onBlur);
|
|
632
|
+
for (const [f, v] of priorNoValidate) f.noValidate = v;
|
|
615
633
|
};
|
|
616
634
|
});
|
|
617
635
|
}
|
|
@@ -702,6 +720,12 @@ export function initCombobox({ root } = {}) {
|
|
|
702
720
|
if (match) any = true;
|
|
703
721
|
}
|
|
704
722
|
if (empty) empty.hidden = any;
|
|
723
|
+
// The active option may have just been filtered out — drop the
|
|
724
|
+
// stale activedescendant so Enter can't select a hidden option.
|
|
725
|
+
if (active >= 0 && options[active]?.hidden) {
|
|
726
|
+
active = -1;
|
|
727
|
+
setActive(null);
|
|
728
|
+
}
|
|
705
729
|
open();
|
|
706
730
|
};
|
|
707
731
|
|
|
@@ -762,7 +786,7 @@ export function initCombobox({ root } = {}) {
|
|
|
762
786
|
}
|
|
763
787
|
break;
|
|
764
788
|
case 'Enter':
|
|
765
|
-
if (!list.hidden && active >= 0) {
|
|
789
|
+
if (!list.hidden && active >= 0 && !options[active].hidden) {
|
|
766
790
|
e.preventDefault();
|
|
767
791
|
select(options[active]);
|
|
768
792
|
}
|
package/classes/index.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
/** @ponchia/ui — GENERATED from classes/index.js by scripts/gen-dts.mjs.
|
|
2
2
|
* Do not edit by hand; run `npm run dts:build`. Drift-checked in CI. */
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// Mirrors clsx's permissive input: `number`/`boolean` accepted so the
|
|
5
|
+
// idiomatic React `reactNode && 'cls'` guard (where the node may be 0 or
|
|
6
|
+
// '') type-checks. The runtime `cx` skips every falsy value regardless.
|
|
7
|
+
export type ClassValue =
|
|
8
|
+
| string
|
|
9
|
+
| number
|
|
10
|
+
| boolean
|
|
11
|
+
| null
|
|
12
|
+
| undefined
|
|
13
|
+
| ClassValue[];
|
|
5
14
|
|
|
6
15
|
/** The flat registry of every class @ponchia/ui defines (literal). */
|
|
7
16
|
export declare const cls: {
|
|
@@ -10,6 +19,8 @@ export declare const cls: {
|
|
|
10
19
|
readonly buttonSubtle: 'ui-button--subtle';
|
|
11
20
|
readonly buttonDanger: 'ui-button--danger';
|
|
12
21
|
readonly buttonIcon: 'ui-button--icon';
|
|
22
|
+
readonly buttonSm: 'ui-button--sm';
|
|
23
|
+
readonly buttonLg: 'ui-button--lg';
|
|
13
24
|
readonly card: 'ui-card';
|
|
14
25
|
readonly cardHead: 'ui-card__head';
|
|
15
26
|
readonly cardAccent: 'ui-card--accent';
|
|
@@ -28,6 +39,7 @@ export declare const cls: {
|
|
|
28
39
|
readonly badgeSuccess: 'ui-badge--success';
|
|
29
40
|
readonly badgeWarning: 'ui-badge--warning';
|
|
30
41
|
readonly badgeDanger: 'ui-badge--danger';
|
|
42
|
+
readonly badgeInfo: 'ui-badge--info';
|
|
31
43
|
readonly badgeMuted: 'ui-badge--muted';
|
|
32
44
|
readonly badgeDot: 'ui-badge--dot';
|
|
33
45
|
readonly chip: 'ui-chip';
|
|
@@ -36,11 +48,13 @@ export declare const cls: {
|
|
|
36
48
|
readonly linkArrow: 'ui-link--arrow';
|
|
37
49
|
readonly linkCta: 'ui-link--cta';
|
|
38
50
|
readonly keyValue: 'ui-key-value';
|
|
51
|
+
readonly emptyState: 'ui-empty-state';
|
|
39
52
|
readonly dot: 'ui-dot';
|
|
40
53
|
readonly dotAccent: 'ui-dot--accent';
|
|
41
54
|
readonly dotSuccess: 'ui-dot--success';
|
|
42
55
|
readonly dotWarning: 'ui-dot--warning';
|
|
43
56
|
readonly dotDanger: 'ui-dot--danger';
|
|
57
|
+
readonly dotInfo: 'ui-dot--info';
|
|
44
58
|
readonly dotLive: 'ui-dot--live';
|
|
45
59
|
readonly dotgrid: 'ui-dotgrid';
|
|
46
60
|
readonly dotgridAccent: 'ui-dotgrid--accent';
|
|
@@ -84,6 +98,7 @@ export declare const cls: {
|
|
|
84
98
|
readonly alertSuccess: 'ui-alert--success';
|
|
85
99
|
readonly alertWarning: 'ui-alert--warning';
|
|
86
100
|
readonly alertDanger: 'ui-alert--danger';
|
|
101
|
+
readonly alertInfo: 'ui-alert--info';
|
|
87
102
|
readonly toastStack: 'ui-toast-stack';
|
|
88
103
|
readonly toastStackAssertive: 'ui-toast-stack--assertive';
|
|
89
104
|
readonly toast: 'ui-toast';
|
|
@@ -93,6 +108,7 @@ export declare const cls: {
|
|
|
93
108
|
readonly toastSuccess: 'ui-toast--success';
|
|
94
109
|
readonly toastWarning: 'ui-toast--warning';
|
|
95
110
|
readonly toastDanger: 'ui-toast--danger';
|
|
111
|
+
readonly toastInfo: 'ui-toast--info';
|
|
96
112
|
readonly tooltip: 'ui-tooltip';
|
|
97
113
|
readonly tooltipBubble: 'ui-tooltip__bubble';
|
|
98
114
|
readonly popover: 'ui-popover';
|
|
@@ -205,6 +221,7 @@ export declare const cls: {
|
|
|
205
221
|
readonly appRailBrand: 'ui-app-rail__brand';
|
|
206
222
|
readonly appRailToggle: 'ui-app-rail__toggle';
|
|
207
223
|
readonly appRailFoot: 'ui-app-rail__foot';
|
|
224
|
+
readonly appRailAccount: 'ui-app-rail__account';
|
|
208
225
|
readonly appTopbar: 'ui-app-topbar';
|
|
209
226
|
readonly appTopbarTitle: 'ui-app-topbar__title';
|
|
210
227
|
readonly appToolbar: 'ui-app-toolbar';
|
|
@@ -235,13 +252,14 @@ export declare function cx(...parts: ClassValue[]): string;
|
|
|
235
252
|
export interface ButtonOpts {
|
|
236
253
|
variant?: 'ghost' | 'subtle' | 'danger';
|
|
237
254
|
icon?: boolean;
|
|
255
|
+
size?: 'sm' | 'lg';
|
|
238
256
|
}
|
|
239
257
|
export interface CardOpts {
|
|
240
258
|
accent?: boolean;
|
|
241
259
|
interactive?: boolean;
|
|
242
260
|
}
|
|
243
261
|
export interface BadgeOpts {
|
|
244
|
-
tone?: 'accent' | 'success' | 'warning' | 'danger' | 'muted';
|
|
262
|
+
tone?: 'accent' | 'success' | 'warning' | 'danger' | 'info' | 'muted';
|
|
245
263
|
dot?: boolean;
|
|
246
264
|
}
|
|
247
265
|
export interface NumOpts {
|
|
@@ -255,7 +273,7 @@ export interface LinkOpts {
|
|
|
255
273
|
cta?: boolean;
|
|
256
274
|
}
|
|
257
275
|
export interface DotOpts {
|
|
258
|
-
tone?: 'accent' | 'success' | 'warning' | 'danger';
|
|
276
|
+
tone?: 'accent' | 'success' | 'warning' | 'danger' | 'info';
|
|
259
277
|
live?: boolean;
|
|
260
278
|
}
|
|
261
279
|
export interface DotgridOpts {
|
|
@@ -279,7 +297,7 @@ export interface ClusterOpts {
|
|
|
279
297
|
export interface StaggerOpts {
|
|
280
298
|
auto?: boolean;
|
|
281
299
|
}
|
|
282
|
-
export type Tone = 'accent' | 'success' | 'warning' | 'danger';
|
|
300
|
+
export type Tone = 'accent' | 'success' | 'warning' | 'danger' | 'info';
|
|
283
301
|
export interface AlertOpts {
|
|
284
302
|
tone?: Tone;
|
|
285
303
|
}
|
|
@@ -297,6 +315,8 @@ export interface DotbarOpts {
|
|
|
297
315
|
}
|
|
298
316
|
export interface ModalOpts {
|
|
299
317
|
drawer?: boolean;
|
|
318
|
+
/** Controlled non-dialog usage — adds the is-open state (focus-trap is yours). */
|
|
319
|
+
open?: boolean;
|
|
300
320
|
}
|
|
301
321
|
export interface TabOpts {
|
|
302
322
|
active?: boolean;
|
package/classes/index.js
CHANGED
|
@@ -23,6 +23,8 @@ export const cls = Object.freeze({
|
|
|
23
23
|
buttonSubtle: 'ui-button--subtle',
|
|
24
24
|
buttonDanger: 'ui-button--danger',
|
|
25
25
|
buttonIcon: 'ui-button--icon',
|
|
26
|
+
buttonSm: 'ui-button--sm',
|
|
27
|
+
buttonLg: 'ui-button--lg',
|
|
26
28
|
card: 'ui-card',
|
|
27
29
|
cardHead: 'ui-card__head',
|
|
28
30
|
cardAccent: 'ui-card--accent',
|
|
@@ -41,6 +43,7 @@ export const cls = Object.freeze({
|
|
|
41
43
|
badgeSuccess: 'ui-badge--success',
|
|
42
44
|
badgeWarning: 'ui-badge--warning',
|
|
43
45
|
badgeDanger: 'ui-badge--danger',
|
|
46
|
+
badgeInfo: 'ui-badge--info',
|
|
44
47
|
badgeMuted: 'ui-badge--muted',
|
|
45
48
|
badgeDot: 'ui-badge--dot',
|
|
46
49
|
chip: 'ui-chip',
|
|
@@ -49,12 +52,14 @@ export const cls = Object.freeze({
|
|
|
49
52
|
linkArrow: 'ui-link--arrow',
|
|
50
53
|
linkCta: 'ui-link--cta',
|
|
51
54
|
keyValue: 'ui-key-value',
|
|
55
|
+
emptyState: 'ui-empty-state',
|
|
52
56
|
// dots
|
|
53
57
|
dot: 'ui-dot',
|
|
54
58
|
dotAccent: 'ui-dot--accent',
|
|
55
59
|
dotSuccess: 'ui-dot--success',
|
|
56
60
|
dotWarning: 'ui-dot--warning',
|
|
57
61
|
dotDanger: 'ui-dot--danger',
|
|
62
|
+
dotInfo: 'ui-dot--info',
|
|
58
63
|
dotLive: 'ui-dot--live',
|
|
59
64
|
dotgrid: 'ui-dotgrid',
|
|
60
65
|
dotgridAccent: 'ui-dotgrid--accent',
|
|
@@ -100,6 +105,7 @@ export const cls = Object.freeze({
|
|
|
100
105
|
alertSuccess: 'ui-alert--success',
|
|
101
106
|
alertWarning: 'ui-alert--warning',
|
|
102
107
|
alertDanger: 'ui-alert--danger',
|
|
108
|
+
alertInfo: 'ui-alert--info',
|
|
103
109
|
toastStack: 'ui-toast-stack',
|
|
104
110
|
toastStackAssertive: 'ui-toast-stack--assertive',
|
|
105
111
|
toast: 'ui-toast',
|
|
@@ -109,6 +115,7 @@ export const cls = Object.freeze({
|
|
|
109
115
|
toastSuccess: 'ui-toast--success',
|
|
110
116
|
toastWarning: 'ui-toast--warning',
|
|
111
117
|
toastDanger: 'ui-toast--danger',
|
|
118
|
+
toastInfo: 'ui-toast--info',
|
|
112
119
|
tooltip: 'ui-tooltip',
|
|
113
120
|
tooltipBubble: 'ui-tooltip__bubble',
|
|
114
121
|
popover: 'ui-popover',
|
|
@@ -229,6 +236,7 @@ export const cls = Object.freeze({
|
|
|
229
236
|
appRailBrand: 'ui-app-rail__brand',
|
|
230
237
|
appRailToggle: 'ui-app-rail__toggle',
|
|
231
238
|
appRailFoot: 'ui-app-rail__foot',
|
|
239
|
+
appRailAccount: 'ui-app-rail__account',
|
|
232
240
|
appTopbar: 'ui-app-topbar',
|
|
233
241
|
appTopbarTitle: 'ui-app-topbar__title',
|
|
234
242
|
appToolbar: 'ui-app-toolbar',
|
|
@@ -264,13 +272,15 @@ export function cx(...parts) {
|
|
|
264
272
|
const j = (...p) => p.filter(Boolean).join(' ');
|
|
265
273
|
|
|
266
274
|
export const ui = {
|
|
267
|
-
button: ({ variant, icon } = {}) =>
|
|
275
|
+
button: ({ variant, icon, size } = {}) =>
|
|
268
276
|
j(
|
|
269
277
|
cls.button,
|
|
270
278
|
variant === 'ghost' && cls.buttonGhost,
|
|
271
279
|
variant === 'subtle' && cls.buttonSubtle,
|
|
272
280
|
variant === 'danger' && cls.buttonDanger,
|
|
273
281
|
icon && cls.buttonIcon,
|
|
282
|
+
size === 'sm' && cls.buttonSm,
|
|
283
|
+
size === 'lg' && cls.buttonLg,
|
|
274
284
|
),
|
|
275
285
|
card: ({ accent, interactive } = {}) =>
|
|
276
286
|
j(cls.card, accent && cls.cardAccent, interactive && cls.cardInteractive),
|
|
@@ -281,6 +291,7 @@ export const ui = {
|
|
|
281
291
|
tone === 'success' && cls.badgeSuccess,
|
|
282
292
|
tone === 'warning' && cls.badgeWarning,
|
|
283
293
|
tone === 'danger' && cls.badgeDanger,
|
|
294
|
+
tone === 'info' && cls.badgeInfo,
|
|
284
295
|
tone === 'muted' && cls.badgeMuted,
|
|
285
296
|
dot && cls.badgeDot,
|
|
286
297
|
),
|
|
@@ -300,6 +311,7 @@ export const ui = {
|
|
|
300
311
|
tone === 'success' && cls.dotSuccess,
|
|
301
312
|
tone === 'warning' && cls.dotWarning,
|
|
302
313
|
tone === 'danger' && cls.dotDanger,
|
|
314
|
+
tone === 'info' && cls.dotInfo,
|
|
303
315
|
live && cls.dotLive,
|
|
304
316
|
),
|
|
305
317
|
dotgrid: ({ accent, dense } = {}) =>
|
|
@@ -322,6 +334,7 @@ export const ui = {
|
|
|
322
334
|
tone === 'success' && cls.alertSuccess,
|
|
323
335
|
tone === 'warning' && cls.alertWarning,
|
|
324
336
|
tone === 'danger' && cls.alertDanger,
|
|
337
|
+
tone === 'info' && cls.alertInfo,
|
|
325
338
|
),
|
|
326
339
|
toast: ({ tone } = {}) =>
|
|
327
340
|
j(
|
|
@@ -330,12 +343,13 @@ export const ui = {
|
|
|
330
343
|
tone === 'success' && cls.toastSuccess,
|
|
331
344
|
tone === 'warning' && cls.toastWarning,
|
|
332
345
|
tone === 'danger' && cls.toastDanger,
|
|
346
|
+
tone === 'info' && cls.toastInfo,
|
|
333
347
|
),
|
|
334
348
|
progress: ({ indeterminate } = {}) => j(cls.progress, indeterminate && cls.progressIndeterminate),
|
|
335
349
|
dotspinner: ({ size } = {}) =>
|
|
336
350
|
j(cls.dotspinner, size === 'sm' && cls.dotspinnerSm, size === 'lg' && cls.dotspinnerLg),
|
|
337
351
|
dotbar: ({ indeterminate } = {}) => j(cls.dotbar, indeterminate && cls.dotbarIndeterminate),
|
|
338
|
-
modal: ({ drawer } = {}) => j(cls.modal, drawer && cls.modalDrawer),
|
|
352
|
+
modal: ({ drawer, open } = {}) => j(cls.modal, drawer && cls.modalDrawer, open && 'is-open'),
|
|
339
353
|
tab: ({ active } = {}) => j(cls.tab, active && 'is-active'),
|
|
340
354
|
avatar: ({ size } = {}) =>
|
|
341
355
|
j(cls.avatar, size === 'sm' && cls.avatarSm, size === 'lg' && cls.avatarLg),
|
|
@@ -85,6 +85,10 @@
|
|
|
85
85
|
"name": "--bronto-color-focus",
|
|
86
86
|
"description": "Global scale token. Value: `var(--focus-ring)`"
|
|
87
87
|
},
|
|
88
|
+
{
|
|
89
|
+
"name": "--bronto-color-info",
|
|
90
|
+
"description": "Global scale token. Value: `var(--info)`"
|
|
91
|
+
},
|
|
88
92
|
{
|
|
89
93
|
"name": "--bronto-color-on-action",
|
|
90
94
|
"description": "Global scale token. Value: `var(--button-text)`"
|
|
@@ -131,7 +135,7 @@
|
|
|
131
135
|
},
|
|
132
136
|
{
|
|
133
137
|
"name": "--display",
|
|
134
|
-
"description": "Global scale token. Value: `
|
|
138
|
+
"description": "Global scale token. Value: `var(--dot-font)`"
|
|
135
139
|
},
|
|
136
140
|
{
|
|
137
141
|
"name": "--dot-font",
|
|
@@ -185,6 +189,14 @@
|
|
|
185
189
|
"name": "--focus-ring",
|
|
186
190
|
"description": "Theme token. Light: `var(--accent)` · Dark: `var(--accent)`"
|
|
187
191
|
},
|
|
192
|
+
{
|
|
193
|
+
"name": "--info",
|
|
194
|
+
"description": "Theme token. Light: `#1f63c4` · Dark: `#6fb0e6`"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "--info-soft",
|
|
198
|
+
"description": "Theme token. Light: `rgb(31, 99, 196, 0.12)` · Dark: `rgb(111, 176, 230, 0.14)`"
|
|
199
|
+
},
|
|
188
200
|
{
|
|
189
201
|
"name": "--line",
|
|
190
202
|
"description": "Theme token. Light: `#d8d8d4` · Dark: `#2a2a2a`"
|
package/css/app.css
CHANGED
|
@@ -111,6 +111,22 @@
|
|
|
111
111
|
padding-block-start: var(--space-sm);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/* Account / identity slot — signed-in user + sign-out. A framework-
|
|
115
|
+
blessed home for what every shell otherwise hand-rolls. Pinned to the
|
|
116
|
+
rail end like __foot, but it stays visible when the rail collapses to
|
|
117
|
+
a horizontal bar on mobile (sign-out must not vanish). */
|
|
118
|
+
.ui-app-rail__account {
|
|
119
|
+
align-items: center;
|
|
120
|
+
display: flex;
|
|
121
|
+
gap: var(--space-xs);
|
|
122
|
+
margin-block-start: auto;
|
|
123
|
+
padding-block-start: var(--space-sm);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.ui-app-rail__account + .ui-app-rail__foot {
|
|
127
|
+
margin-block-start: 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
114
130
|
/* --- Main column --- */
|
|
115
131
|
|
|
116
132
|
.ui-app-main {
|
|
@@ -196,8 +212,7 @@
|
|
|
196
212
|
text-transform: uppercase;
|
|
197
213
|
}
|
|
198
214
|
|
|
199
|
-
.ui-app-panel__head p
|
|
200
|
-
.ui-app-empty-state p {
|
|
215
|
+
.ui-app-panel__head p {
|
|
201
216
|
color: var(--text-dim);
|
|
202
217
|
margin: 0.3rem 0 0;
|
|
203
218
|
}
|
|
@@ -209,15 +224,11 @@
|
|
|
209
224
|
the canonical rules there — identical output). Nothing app-specific
|
|
210
225
|
left to define here. */
|
|
211
226
|
|
|
212
|
-
/* --- Empty state ---
|
|
213
|
-
|
|
214
|
-
.ui-app-empty-state
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
color: var(--text-dim);
|
|
218
|
-
padding: var(--space-lg);
|
|
219
|
-
text-align: center;
|
|
220
|
-
}
|
|
227
|
+
/* --- Empty state ---
|
|
228
|
+
The empty-state primitive is shell-agnostic and now lives in
|
|
229
|
+
primitives.css as `.ui-empty-state`; `.ui-app-empty-state` remains a
|
|
230
|
+
permanent admin-shell alias (grouped on the canonical rules there —
|
|
231
|
+
identical output). Nothing app-specific left to define here. */
|
|
221
232
|
|
|
222
233
|
/* --- Mobile rail collapse --- */
|
|
223
234
|
|
package/css/dots.css
CHANGED
|
@@ -94,6 +94,10 @@
|
|
|
94
94
|
background: var(--danger);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
.ui-dot--info {
|
|
98
|
+
background: var(--info);
|
|
99
|
+
}
|
|
100
|
+
|
|
97
101
|
.ui-dot--live {
|
|
98
102
|
background: var(--success);
|
|
99
103
|
box-shadow: 0 0 0 0 color-mix(in srgb, var(--success) 70%, transparent);
|
|
@@ -229,7 +233,7 @@
|
|
|
229
233
|
|
|
230
234
|
.ui-dotbar i {
|
|
231
235
|
background: var(--field-dot);
|
|
232
|
-
border-radius:
|
|
236
|
+
border-radius: var(--radius-sm);
|
|
233
237
|
flex: 1;
|
|
234
238
|
block-size: 0.5rem;
|
|
235
239
|
}
|
package/css/feedback.css
CHANGED
|
@@ -95,6 +95,14 @@
|
|
|
95
95
|
background: var(--danger);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
.ui-alert--info {
|
|
99
|
+
border-inline-start-color: var(--info);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.ui-alert--info::before {
|
|
103
|
+
background: var(--info);
|
|
104
|
+
}
|
|
105
|
+
|
|
98
106
|
@media (hover: hover) {
|
|
99
107
|
.ui-alert__dismiss:hover {
|
|
100
108
|
color: var(--text);
|
|
@@ -201,6 +209,10 @@
|
|
|
201
209
|
background: var(--danger);
|
|
202
210
|
}
|
|
203
211
|
|
|
212
|
+
.ui-toast--info::before {
|
|
213
|
+
background: var(--info);
|
|
214
|
+
}
|
|
215
|
+
|
|
204
216
|
/* --- Tooltip — CSS-only, hover/focus, no JS --- */
|
|
205
217
|
|
|
206
218
|
.ui-tooltip {
|
package/css/overlay.css
CHANGED
|
@@ -31,6 +31,16 @@
|
|
|
31
31
|
grid-template-rows: auto 1fr auto;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/* Controlled (non-<dialog>) usage. A portal/React modal that can't be a
|
|
35
|
+
native <dialog> wears the same skin + open layout via `.is-open`.
|
|
36
|
+
Backdrop, top-layer stacking and focus-trapping are then the
|
|
37
|
+
consumer's responsibility (the native <dialog> path gets them free). */
|
|
38
|
+
.ui-modal.is-open {
|
|
39
|
+
animation: uiToastIn var(--duration-base) var(--ease-spring) both;
|
|
40
|
+
display: grid;
|
|
41
|
+
grid-template-rows: auto 1fr auto;
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
.ui-modal__head {
|
|
35
45
|
align-items: flex-start;
|
|
36
46
|
border-block-end: 1px solid var(--line);
|
package/css/primitives.css
CHANGED
|
@@ -314,6 +314,24 @@
|
|
|
314
314
|
padding: 0;
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
/* Size scale — dense tooling (toolbars, pagination, table actions) wants
|
|
318
|
+
--sm; --lg for hero CTAs. Token-driven; the default is unchanged. */
|
|
319
|
+
.ui-button--sm {
|
|
320
|
+
font-size: var(--text-xs);
|
|
321
|
+
gap: 0.3rem;
|
|
322
|
+
min-block-size: 1.9rem;
|
|
323
|
+
min-inline-size: 1.9rem;
|
|
324
|
+
padding: 0.4rem 0.66rem;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.ui-button--lg {
|
|
328
|
+
font-size: var(--text-base);
|
|
329
|
+
gap: 0.55rem;
|
|
330
|
+
min-block-size: 2.9rem;
|
|
331
|
+
min-inline-size: 2.9rem;
|
|
332
|
+
padding: 0.85rem 1.4rem;
|
|
333
|
+
}
|
|
334
|
+
|
|
317
335
|
.ui-button:disabled,
|
|
318
336
|
.ui-button[aria-disabled='true'] {
|
|
319
337
|
cursor: not-allowed;
|
|
@@ -459,6 +477,11 @@
|
|
|
459
477
|
border-color: color-mix(in srgb, var(--danger) 40%, var(--line));
|
|
460
478
|
}
|
|
461
479
|
|
|
480
|
+
.ui-badge--info {
|
|
481
|
+
background: var(--info-soft);
|
|
482
|
+
border-color: color-mix(in srgb, var(--info) 40%, var(--line));
|
|
483
|
+
}
|
|
484
|
+
|
|
462
485
|
/* Idle / unknown — the honest "no signal yet" tone (distinct from the
|
|
463
486
|
default tinted badge). Token-safe; no new hue. */
|
|
464
487
|
.ui-badge--muted {
|
|
@@ -495,6 +518,10 @@
|
|
|
495
518
|
background: var(--danger);
|
|
496
519
|
}
|
|
497
520
|
|
|
521
|
+
.ui-badge--info.ui-badge--dot::before {
|
|
522
|
+
background: var(--info);
|
|
523
|
+
}
|
|
524
|
+
|
|
498
525
|
/* --- Key/value list --- */
|
|
499
526
|
|
|
500
527
|
.ui-key-value {
|
|
@@ -586,3 +613,21 @@
|
|
|
586
613
|
min-inline-size: 2.9rem;
|
|
587
614
|
}
|
|
588
615
|
}
|
|
616
|
+
|
|
617
|
+
/* --- Empty state — shell-agnostic placeholder for "no data yet".
|
|
618
|
+
Freed from the admin shell; `.ui-app-empty-state` stays a permanent
|
|
619
|
+
grouped alias (identical output, no baseline drift). --- */
|
|
620
|
+
.ui-empty-state,
|
|
621
|
+
.ui-app-empty-state {
|
|
622
|
+
border: 1px dashed var(--line-strong);
|
|
623
|
+
border-radius: var(--radius-md);
|
|
624
|
+
color: var(--text-dim);
|
|
625
|
+
padding: var(--space-lg);
|
|
626
|
+
text-align: center;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.ui-empty-state p,
|
|
630
|
+
.ui-app-empty-state p {
|
|
631
|
+
color: var(--text-dim);
|
|
632
|
+
margin: 0.3rem 0 0;
|
|
633
|
+
}
|
package/css/tokens.css
CHANGED
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
--mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace;
|
|
30
30
|
--sans:
|
|
31
31
|
'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
|
32
|
-
--display: 'Doto', var(--mono);
|
|
33
32
|
--dot-font: 'Doto', var(--mono);
|
|
33
|
+
--display: var(--dot-font);
|
|
34
34
|
--text-2xs: 0.68rem;
|
|
35
35
|
--text-xs: 0.76rem;
|
|
36
36
|
--text-sm: 0.86rem;
|
|
@@ -99,6 +99,7 @@
|
|
|
99
99
|
--bronto-color-success: var(--success);
|
|
100
100
|
--bronto-color-warning: var(--warning);
|
|
101
101
|
--bronto-color-danger: var(--danger);
|
|
102
|
+
--bronto-color-info: var(--info);
|
|
102
103
|
|
|
103
104
|
/* Aliases kept for back-compat with existing semantic classes */
|
|
104
105
|
--surface: var(--panel);
|
|
@@ -138,6 +139,8 @@
|
|
|
138
139
|
--orange-soft: rgb(168, 95, 50, 0.13);
|
|
139
140
|
--danger: #c01622;
|
|
140
141
|
--danger-soft: rgb(192, 22, 34, 0.1);
|
|
142
|
+
--info: #1f63c4;
|
|
143
|
+
--info-soft: rgb(31, 99, 196, 0.12);
|
|
141
144
|
--code-bg: rgb(10, 10, 10, 0.05);
|
|
142
145
|
--button-text: #ffffff;
|
|
143
146
|
--field-dot: rgb(10, 10, 10, 0.16);
|
|
@@ -189,6 +192,8 @@
|
|
|
189
192
|
--orange-soft: rgb(208, 140, 91, 0.15);
|
|
190
193
|
--danger: #ff4d54;
|
|
191
194
|
--danger-soft: rgb(255, 77, 84, 0.15);
|
|
195
|
+
--info: #6fb0e6;
|
|
196
|
+
--info-soft: rgb(111, 176, 230, 0.14);
|
|
192
197
|
--code-bg: rgb(255, 255, 255, 0.05);
|
|
193
198
|
--button-text: #000000;
|
|
194
199
|
--field-dot: rgb(242, 242, 242, 0.14);
|
|
@@ -226,6 +231,8 @@
|
|
|
226
231
|
--orange-soft: rgb(208, 140, 91, 0.15);
|
|
227
232
|
--danger: #ff4d54;
|
|
228
233
|
--danger-soft: rgb(255, 77, 84, 0.15);
|
|
234
|
+
--info: #6fb0e6;
|
|
235
|
+
--info-soft: rgb(111, 176, 230, 0.14);
|
|
229
236
|
--code-bg: rgb(255, 255, 255, 0.05);
|
|
230
237
|
--button-text: #000000;
|
|
231
238
|
--field-dot: rgb(242, 242, 242, 0.14);
|