@ponchia/ui 0.3.3 → 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.
@@ -39,6 +39,7 @@ export declare const cls: {
39
39
  readonly badgeSuccess: 'ui-badge--success';
40
40
  readonly badgeWarning: 'ui-badge--warning';
41
41
  readonly badgeDanger: 'ui-badge--danger';
42
+ readonly badgeInfo: 'ui-badge--info';
42
43
  readonly badgeMuted: 'ui-badge--muted';
43
44
  readonly badgeDot: 'ui-badge--dot';
44
45
  readonly chip: 'ui-chip';
@@ -53,6 +54,7 @@ export declare const cls: {
53
54
  readonly dotSuccess: 'ui-dot--success';
54
55
  readonly dotWarning: 'ui-dot--warning';
55
56
  readonly dotDanger: 'ui-dot--danger';
57
+ readonly dotInfo: 'ui-dot--info';
56
58
  readonly dotLive: 'ui-dot--live';
57
59
  readonly dotgrid: 'ui-dotgrid';
58
60
  readonly dotgridAccent: 'ui-dotgrid--accent';
@@ -83,6 +85,9 @@ export declare const cls: {
83
85
  readonly hintError: 'ui-hint--error';
84
86
  readonly inputGroup: 'ui-input-group';
85
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';
86
91
  readonly file: 'ui-file';
87
92
  readonly range: 'ui-range';
88
93
  readonly errorSummary: 'ui-error-summary';
@@ -96,6 +101,7 @@ export declare const cls: {
96
101
  readonly alertSuccess: 'ui-alert--success';
97
102
  readonly alertWarning: 'ui-alert--warning';
98
103
  readonly alertDanger: 'ui-alert--danger';
104
+ readonly alertInfo: 'ui-alert--info';
99
105
  readonly toastStack: 'ui-toast-stack';
100
106
  readonly toastStackAssertive: 'ui-toast-stack--assertive';
101
107
  readonly toast: 'ui-toast';
@@ -105,12 +111,22 @@ export declare const cls: {
105
111
  readonly toastSuccess: 'ui-toast--success';
106
112
  readonly toastWarning: 'ui-toast--warning';
107
113
  readonly toastDanger: 'ui-toast--danger';
114
+ readonly toastInfo: 'ui-toast--info';
108
115
  readonly tooltip: 'ui-tooltip';
109
116
  readonly tooltipBubble: 'ui-tooltip__bubble';
110
117
  readonly popover: 'ui-popover';
111
118
  readonly progress: 'ui-progress';
112
119
  readonly progressBar: 'ui-progress__bar';
113
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';
114
130
  readonly modal: 'ui-modal';
115
131
  readonly modalHead: 'ui-modal__head';
116
132
  readonly modalTitle: 'ui-modal__title';
@@ -128,6 +144,8 @@ export declare const cls: {
128
144
  readonly comboboxList: 'ui-combobox__list';
129
145
  readonly comboboxOption: 'ui-combobox__option';
130
146
  readonly comboboxEmpty: 'ui-combobox__empty';
147
+ readonly lightbox: 'ui-lightbox';
148
+ readonly lightboxClose: 'ui-lightbox__close';
131
149
  readonly tabs: 'ui-tabs';
132
150
  readonly tabsList: 'ui-tabs__list';
133
151
  readonly tab: 'ui-tab';
@@ -146,6 +164,15 @@ export declare const cls: {
146
164
  readonly avatarSm: 'ui-avatar--sm';
147
165
  readonly avatarLg: 'ui-avatar--lg';
148
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';
149
176
  readonly table: 'ui-table';
150
177
  readonly tableDense: 'ui-table--dense';
151
178
  readonly tableComfortable: 'ui-table--comfortable';
@@ -186,6 +213,9 @@ export declare const cls: {
186
213
  readonly siteheaderSticky: 'ui-siteheader--sticky';
187
214
  readonly siteheaderBrand: 'ui-siteheader__brand';
188
215
  readonly siteheaderActions: 'ui-siteheader__actions';
216
+ readonly pagehead: 'ui-pagehead';
217
+ readonly pageheadTitle: 'ui-pagehead__title';
218
+ readonly pageheadActions: 'ui-pagehead__actions';
189
219
  readonly sitenav: 'ui-sitenav';
190
220
  readonly sitemenu: 'ui-sitemenu';
191
221
  readonly sitemenuPanel: 'ui-sitemenu__panel';
@@ -196,6 +226,10 @@ export declare const cls: {
196
226
  readonly tagAccent: 'ui-tag--accent';
197
227
  readonly meta: 'ui-meta';
198
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';
199
233
  readonly display: 'ui-display';
200
234
  readonly mono: 'ui-mono';
201
235
  readonly muted: 'ui-muted';
@@ -255,7 +289,7 @@ export interface CardOpts {
255
289
  interactive?: boolean;
256
290
  }
257
291
  export interface BadgeOpts {
258
- tone?: 'accent' | 'success' | 'warning' | 'danger' | 'muted';
292
+ tone?: 'accent' | 'success' | 'warning' | 'danger' | 'info' | 'muted';
259
293
  dot?: boolean;
260
294
  }
261
295
  export interface NumOpts {
@@ -269,7 +303,7 @@ export interface LinkOpts {
269
303
  cta?: boolean;
270
304
  }
271
305
  export interface DotOpts {
272
- tone?: 'accent' | 'success' | 'warning' | 'danger';
306
+ tone?: 'accent' | 'success' | 'warning' | 'danger' | 'info';
273
307
  live?: boolean;
274
308
  }
275
309
  export interface DotgridOpts {
@@ -293,7 +327,7 @@ export interface ClusterOpts {
293
327
  export interface StaggerOpts {
294
328
  auto?: boolean;
295
329
  }
296
- export type Tone = 'accent' | 'success' | 'warning' | 'danger';
330
+ export type Tone = 'accent' | 'success' | 'warning' | 'danger' | 'info';
297
331
  export interface AlertOpts {
298
332
  tone?: Tone;
299
333
  }
@@ -303,6 +337,9 @@ export interface ToastOpts {
303
337
  export interface ProgressOpts {
304
338
  indeterminate?: boolean;
305
339
  }
340
+ export interface MeterOpts {
341
+ tone?: 'accent' | 'success' | 'warning' | 'danger';
342
+ }
306
343
  export interface DotspinnerOpts {
307
344
  size?: 'sm' | 'lg';
308
345
  }
@@ -330,6 +367,10 @@ export interface ContainerOpts {
330
367
  export interface TagOpts {
331
368
  accent?: boolean;
332
369
  }
370
+ export interface InputIconOpts {
371
+ /** Place the icon at the inline-end instead of the start. */
372
+ end?: boolean;
373
+ }
333
374
 
334
375
  export interface Ui {
335
376
  button(opts?: ButtonOpts): string;
@@ -348,6 +389,7 @@ export interface Ui {
348
389
  alert(opts?: AlertOpts): string;
349
390
  toast(opts?: ToastOpts): string;
350
391
  progress(opts?: ProgressOpts): string;
392
+ meter(opts?: MeterOpts): string;
351
393
  dotspinner(opts?: DotspinnerOpts): string;
352
394
  dotbar(opts?: DotbarOpts): string;
353
395
  modal(opts?: ModalOpts): string;
@@ -356,6 +398,7 @@ export interface Ui {
356
398
  prose(opts?: ProseOpts): string;
357
399
  container(opts?: ContainerOpts): string;
358
400
  tag(opts?: TagOpts): string;
401
+ inputIcon(opts?: InputIconOpts): string;
359
402
  }
360
403
 
361
404
  export declare const ui: Ui;
package/classes/index.js CHANGED
@@ -43,6 +43,7 @@ export const cls = Object.freeze({
43
43
  badgeSuccess: 'ui-badge--success',
44
44
  badgeWarning: 'ui-badge--warning',
45
45
  badgeDanger: 'ui-badge--danger',
46
+ badgeInfo: 'ui-badge--info',
46
47
  badgeMuted: 'ui-badge--muted',
47
48
  badgeDot: 'ui-badge--dot',
48
49
  chip: 'ui-chip',
@@ -58,6 +59,7 @@ export const cls = Object.freeze({
58
59
  dotSuccess: 'ui-dot--success',
59
60
  dotWarning: 'ui-dot--warning',
60
61
  dotDanger: 'ui-dot--danger',
62
+ dotInfo: 'ui-dot--info',
61
63
  dotLive: 'ui-dot--live',
62
64
  dotgrid: 'ui-dotgrid',
63
65
  dotgridAccent: 'ui-dotgrid--accent',
@@ -89,6 +91,9 @@ export const cls = Object.freeze({
89
91
  hintError: 'ui-hint--error',
90
92
  inputGroup: 'ui-input-group',
91
93
  inputGroupAddon: 'ui-input-group__addon',
94
+ inputIcon: 'ui-input-icon',
95
+ inputIconSlot: 'ui-input-icon__icon',
96
+ inputIconEnd: 'ui-input-icon--end',
92
97
  file: 'ui-file',
93
98
  range: 'ui-range',
94
99
  errorSummary: 'ui-error-summary',
@@ -103,6 +108,7 @@ export const cls = Object.freeze({
103
108
  alertSuccess: 'ui-alert--success',
104
109
  alertWarning: 'ui-alert--warning',
105
110
  alertDanger: 'ui-alert--danger',
111
+ alertInfo: 'ui-alert--info',
106
112
  toastStack: 'ui-toast-stack',
107
113
  toastStackAssertive: 'ui-toast-stack--assertive',
108
114
  toast: 'ui-toast',
@@ -112,12 +118,22 @@ export const cls = Object.freeze({
112
118
  toastSuccess: 'ui-toast--success',
113
119
  toastWarning: 'ui-toast--warning',
114
120
  toastDanger: 'ui-toast--danger',
121
+ toastInfo: 'ui-toast--info',
115
122
  tooltip: 'ui-tooltip',
116
123
  tooltipBubble: 'ui-tooltip__bubble',
117
124
  popover: 'ui-popover',
118
125
  progress: 'ui-progress',
119
126
  progressBar: 'ui-progress__bar',
120
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',
121
137
  // overlay
122
138
  modal: 'ui-modal',
123
139
  modalHead: 'ui-modal__head',
@@ -136,6 +152,8 @@ export const cls = Object.freeze({
136
152
  comboboxList: 'ui-combobox__list',
137
153
  comboboxOption: 'ui-combobox__option',
138
154
  comboboxEmpty: 'ui-combobox__empty',
155
+ lightbox: 'ui-lightbox',
156
+ lightboxClose: 'ui-lightbox__close',
139
157
  // disclosure
140
158
  tabs: 'ui-tabs',
141
159
  tabsList: 'ui-tabs__list',
@@ -155,6 +173,15 @@ export const cls = Object.freeze({
155
173
  avatarSm: 'ui-avatar--sm',
156
174
  avatarLg: 'ui-avatar--lg',
157
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',
158
185
  // table
159
186
  table: 'ui-table',
160
187
  tableDense: 'ui-table--dense',
@@ -199,6 +226,9 @@ export const cls = Object.freeze({
199
226
  siteheaderSticky: 'ui-siteheader--sticky',
200
227
  siteheaderBrand: 'ui-siteheader__brand',
201
228
  siteheaderActions: 'ui-siteheader__actions',
229
+ pagehead: 'ui-pagehead',
230
+ pageheadTitle: 'ui-pagehead__title',
231
+ pageheadActions: 'ui-pagehead__actions',
202
232
  sitenav: 'ui-sitenav',
203
233
  sitemenu: 'ui-sitemenu',
204
234
  sitemenuPanel: 'ui-sitemenu__panel',
@@ -209,6 +239,10 @@ export const cls = Object.freeze({
209
239
  tagAccent: 'ui-tag--accent',
210
240
  meta: 'ui-meta',
211
241
  metaItem: 'ui-meta__item',
242
+ timeline: 'ui-timeline',
243
+ timelineItem: 'ui-timeline__item',
244
+ timelineTime: 'ui-timeline__time',
245
+ kbd: 'ui-kbd',
212
246
  display: 'ui-display',
213
247
  mono: 'ui-mono',
214
248
  muted: 'ui-muted',
@@ -287,6 +321,7 @@ export const ui = {
287
321
  tone === 'success' && cls.badgeSuccess,
288
322
  tone === 'warning' && cls.badgeWarning,
289
323
  tone === 'danger' && cls.badgeDanger,
324
+ tone === 'info' && cls.badgeInfo,
290
325
  tone === 'muted' && cls.badgeMuted,
291
326
  dot && cls.badgeDot,
292
327
  ),
@@ -306,6 +341,7 @@ export const ui = {
306
341
  tone === 'success' && cls.dotSuccess,
307
342
  tone === 'warning' && cls.dotWarning,
308
343
  tone === 'danger' && cls.dotDanger,
344
+ tone === 'info' && cls.dotInfo,
309
345
  live && cls.dotLive,
310
346
  ),
311
347
  dotgrid: ({ accent, dense } = {}) =>
@@ -328,6 +364,7 @@ export const ui = {
328
364
  tone === 'success' && cls.alertSuccess,
329
365
  tone === 'warning' && cls.alertWarning,
330
366
  tone === 'danger' && cls.alertDanger,
367
+ tone === 'info' && cls.alertInfo,
331
368
  ),
332
369
  toast: ({ tone } = {}) =>
333
370
  j(
@@ -336,8 +373,17 @@ export const ui = {
336
373
  tone === 'success' && cls.toastSuccess,
337
374
  tone === 'warning' && cls.toastWarning,
338
375
  tone === 'danger' && cls.toastDanger,
376
+ tone === 'info' && cls.toastInfo,
339
377
  ),
340
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
+ ),
341
387
  dotspinner: ({ size } = {}) =>
342
388
  j(cls.dotspinner, size === 'sm' && cls.dotspinnerSm, size === 'lg' && cls.dotspinnerLg),
343
389
  dotbar: ({ indeterminate } = {}) => j(cls.dotbar, indeterminate && cls.dotbarIndeterminate),
@@ -349,6 +395,7 @@ export const ui = {
349
395
  container: ({ narrow, wide } = {}) =>
350
396
  j(cls.container, narrow && cls.containerNarrow, wide && cls.containerWide),
351
397
  tag: ({ accent } = {}) => j(cls.tag, accent && cls.tagAccent),
398
+ inputIcon: ({ end } = {}) => j(cls.inputIcon, end && cls.inputIconEnd),
352
399
  };
353
400
 
354
401
  export default ui;
@@ -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: `'Doto', var(--mono)`"
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`"
@@ -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/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: 1px;
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 {
@@ -326,3 +338,114 @@
326
338
  animation: none;
327
339
  }
328
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
+ }