@ponchia/ui 0.3.4 → 0.3.5
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 +108 -176
- package/behaviors/index.d.ts +16 -0
- package/behaviors/index.js +196 -0
- package/classes/index.d.ts +39 -0
- package/classes/index.js +39 -0
- package/css/disclosure.css +180 -0
- package/css/feedback.css +111 -0
- package/css/forms.css +44 -0
- package/css/overlay.css +48 -0
- package/css/primitives.css +60 -0
- package/css/site.css +33 -0
- package/dist/bronto.css +1 -1
- package/dist/css/disclosure.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/forms.css +1 -1
- package/dist/css/overlay.css +1 -1
- package/dist/css/primitives.css +1 -1
- package/dist/css/site.css +1 -1
- package/docs/reference.md +71 -1
- package/docs/usage.md +52 -0
- package/fonts/OFL.txt +93 -0
- package/package.json +17 -2
package/classes/index.d.ts
CHANGED
|
@@ -85,6 +85,9 @@ export declare const cls: {
|
|
|
85
85
|
readonly hintError: 'ui-hint--error';
|
|
86
86
|
readonly inputGroup: 'ui-input-group';
|
|
87
87
|
readonly inputGroupAddon: 'ui-input-group__addon';
|
|
88
|
+
readonly inputIcon: 'ui-input-icon';
|
|
89
|
+
readonly inputIconSlot: 'ui-input-icon__icon';
|
|
90
|
+
readonly inputIconEnd: 'ui-input-icon--end';
|
|
88
91
|
readonly file: 'ui-file';
|
|
89
92
|
readonly range: 'ui-range';
|
|
90
93
|
readonly errorSummary: 'ui-error-summary';
|
|
@@ -115,6 +118,15 @@ export declare const cls: {
|
|
|
115
118
|
readonly progress: 'ui-progress';
|
|
116
119
|
readonly progressBar: 'ui-progress__bar';
|
|
117
120
|
readonly progressIndeterminate: 'ui-progress--indeterminate';
|
|
121
|
+
readonly meter: 'ui-meter';
|
|
122
|
+
readonly meterFill: 'ui-meter__fill';
|
|
123
|
+
readonly meterAccent: 'ui-meter--accent';
|
|
124
|
+
readonly meterSuccess: 'ui-meter--success';
|
|
125
|
+
readonly meterWarning: 'ui-meter--warning';
|
|
126
|
+
readonly meterDanger: 'ui-meter--danger';
|
|
127
|
+
readonly steps: 'ui-steps';
|
|
128
|
+
readonly stepsItem: 'ui-steps__item';
|
|
129
|
+
readonly stepsItemDone: 'ui-steps__item--done';
|
|
118
130
|
readonly modal: 'ui-modal';
|
|
119
131
|
readonly modalHead: 'ui-modal__head';
|
|
120
132
|
readonly modalTitle: 'ui-modal__title';
|
|
@@ -132,6 +144,8 @@ export declare const cls: {
|
|
|
132
144
|
readonly comboboxList: 'ui-combobox__list';
|
|
133
145
|
readonly comboboxOption: 'ui-combobox__option';
|
|
134
146
|
readonly comboboxEmpty: 'ui-combobox__empty';
|
|
147
|
+
readonly lightbox: 'ui-lightbox';
|
|
148
|
+
readonly lightboxClose: 'ui-lightbox__close';
|
|
135
149
|
readonly tabs: 'ui-tabs';
|
|
136
150
|
readonly tabsList: 'ui-tabs__list';
|
|
137
151
|
readonly tab: 'ui-tab';
|
|
@@ -150,6 +164,15 @@ export declare const cls: {
|
|
|
150
164
|
readonly avatarSm: 'ui-avatar--sm';
|
|
151
165
|
readonly avatarLg: 'ui-avatar--lg';
|
|
152
166
|
readonly avatarGroup: 'ui-avatar-group';
|
|
167
|
+
readonly carousel: 'ui-carousel';
|
|
168
|
+
readonly carouselStage: 'ui-carousel__stage';
|
|
169
|
+
readonly carouselViewport: 'ui-carousel__viewport';
|
|
170
|
+
readonly carouselSlide: 'ui-carousel__slide';
|
|
171
|
+
readonly carouselPrev: 'ui-carousel__prev';
|
|
172
|
+
readonly carouselNext: 'ui-carousel__next';
|
|
173
|
+
readonly carouselStatus: 'ui-carousel__status';
|
|
174
|
+
readonly carouselThumbs: 'ui-carousel__thumbs';
|
|
175
|
+
readonly carouselThumb: 'ui-carousel__thumb';
|
|
153
176
|
readonly table: 'ui-table';
|
|
154
177
|
readonly tableDense: 'ui-table--dense';
|
|
155
178
|
readonly tableComfortable: 'ui-table--comfortable';
|
|
@@ -190,6 +213,9 @@ export declare const cls: {
|
|
|
190
213
|
readonly siteheaderSticky: 'ui-siteheader--sticky';
|
|
191
214
|
readonly siteheaderBrand: 'ui-siteheader__brand';
|
|
192
215
|
readonly siteheaderActions: 'ui-siteheader__actions';
|
|
216
|
+
readonly pagehead: 'ui-pagehead';
|
|
217
|
+
readonly pageheadTitle: 'ui-pagehead__title';
|
|
218
|
+
readonly pageheadActions: 'ui-pagehead__actions';
|
|
193
219
|
readonly sitenav: 'ui-sitenav';
|
|
194
220
|
readonly sitemenu: 'ui-sitemenu';
|
|
195
221
|
readonly sitemenuPanel: 'ui-sitemenu__panel';
|
|
@@ -200,6 +226,10 @@ export declare const cls: {
|
|
|
200
226
|
readonly tagAccent: 'ui-tag--accent';
|
|
201
227
|
readonly meta: 'ui-meta';
|
|
202
228
|
readonly metaItem: 'ui-meta__item';
|
|
229
|
+
readonly timeline: 'ui-timeline';
|
|
230
|
+
readonly timelineItem: 'ui-timeline__item';
|
|
231
|
+
readonly timelineTime: 'ui-timeline__time';
|
|
232
|
+
readonly kbd: 'ui-kbd';
|
|
203
233
|
readonly display: 'ui-display';
|
|
204
234
|
readonly mono: 'ui-mono';
|
|
205
235
|
readonly muted: 'ui-muted';
|
|
@@ -307,6 +337,9 @@ export interface ToastOpts {
|
|
|
307
337
|
export interface ProgressOpts {
|
|
308
338
|
indeterminate?: boolean;
|
|
309
339
|
}
|
|
340
|
+
export interface MeterOpts {
|
|
341
|
+
tone?: 'accent' | 'success' | 'warning' | 'danger';
|
|
342
|
+
}
|
|
310
343
|
export interface DotspinnerOpts {
|
|
311
344
|
size?: 'sm' | 'lg';
|
|
312
345
|
}
|
|
@@ -334,6 +367,10 @@ export interface ContainerOpts {
|
|
|
334
367
|
export interface TagOpts {
|
|
335
368
|
accent?: boolean;
|
|
336
369
|
}
|
|
370
|
+
export interface InputIconOpts {
|
|
371
|
+
/** Place the icon at the inline-end instead of the start. */
|
|
372
|
+
end?: boolean;
|
|
373
|
+
}
|
|
337
374
|
|
|
338
375
|
export interface Ui {
|
|
339
376
|
button(opts?: ButtonOpts): string;
|
|
@@ -352,6 +389,7 @@ export interface Ui {
|
|
|
352
389
|
alert(opts?: AlertOpts): string;
|
|
353
390
|
toast(opts?: ToastOpts): string;
|
|
354
391
|
progress(opts?: ProgressOpts): string;
|
|
392
|
+
meter(opts?: MeterOpts): string;
|
|
355
393
|
dotspinner(opts?: DotspinnerOpts): string;
|
|
356
394
|
dotbar(opts?: DotbarOpts): string;
|
|
357
395
|
modal(opts?: ModalOpts): string;
|
|
@@ -360,6 +398,7 @@ export interface Ui {
|
|
|
360
398
|
prose(opts?: ProseOpts): string;
|
|
361
399
|
container(opts?: ContainerOpts): string;
|
|
362
400
|
tag(opts?: TagOpts): string;
|
|
401
|
+
inputIcon(opts?: InputIconOpts): string;
|
|
363
402
|
}
|
|
364
403
|
|
|
365
404
|
export declare const ui: Ui;
|
package/classes/index.js
CHANGED
|
@@ -91,6 +91,9 @@ export const cls = Object.freeze({
|
|
|
91
91
|
hintError: 'ui-hint--error',
|
|
92
92
|
inputGroup: 'ui-input-group',
|
|
93
93
|
inputGroupAddon: 'ui-input-group__addon',
|
|
94
|
+
inputIcon: 'ui-input-icon',
|
|
95
|
+
inputIconSlot: 'ui-input-icon__icon',
|
|
96
|
+
inputIconEnd: 'ui-input-icon--end',
|
|
94
97
|
file: 'ui-file',
|
|
95
98
|
range: 'ui-range',
|
|
96
99
|
errorSummary: 'ui-error-summary',
|
|
@@ -122,6 +125,15 @@ export const cls = Object.freeze({
|
|
|
122
125
|
progress: 'ui-progress',
|
|
123
126
|
progressBar: 'ui-progress__bar',
|
|
124
127
|
progressIndeterminate: 'ui-progress--indeterminate',
|
|
128
|
+
meter: 'ui-meter',
|
|
129
|
+
meterFill: 'ui-meter__fill',
|
|
130
|
+
meterAccent: 'ui-meter--accent',
|
|
131
|
+
meterSuccess: 'ui-meter--success',
|
|
132
|
+
meterWarning: 'ui-meter--warning',
|
|
133
|
+
meterDanger: 'ui-meter--danger',
|
|
134
|
+
steps: 'ui-steps',
|
|
135
|
+
stepsItem: 'ui-steps__item',
|
|
136
|
+
stepsItemDone: 'ui-steps__item--done',
|
|
125
137
|
// overlay
|
|
126
138
|
modal: 'ui-modal',
|
|
127
139
|
modalHead: 'ui-modal__head',
|
|
@@ -140,6 +152,8 @@ export const cls = Object.freeze({
|
|
|
140
152
|
comboboxList: 'ui-combobox__list',
|
|
141
153
|
comboboxOption: 'ui-combobox__option',
|
|
142
154
|
comboboxEmpty: 'ui-combobox__empty',
|
|
155
|
+
lightbox: 'ui-lightbox',
|
|
156
|
+
lightboxClose: 'ui-lightbox__close',
|
|
143
157
|
// disclosure
|
|
144
158
|
tabs: 'ui-tabs',
|
|
145
159
|
tabsList: 'ui-tabs__list',
|
|
@@ -159,6 +173,15 @@ export const cls = Object.freeze({
|
|
|
159
173
|
avatarSm: 'ui-avatar--sm',
|
|
160
174
|
avatarLg: 'ui-avatar--lg',
|
|
161
175
|
avatarGroup: 'ui-avatar-group',
|
|
176
|
+
carousel: 'ui-carousel',
|
|
177
|
+
carouselStage: 'ui-carousel__stage',
|
|
178
|
+
carouselViewport: 'ui-carousel__viewport',
|
|
179
|
+
carouselSlide: 'ui-carousel__slide',
|
|
180
|
+
carouselPrev: 'ui-carousel__prev',
|
|
181
|
+
carouselNext: 'ui-carousel__next',
|
|
182
|
+
carouselStatus: 'ui-carousel__status',
|
|
183
|
+
carouselThumbs: 'ui-carousel__thumbs',
|
|
184
|
+
carouselThumb: 'ui-carousel__thumb',
|
|
162
185
|
// table
|
|
163
186
|
table: 'ui-table',
|
|
164
187
|
tableDense: 'ui-table--dense',
|
|
@@ -203,6 +226,9 @@ export const cls = Object.freeze({
|
|
|
203
226
|
siteheaderSticky: 'ui-siteheader--sticky',
|
|
204
227
|
siteheaderBrand: 'ui-siteheader__brand',
|
|
205
228
|
siteheaderActions: 'ui-siteheader__actions',
|
|
229
|
+
pagehead: 'ui-pagehead',
|
|
230
|
+
pageheadTitle: 'ui-pagehead__title',
|
|
231
|
+
pageheadActions: 'ui-pagehead__actions',
|
|
206
232
|
sitenav: 'ui-sitenav',
|
|
207
233
|
sitemenu: 'ui-sitemenu',
|
|
208
234
|
sitemenuPanel: 'ui-sitemenu__panel',
|
|
@@ -213,6 +239,10 @@ export const cls = Object.freeze({
|
|
|
213
239
|
tagAccent: 'ui-tag--accent',
|
|
214
240
|
meta: 'ui-meta',
|
|
215
241
|
metaItem: 'ui-meta__item',
|
|
242
|
+
timeline: 'ui-timeline',
|
|
243
|
+
timelineItem: 'ui-timeline__item',
|
|
244
|
+
timelineTime: 'ui-timeline__time',
|
|
245
|
+
kbd: 'ui-kbd',
|
|
216
246
|
display: 'ui-display',
|
|
217
247
|
mono: 'ui-mono',
|
|
218
248
|
muted: 'ui-muted',
|
|
@@ -346,6 +376,14 @@ export const ui = {
|
|
|
346
376
|
tone === 'info' && cls.toastInfo,
|
|
347
377
|
),
|
|
348
378
|
progress: ({ indeterminate } = {}) => j(cls.progress, indeterminate && cls.progressIndeterminate),
|
|
379
|
+
meter: ({ tone } = {}) =>
|
|
380
|
+
j(
|
|
381
|
+
cls.meter,
|
|
382
|
+
tone === 'accent' && cls.meterAccent,
|
|
383
|
+
tone === 'success' && cls.meterSuccess,
|
|
384
|
+
tone === 'warning' && cls.meterWarning,
|
|
385
|
+
tone === 'danger' && cls.meterDanger,
|
|
386
|
+
),
|
|
349
387
|
dotspinner: ({ size } = {}) =>
|
|
350
388
|
j(cls.dotspinner, size === 'sm' && cls.dotspinnerSm, size === 'lg' && cls.dotspinnerLg),
|
|
351
389
|
dotbar: ({ indeterminate } = {}) => j(cls.dotbar, indeterminate && cls.dotbarIndeterminate),
|
|
@@ -357,6 +395,7 @@ export const ui = {
|
|
|
357
395
|
container: ({ narrow, wide } = {}) =>
|
|
358
396
|
j(cls.container, narrow && cls.containerNarrow, wide && cls.containerWide),
|
|
359
397
|
tag: ({ accent } = {}) => j(cls.tag, accent && cls.tagAccent),
|
|
398
|
+
inputIcon: ({ end } = {}) => j(cls.inputIcon, end && cls.inputIconEnd),
|
|
360
399
|
};
|
|
361
400
|
|
|
362
401
|
export default ui;
|
package/css/disclosure.css
CHANGED
|
@@ -321,3 +321,183 @@
|
|
|
321
321
|
.ui-avatar-group .ui-avatar:not(:first-child) {
|
|
322
322
|
margin-inline-start: -0.5rem;
|
|
323
323
|
}
|
|
324
|
+
|
|
325
|
+
/* --- Carousel — a scroll-snap track of slides with an optional thumbnail
|
|
326
|
+
strip. Native horizontal scroll gives touch/trackpad swipe + momentum
|
|
327
|
+
for free; initCarousel adds prev/next, keyboard, thumbnail sync and the
|
|
328
|
+
position counter. A full-screen gallery is this same carousel inside a
|
|
329
|
+
native <dialog class="ui-lightbox"> (see overlay.css) — the dialog
|
|
330
|
+
brings the top layer, focus-trap and Esc; nothing is re-implemented. --- */
|
|
331
|
+
|
|
332
|
+
.ui-carousel {
|
|
333
|
+
display: grid;
|
|
334
|
+
gap: var(--space-sm);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/* Positioned frame for the viewport + overlay controls. The arrows can't
|
|
338
|
+
live inside the scroll viewport (absolute children of a scroll container
|
|
339
|
+
scroll away with the content), so they anchor to this stage instead. */
|
|
340
|
+
.ui-carousel__stage {
|
|
341
|
+
position: relative;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.ui-carousel__viewport {
|
|
345
|
+
display: flex;
|
|
346
|
+
overflow-x: auto;
|
|
347
|
+
scroll-behavior: smooth;
|
|
348
|
+
scroll-snap-type: x mandatory;
|
|
349
|
+
scrollbar-width: none;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.ui-carousel__viewport::-webkit-scrollbar {
|
|
353
|
+
display: none;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.ui-carousel__viewport:focus-visible {
|
|
357
|
+
outline: 2px solid var(--focus-ring);
|
|
358
|
+
outline-offset: -2px;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.ui-carousel__slide {
|
|
362
|
+
flex: 0 0 100%;
|
|
363
|
+
scroll-snap-align: center;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.ui-carousel__slide > :is(img, picture, .ui-ratio) {
|
|
367
|
+
display: block;
|
|
368
|
+
inline-size: 100%;
|
|
369
|
+
object-fit: cover;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* Prev / next — hairline round controls over the stage edges. */
|
|
373
|
+
.ui-carousel__prev,
|
|
374
|
+
.ui-carousel__next {
|
|
375
|
+
align-items: center;
|
|
376
|
+
background: color-mix(in srgb, var(--panel-strong) 80%, transparent);
|
|
377
|
+
border: 1px solid var(--line-strong);
|
|
378
|
+
border-radius: var(--radius-pill);
|
|
379
|
+
color: var(--text);
|
|
380
|
+
cursor: pointer;
|
|
381
|
+
display: inline-flex;
|
|
382
|
+
block-size: 2.2rem;
|
|
383
|
+
inline-size: 2.2rem;
|
|
384
|
+
inset-block-start: 50%;
|
|
385
|
+
justify-content: center;
|
|
386
|
+
position: absolute;
|
|
387
|
+
transform: translateY(-50%);
|
|
388
|
+
z-index: var(--z-raised);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.ui-carousel__prev {
|
|
392
|
+
inset-inline-start: var(--space-xs);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.ui-carousel__next {
|
|
396
|
+
inset-inline-end: var(--space-xs);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/* Chevron glyph, mirrors for RTL via the logical border edges. */
|
|
400
|
+
.ui-carousel__prev::before,
|
|
401
|
+
.ui-carousel__next::before {
|
|
402
|
+
block-size: 0.5rem;
|
|
403
|
+
border-block-start: 1.5px solid currentColor;
|
|
404
|
+
border-inline-end: 1.5px solid currentColor;
|
|
405
|
+
content: '';
|
|
406
|
+
inline-size: 0.5rem;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.ui-carousel__prev::before {
|
|
410
|
+
margin-inline-start: 0.2rem;
|
|
411
|
+
transform: rotate(-135deg);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.ui-carousel__next::before {
|
|
415
|
+
margin-inline-end: 0.2rem;
|
|
416
|
+
transform: rotate(45deg);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/* The chevron direction is a physical rotation, so flip it for RTL (the
|
|
420
|
+
buttons already swap sides via the logical insets above). */
|
|
421
|
+
[dir='rtl'] .ui-carousel__prev::before {
|
|
422
|
+
transform: rotate(45deg);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
[dir='rtl'] .ui-carousel__next::before {
|
|
426
|
+
transform: rotate(-135deg);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.ui-carousel__prev:disabled,
|
|
430
|
+
.ui-carousel__next:disabled {
|
|
431
|
+
cursor: not-allowed;
|
|
432
|
+
opacity: 0.35;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.ui-carousel__prev:focus-visible,
|
|
436
|
+
.ui-carousel__next:focus-visible {
|
|
437
|
+
outline: 2px solid var(--focus-ring);
|
|
438
|
+
outline-offset: 2px;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* Position counter ("3 / 12") — initCarousel fills the text; aria-live. */
|
|
442
|
+
.ui-carousel__status {
|
|
443
|
+
background: color-mix(in srgb, var(--panel-strong) 75%, transparent);
|
|
444
|
+
border-radius: var(--radius-pill);
|
|
445
|
+
color: var(--text);
|
|
446
|
+
font-family: var(--mono);
|
|
447
|
+
font-size: var(--text-2xs);
|
|
448
|
+
inset-block-end: var(--space-xs);
|
|
449
|
+
inset-inline-start: var(--space-xs);
|
|
450
|
+
letter-spacing: var(--tracking-wide);
|
|
451
|
+
padding: 0.2rem 0.5rem;
|
|
452
|
+
position: absolute;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/* Thumbnail strip — clicking a thumb jumps the track; active = accent. */
|
|
456
|
+
.ui-carousel__thumbs {
|
|
457
|
+
display: flex;
|
|
458
|
+
gap: 0.4rem;
|
|
459
|
+
list-style: none;
|
|
460
|
+
margin: 0;
|
|
461
|
+
overflow-x: auto;
|
|
462
|
+
padding: 0;
|
|
463
|
+
scroll-behavior: smooth;
|
|
464
|
+
scrollbar-width: none;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.ui-carousel__thumbs::-webkit-scrollbar {
|
|
468
|
+
display: none;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.ui-carousel__thumb {
|
|
472
|
+
background: transparent;
|
|
473
|
+
border: 1px solid var(--line);
|
|
474
|
+
border-radius: var(--radius-sm);
|
|
475
|
+
cursor: pointer;
|
|
476
|
+
flex: 0 0 auto;
|
|
477
|
+
block-size: 3rem;
|
|
478
|
+
inline-size: 4rem;
|
|
479
|
+
overflow: hidden;
|
|
480
|
+
padding: 0;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.ui-carousel__thumb > img {
|
|
484
|
+
block-size: 100%;
|
|
485
|
+
inline-size: 100%;
|
|
486
|
+
object-fit: cover;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.ui-carousel__thumb[aria-current='true'] {
|
|
490
|
+
border-color: var(--accent);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.ui-carousel__thumb:focus-visible {
|
|
494
|
+
outline: 2px solid var(--focus-ring);
|
|
495
|
+
outline-offset: 2px;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
@media (prefers-reduced-motion: reduce) {
|
|
499
|
+
.ui-carousel__viewport,
|
|
500
|
+
.ui-carousel__thumbs {
|
|
501
|
+
scroll-behavior: auto;
|
|
502
|
+
}
|
|
503
|
+
}
|
package/css/feedback.css
CHANGED
|
@@ -338,3 +338,114 @@
|
|
|
338
338
|
animation: none;
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
|
+
|
|
342
|
+
/* --- Meter — a labelled proportion / threshold bar. Unlike ui-progress
|
|
343
|
+
(task progress, can be indeterminate), a meter shows a measured static
|
|
344
|
+
value for data display (coverage, capacity, a KPI against a target).
|
|
345
|
+
Drive the fill with the same --value knob as progress; tone the fill by
|
|
346
|
+
threshold. Author role="meter" + aria-valuenow/min/max for AT. --- */
|
|
347
|
+
|
|
348
|
+
.ui-meter {
|
|
349
|
+
background: var(--panel-soft);
|
|
350
|
+
border: 1px solid var(--line);
|
|
351
|
+
border-radius: var(--radius-pill);
|
|
352
|
+
block-size: 0.5rem;
|
|
353
|
+
overflow: hidden;
|
|
354
|
+
inline-size: 100%;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* Default (untoned) fill is --text-dim, not --line-strong: the fill
|
|
358
|
+
conveys the measured value, so it must clear the 3:1 non-text floor
|
|
359
|
+
against the --panel-soft track (--line-strong is only ~2:1). --text-dim
|
|
360
|
+
on --surface-muted is already a gated contrast pairing (≥4.5:1). */
|
|
361
|
+
.ui-meter__fill {
|
|
362
|
+
background: var(--text-dim);
|
|
363
|
+
block-size: 100%;
|
|
364
|
+
transition: inline-size var(--duration-base) var(--ease-out);
|
|
365
|
+
inline-size: calc(clamp(0, var(--value, 0), 100) * 1%);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.ui-meter--accent .ui-meter__fill {
|
|
369
|
+
background: var(--accent);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.ui-meter--success .ui-meter__fill {
|
|
373
|
+
background: var(--success);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.ui-meter--warning .ui-meter__fill {
|
|
377
|
+
background: var(--warning);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.ui-meter--danger .ui-meter__fill {
|
|
381
|
+
background: var(--danger);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* --- Steps — progress through a multi-step flow. Use an <ol>; the
|
|
385
|
+
current step is aria-current="step" (no class), completed steps take
|
|
386
|
+
--done. Counter-numbered markers, hairline connectors. --- */
|
|
387
|
+
|
|
388
|
+
.ui-steps {
|
|
389
|
+
counter-reset: ui-step;
|
|
390
|
+
display: flex;
|
|
391
|
+
flex-wrap: wrap;
|
|
392
|
+
gap: var(--space-sm);
|
|
393
|
+
list-style: none;
|
|
394
|
+
margin: 0;
|
|
395
|
+
padding: 0;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.ui-steps__item {
|
|
399
|
+
align-items: center;
|
|
400
|
+
color: var(--text-dim);
|
|
401
|
+
counter-increment: ui-step;
|
|
402
|
+
display: inline-flex;
|
|
403
|
+
flex: 1 1 auto;
|
|
404
|
+
font-family: var(--mono);
|
|
405
|
+
font-size: var(--text-xs);
|
|
406
|
+
gap: 0.5rem;
|
|
407
|
+
letter-spacing: var(--tracking-wide);
|
|
408
|
+
min-inline-size: max-content;
|
|
409
|
+
text-transform: uppercase;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.ui-steps__item::before {
|
|
413
|
+
align-items: center;
|
|
414
|
+
border: 1px solid var(--line-strong);
|
|
415
|
+
border-radius: 50%;
|
|
416
|
+
color: var(--text-dim);
|
|
417
|
+
content: counter(ui-step);
|
|
418
|
+
display: inline-flex;
|
|
419
|
+
flex: 0 0 auto;
|
|
420
|
+
block-size: 1.5rem;
|
|
421
|
+
inline-size: 1.5rem;
|
|
422
|
+
justify-content: center;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/* Connector hairline trailing every item except the last. */
|
|
426
|
+
.ui-steps__item:not(:last-child)::after {
|
|
427
|
+
background: var(--line);
|
|
428
|
+
block-size: 1px;
|
|
429
|
+
content: '';
|
|
430
|
+
flex: 1 1 1.5rem;
|
|
431
|
+
min-inline-size: 1rem;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.ui-steps__item[aria-current='step'] {
|
|
435
|
+
color: var(--text);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.ui-steps__item[aria-current='step']::before {
|
|
439
|
+
border-color: var(--accent);
|
|
440
|
+
color: var(--accent-text);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.ui-steps__item--done {
|
|
444
|
+
color: var(--text-soft);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.ui-steps__item--done::before {
|
|
448
|
+
background: var(--accent);
|
|
449
|
+
border-color: var(--accent);
|
|
450
|
+
color: var(--button-text);
|
|
451
|
+
}
|
package/css/forms.css
CHANGED
|
@@ -131,6 +131,50 @@
|
|
|
131
131
|
white-space: nowrap;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/* --- Input icon: a leading (or trailing) glyph *inside* a single
|
|
135
|
+
control, distinct from input-group's adjacent affix. Wrap the input:
|
|
136
|
+
<span class="ui-input-icon"><svg class="ui-input-icon__icon"/>
|
|
137
|
+
<input class="ui-input"></span>. The icon is a decorative overlay; the
|
|
138
|
+
input keeps full width and gains padding on the icon side. --end moves
|
|
139
|
+
the icon to the inline-end. RTL falls out of the logical props. --- */
|
|
140
|
+
.ui-input-icon {
|
|
141
|
+
display: block;
|
|
142
|
+
position: relative;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.ui-input-icon > .ui-input {
|
|
146
|
+
padding-inline-start: 2.2rem;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.ui-input-icon--end > .ui-input {
|
|
150
|
+
padding-inline: 0.7rem 2.2rem;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.ui-input-icon__icon {
|
|
154
|
+
align-items: center;
|
|
155
|
+
color: var(--text-dim);
|
|
156
|
+
display: inline-flex;
|
|
157
|
+
inset-block-start: 50%;
|
|
158
|
+
inset-inline-start: 0.7rem;
|
|
159
|
+
justify-content: center;
|
|
160
|
+
pointer-events: none;
|
|
161
|
+
position: absolute;
|
|
162
|
+
transform: translateY(-50%);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.ui-input-icon--end .ui-input-icon__icon {
|
|
166
|
+
inset-inline: auto 0.7rem;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.ui-input-icon__icon svg {
|
|
170
|
+
block-size: 1rem;
|
|
171
|
+
inline-size: 1rem;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.ui-input-icon:focus-within .ui-input-icon__icon {
|
|
175
|
+
color: var(--accent);
|
|
176
|
+
}
|
|
177
|
+
|
|
134
178
|
/* --- File input --- */
|
|
135
179
|
.ui-file {
|
|
136
180
|
color: var(--text-soft);
|
package/css/overlay.css
CHANGED
|
@@ -105,6 +105,54 @@
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/* --- Lightbox — a full-viewport <dialog> wrapping a .ui-carousel. The
|
|
109
|
+
native <dialog> brings the top layer, focus-trap and Escape; initDialog
|
|
110
|
+
adds open (`data-bronto-open`) + focus-return; initCarousel drives the
|
|
111
|
+
inner carousel. No bespoke overlay/focus code — that is the whole point
|
|
112
|
+
of building it on <dialog>. --- */
|
|
113
|
+
.ui-lightbox {
|
|
114
|
+
background: color-mix(in srgb, var(--bg) 92%, #000);
|
|
115
|
+
border: 0;
|
|
116
|
+
block-size: 100vh;
|
|
117
|
+
color: var(--text);
|
|
118
|
+
max-block-size: 100vh;
|
|
119
|
+
max-inline-size: 100vw;
|
|
120
|
+
padding: 0;
|
|
121
|
+
inline-size: 100vw;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.ui-lightbox::backdrop {
|
|
125
|
+
background: color-mix(in srgb, #000 80%, transparent);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.ui-lightbox[open] {
|
|
129
|
+
align-content: center;
|
|
130
|
+
display: grid;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.ui-lightbox .ui-carousel {
|
|
134
|
+
inline-size: 100%;
|
|
135
|
+
margin: auto;
|
|
136
|
+
max-inline-size: min(64rem, 100vw);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Lightbox images are shown whole (contain), not cropped like the inline
|
|
140
|
+
carousel's cover thumbnails. */
|
|
141
|
+
.ui-lightbox .ui-carousel__slide > img {
|
|
142
|
+
margin-inline: auto;
|
|
143
|
+
max-block-size: 80vh;
|
|
144
|
+
max-inline-size: 100%;
|
|
145
|
+
inline-size: auto;
|
|
146
|
+
object-fit: contain;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.ui-lightbox__close {
|
|
150
|
+
inset-block-start: var(--space-sm);
|
|
151
|
+
inset-inline-end: var(--space-sm);
|
|
152
|
+
position: fixed;
|
|
153
|
+
z-index: var(--z-raised);
|
|
154
|
+
}
|
|
155
|
+
|
|
108
156
|
/* --- Dropdown menu — pairs with native <details> --- */
|
|
109
157
|
|
|
110
158
|
/* The positioned host for a <details> dropdown: .ui-menu is absolute, so
|
package/css/primitives.css
CHANGED
|
@@ -631,3 +631,63 @@
|
|
|
631
631
|
color: var(--text-dim);
|
|
632
632
|
margin: 0.3rem 0 0;
|
|
633
633
|
}
|
|
634
|
+
|
|
635
|
+
/* --- Keyboard key — inline glyph for shortcut hints. Wrap a <kbd>. --- */
|
|
636
|
+
|
|
637
|
+
.ui-kbd {
|
|
638
|
+
align-items: center;
|
|
639
|
+
background: var(--panel-soft);
|
|
640
|
+
border: 1px solid var(--line-strong);
|
|
641
|
+
border-radius: var(--radius-sm);
|
|
642
|
+
color: var(--text-soft);
|
|
643
|
+
display: inline-flex;
|
|
644
|
+
font-family: var(--mono);
|
|
645
|
+
font-size: var(--text-2xs);
|
|
646
|
+
justify-content: center;
|
|
647
|
+
line-height: 1;
|
|
648
|
+
min-inline-size: 1.4rem;
|
|
649
|
+
padding: 0.18rem 0.4rem;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/* --- Timeline — vertical event list on a hairline spine. Use an <ol>;
|
|
653
|
+
each <li> is a __item with an optional __time label. aria-current marks
|
|
654
|
+
the live/most-recent event (accent marker). --- */
|
|
655
|
+
|
|
656
|
+
.ui-timeline {
|
|
657
|
+
display: grid;
|
|
658
|
+
gap: var(--space-md);
|
|
659
|
+
list-style: none;
|
|
660
|
+
margin: 0;
|
|
661
|
+
padding: 0;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.ui-timeline__item {
|
|
665
|
+
border-inline-start: 1px solid var(--line);
|
|
666
|
+
display: grid;
|
|
667
|
+
gap: 0.2rem;
|
|
668
|
+
padding-inline-start: var(--space-md);
|
|
669
|
+
position: relative;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.ui-timeline__item::before {
|
|
673
|
+
background: var(--line-strong);
|
|
674
|
+
border-radius: 50%;
|
|
675
|
+
block-size: 0.5rem;
|
|
676
|
+
content: '';
|
|
677
|
+
inline-size: 0.5rem;
|
|
678
|
+
inset-block-start: 0.18rem;
|
|
679
|
+
inset-inline-start: calc(-0.25rem - 0.5px);
|
|
680
|
+
position: absolute;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
.ui-timeline__item[aria-current]::before {
|
|
684
|
+
background: var(--accent);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.ui-timeline__time {
|
|
688
|
+
color: var(--text-dim);
|
|
689
|
+
font-family: var(--mono);
|
|
690
|
+
font-size: var(--text-2xs);
|
|
691
|
+
letter-spacing: var(--tracking-wide);
|
|
692
|
+
text-transform: uppercase;
|
|
693
|
+
}
|
package/css/site.css
CHANGED
|
@@ -91,6 +91,39 @@
|
|
|
91
91
|
gap: var(--space-xs);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/* --- Page header — the in-page title bar every view re-hand-rolls:
|
|
95
|
+
eyebrow/breadcrumb + title on the start, actions on the end, one
|
|
96
|
+
hairline beneath. Compose the existing ui-eyebrow / ui-breadcrumb in
|
|
97
|
+
the title block (don't duplicate them); __actions is a cluster. --- */
|
|
98
|
+
|
|
99
|
+
.ui-pagehead {
|
|
100
|
+
align-items: flex-end;
|
|
101
|
+
border-block-end: 1px solid var(--line);
|
|
102
|
+
display: flex;
|
|
103
|
+
flex-wrap: wrap;
|
|
104
|
+
gap: var(--space-md);
|
|
105
|
+
justify-content: space-between;
|
|
106
|
+
padding-block-end: var(--space-md);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.ui-pagehead__title {
|
|
110
|
+
color: var(--text);
|
|
111
|
+
font-family: var(--display);
|
|
112
|
+
font-size: var(--text-xl);
|
|
113
|
+
letter-spacing: var(--tracking-wide);
|
|
114
|
+
line-height: 1.1;
|
|
115
|
+
margin: 0;
|
|
116
|
+
text-transform: uppercase;
|
|
117
|
+
text-wrap: balance;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.ui-pagehead__actions {
|
|
121
|
+
align-items: center;
|
|
122
|
+
display: inline-flex;
|
|
123
|
+
flex-wrap: wrap;
|
|
124
|
+
gap: var(--space-xs);
|
|
125
|
+
}
|
|
126
|
+
|
|
94
127
|
/* --- Nav — desktop inline, dot active cue via aria-current --- */
|
|
95
128
|
|
|
96
129
|
.ui-sitenav {
|