barua-ui 0.13.0 → 0.14.0

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/css/base.css CHANGED
@@ -15,6 +15,9 @@
15
15
  html {
16
16
  -webkit-text-size-adjust: 100%;
17
17
  tab-size: 4;
18
+ /* Every rem in the system answers to this: data-scale="110" on <html>
19
+ makes the whole interface a tenth larger, type and spacing together. */
20
+ font-size: calc(100% * var(--b-scale, 1));
18
21
  }
19
22
 
20
23
  body {
@@ -64,7 +67,9 @@
64
67
  color: var(--b-color-accent-text);
65
68
  text-decoration: none;
66
69
  }
67
- a:hover { text-decoration: underline; text-underline-offset: 0.2em; }
70
+ @media (hover: hover) {
71
+ a:hover { text-decoration: underline; text-underline-offset: 0.2em; }
72
+ }
68
73
 
69
74
  code, kbd, samp, pre { font-family: var(--b-font-mono); font-size: 0.875em; }
70
75
  code {
@@ -12,7 +12,7 @@
12
12
  align-items: center;
13
13
  justify-content: center;
14
14
  gap: var(--b-space-2);
15
- height: var(--b-control-h-md);
15
+ height: var(--b-field-h);
16
16
  /* The border is real and transparent, the padding pays for it, and the
17
17
  rendered box does not move by a pixel. Windows High Contrast strips
18
18
  backgrounds and shadows; a button that was only a fill became bare
@@ -34,7 +34,9 @@
34
34
  box-shadow var(--b-duration-fast) var(--b-ease-standard),
35
35
  scale var(--b-duration-instant) var(--b-ease-standard);
36
36
  }
37
- .b-btn:hover { background: var(--b-fill); text-decoration: none; }
37
+ @media (hover: hover) {
38
+ .b-btn:hover { background: var(--b-fill); text-decoration: none; }
39
+ }
38
40
  .b-btn:active, .b-btn.is-pressed { scale: 0.97; }
39
41
  .b-btn svg { width: 1.1em; height: 1.1em; flex: none; }
40
42
 
@@ -44,15 +46,19 @@
44
46
  background: var(--b-color-accent-fill);
45
47
  box-shadow: var(--b-shadow-xs);
46
48
  }
47
- .b-btn--primary:hover { background: var(--b-color-accent-fill-hover); }
49
+ @media (hover: hover) {
50
+ .b-btn--primary:hover { background: var(--b-color-accent-fill-hover); }
51
+ }
48
52
  .b-btn--primary.is-active { background: var(--b-color-accent-fill-active); }
49
53
 
50
54
  .b-btn--tinted {
51
55
  color: var(--b-color-accent-text);
52
56
  background: var(--b-color-accent-soft);
53
57
  }
54
- .b-btn--tinted:hover {
55
- background: color-mix(in srgb, var(--b-color-accent) 22%, transparent);
58
+ @media (hover: hover) {
59
+ .b-btn--tinted:hover {
60
+ background: color-mix(in srgb, var(--b-color-accent) 22%, transparent);
61
+ }
56
62
  }
57
63
 
58
64
  /* Liquid glass primary — glossy accent pane with a light-catch */
@@ -65,10 +71,12 @@
65
71
  inset 0 0 0 1px color-mix(in srgb, var(--b-color-accent) 55%, transparent),
66
72
  var(--b-shadow-accent);
67
73
  }
68
- .b-btn--liquid:hover {
69
- background:
70
- radial-gradient(120% 140% at 30% 12%, rgba(255, 255, 255, 0.45), transparent 44%),
71
- var(--b-color-accent-hover);
74
+ @media (hover: hover) {
75
+ .b-btn--liquid:hover {
76
+ background:
77
+ radial-gradient(120% 140% at 30% 12%, rgba(255, 255, 255, 0.45), transparent 44%),
78
+ var(--b-color-accent-hover);
79
+ }
72
80
  }
73
81
  .b-btn--liquid.is-loading::after {
74
82
  border-color: rgba(255, 255, 255, 0.35);
@@ -79,10 +87,14 @@
79
87
  background: transparent;
80
88
  border-color: var(--b-border-strong);
81
89
  }
82
- .b-btn--outline:hover { background: var(--b-fill-quaternary); }
90
+ @media (hover: hover) {
91
+ .b-btn--outline:hover { background: var(--b-fill-quaternary); }
92
+ }
83
93
 
84
94
  .b-btn--ghost { background: transparent; }
85
- .b-btn--ghost:hover { background: var(--b-fill-tertiary); }
95
+ @media (hover: hover) {
96
+ .b-btn--ghost:hover { background: var(--b-fill-tertiary); }
97
+ }
86
98
 
87
99
  .b-btn--glass {
88
100
  position: relative;
@@ -104,9 +116,11 @@
104
116
  box-shadow: var(--b-glass-edge);
105
117
  pointer-events: none;
106
118
  }
107
- .b-btn--glass:hover {
108
- background: var(--b-material-regular-bg);
109
- box-shadow: var(--b-glass-lensing), var(--b-glass-ambient);
119
+ @media (hover: hover) {
120
+ .b-btn--glass:hover {
121
+ background: var(--b-material-regular-bg);
122
+ box-shadow: var(--b-glass-lensing), var(--b-glass-ambient);
123
+ }
110
124
  }
111
125
  .b-btn--glass:active {
112
126
  scale: 0.97;
@@ -147,8 +161,10 @@
147
161
  box-shadow: var(--b-glass-edge);
148
162
  pointer-events: none;
149
163
  }
150
- .b-btn--glass-clear:hover {
151
- background: light-dark(rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.10));
164
+ @media (hover: hover) {
165
+ .b-btn--glass-clear:hover {
166
+ background: light-dark(rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.10));
167
+ }
152
168
  }
153
169
  .b-btn--glass-clear:active {
154
170
  scale: 0.97;
@@ -186,8 +202,10 @@
186
202
  box-shadow: var(--b-glass-edge);
187
203
  pointer-events: none;
188
204
  }
189
- .b-btn--glass-prominent:hover {
190
- background: light-dark(rgba(10, 122, 255, 0.18), rgba(10, 132, 255, 0.26));
205
+ @media (hover: hover) {
206
+ .b-btn--glass-prominent:hover {
207
+ background: light-dark(rgba(10, 122, 255, 0.18), rgba(10, 132, 255, 0.26));
208
+ }
191
209
  }
192
210
  .b-btn--glass-prominent:active {
193
211
  scale: 0.97;
@@ -209,11 +227,17 @@
209
227
  }
210
228
 
211
229
  .b-btn--danger { color: var(--b-color-on-accent); background: var(--b-color-danger-fill); }
212
- .b-btn--danger:hover { background: color-mix(in srgb, var(--b-color-danger-fill) 85%, black); }
230
+ @media (hover: hover) {
231
+ .b-btn--danger:hover { background: color-mix(in srgb, var(--b-color-danger-fill) 85%, black); }
232
+ }
213
233
  .b-btn--danger-tinted { color: var(--b-color-danger-text); background: var(--b-color-danger-soft); }
214
- .b-btn--danger-tinted:hover { background: color-mix(in srgb, var(--b-color-red) 22%, transparent); }
234
+ @media (hover: hover) {
235
+ .b-btn--danger-tinted:hover { background: color-mix(in srgb, var(--b-color-red) 22%, transparent); }
236
+ }
215
237
  .b-btn--success { color: var(--b-color-on-accent); background: var(--b-color-success-fill); }
216
- .b-btn--success:hover { background: color-mix(in srgb, var(--b-color-success) 85%, black); }
238
+ @media (hover: hover) {
239
+ .b-btn--success:hover { background: color-mix(in srgb, var(--b-color-success) 85%, black); }
240
+ }
217
241
 
218
242
  /* Sizes */
219
243
  .b-btn--xs { height: var(--b-control-h-xs); padding-inline: calc(var(--b-space-2) - 1px); font-size: var(--b-text-caption); border-radius: var(--b-radius-sm); }
@@ -255,7 +279,7 @@
255
279
  /* ---- Floating Action Button ------------------------------------------- */
256
280
  .b-fab {
257
281
  position: fixed;
258
- inset-block-end: max(var(--b-space-6), env(safe-area-inset-bottom));
282
+ inset-block-end: calc(var(--b-bottomnav-h, 0px) + max(var(--b-space-6), env(safe-area-inset-bottom)));
259
283
  inset-inline-end: var(--b-space-6);
260
284
  z-index: var(--b-z-sticky);
261
285
  width: 3.5rem; height: 3.5rem;
@@ -265,7 +289,9 @@
265
289
  background: var(--b-color-accent-fill);
266
290
  box-shadow: var(--b-shadow-lg), var(--b-shadow-accent);
267
291
  }
268
- .b-fab:hover { background: var(--b-color-accent-hover); translate: 0 -2px; }
292
+ @media (hover: hover) {
293
+ .b-fab:hover { background: var(--b-color-accent-hover); translate: 0 -2px; }
294
+ }
269
295
  .b-fab--extended { width: auto; padding-inline: var(--b-space-5); gap: var(--b-space-2); }
270
296
 
271
297
  /* ---- Button Group ------------------------------------------------------ */
@@ -306,8 +332,10 @@
306
332
  color: var(--b-color-accent-text);
307
333
  background: var(--b-color-accent-soft);
308
334
  }
309
- .b-toggle-btn[aria-pressed="true"]:hover {
310
- background: color-mix(in srgb, var(--b-color-accent) 22%, transparent);
335
+ @media (hover: hover) {
336
+ .b-toggle-btn[aria-pressed="true"]:hover {
337
+ background: color-mix(in srgb, var(--b-color-accent) 22%, transparent);
338
+ }
311
339
  }
312
340
 
313
341
  /* ---- Link & Link Group ------------------------------------------------- */
@@ -322,9 +350,13 @@
322
350
  border: none;
323
351
  padding: 0;
324
352
  }
325
- .b-link:hover { text-decoration: underline; text-underline-offset: 0.2em; }
353
+ @media (hover: hover) {
354
+ .b-link:hover { text-decoration: underline; text-underline-offset: 0.2em; }
355
+ }
326
356
  .b-link--muted { color: var(--b-text-secondary); }
327
- .b-link--muted:hover { color: var(--b-text); }
357
+ @media (hover: hover) {
358
+ .b-link--muted:hover { color: var(--b-text); }
359
+ }
328
360
  .b-link--danger { color: var(--b-color-danger-text); }
329
361
  .b-link svg { width: 1em; height: 1em; }
330
362
  .b-link-group {
@@ -72,7 +72,9 @@
72
72
  gap: var(--b-space-2);
73
73
  font-weight: var(--b-weight-medium);
74
74
  }
75
- .b-btn--provider:hover { background: var(--b-fill-quaternary); }
75
+ @media (hover: hover) {
76
+ .b-btn--provider:hover { background: var(--b-fill-quaternary); }
77
+ }
76
78
 
77
79
  /* ---- OTP Input ----------------------------------------------------------------- */
78
80
  .b-otp {
@@ -147,7 +149,9 @@
147
149
  cursor: pointer;
148
150
  text-align: start;
149
151
  }
150
- .b-account:hover { background: var(--b-fill-tertiary); }
152
+ @media (hover: hover) {
153
+ .b-account:hover { background: var(--b-fill-tertiary); }
154
+ }
151
155
  .b-account.is-active { background: var(--b-color-accent-soft); }
152
156
  .b-account__meta { flex: 1; min-width: 0; }
153
157
  .b-account__name { font-size: var(--b-text-footnote); font-weight: var(--b-weight-semibold); }
@@ -166,7 +170,9 @@
166
170
  cursor: pointer;
167
171
  transition: background var(--b-duration-fast), border-color var(--b-duration-fast);
168
172
  }
169
- .b-profile-trigger:hover { background: var(--b-fill-tertiary); border-color: var(--b-separator); }
173
+ @media (hover: hover) {
174
+ .b-profile-trigger:hover { background: var(--b-fill-tertiary); border-color: var(--b-separator); }
175
+ }
170
176
 
171
177
  /* ---- System / error page scaffold ----------------------------------------------------- */
172
178
  .b-syspage {
@@ -93,7 +93,9 @@
93
93
  transition: height var(--b-duration-slow) var(--b-ease-out),
94
94
  opacity var(--b-duration-fast);
95
95
  }
96
- .b-columns__bar:hover { opacity: 0.85; }
96
+ @media (hover: hover) {
97
+ .b-columns__bar:hover { opacity: 0.85; }
98
+ }
97
99
  .b-columns__bar::after {
98
100
  content: attr(data-label);
99
101
  position: absolute;
@@ -232,7 +234,9 @@
232
234
  calc(var(--v, 0) * 100%), var(--b-fill-quaternary));
233
235
  transition: scale var(--b-duration-fast) var(--b-ease-standard);
234
236
  }
235
- .b-heatmap > i:hover { scale: 1.2; }
237
+ @media (hover: hover) {
238
+ .b-heatmap > i:hover { scale: 1.2; }
239
+ }
236
240
 
237
241
  /* ---- Sparkline & SVG chart styling ------------------------------------------ */
238
242
  .b-sparkline { width: 100%; height: 2rem; overflow: visible; }
@@ -269,7 +273,9 @@
269
273
  stroke-width: 1.5;
270
274
  transition: r var(--b-duration-fast);
271
275
  }
272
- .b-dot:hover { r: 6; }
276
+ @media (hover: hover) {
277
+ .b-dot:hover { r: 6; }
278
+ }
273
279
  .b-radar-svg polygon.b-series {
274
280
  fill: color-mix(in srgb, var(--b-chart-color, var(--b-chart-1)) 22%, transparent);
275
281
  stroke: var(--b-chart-color, var(--b-chart-1));
@@ -217,9 +217,11 @@
217
217
  transition: translate var(--b-duration-normal) var(--b-ease-out),
218
218
  box-shadow var(--b-duration-normal) var(--b-ease-out);
219
219
  }
220
- .b-card--interactive:hover {
221
- translate: 0 -2px;
222
- box-shadow: var(--b-elevation-3);
220
+ @media (hover: hover) {
221
+ .b-card--interactive:hover {
222
+ translate: 0 -2px;
223
+ box-shadow: var(--b-elevation-3);
224
+ }
223
225
  }
224
226
  .b-card--accent { border-top: 3px solid var(--b-color-accent); }
225
227
 
@@ -238,7 +240,7 @@
238
240
  display: flex;
239
241
  align-items: center;
240
242
  gap: var(--b-space-3);
241
- padding: var(--b-space-3) var(--b-space-4);
243
+ padding: var(--b-row-py) var(--b-row-px);
242
244
  font-size: var(--b-text-subheadline);
243
245
  /* A row that does something is a <button>, and a <button> arrives wearing
244
246
  the browser's own face: a grey plate, a border and centred text. Left
@@ -268,7 +270,9 @@
268
270
  border-top: 1px solid var(--b-separator);
269
271
  }
270
272
  .b-list-item--interactive { cursor: pointer; transition: background var(--b-duration-fast); }
271
- .b-list-item--interactive:hover { background: var(--b-fill-quaternary); }
273
+ @media (hover: hover) {
274
+ .b-list-item--interactive:hover { background: var(--b-fill-quaternary); }
275
+ }
272
276
  .b-list-item.is-selected { background: var(--b-color-accent-soft); }
273
277
  .b-list-item__leading { flex: none; display: flex; color: var(--b-text-secondary); }
274
278
  .b-list-item__leading svg { width: 1.25rem; height: 1.25rem; }
@@ -358,6 +362,42 @@
358
362
  .b-dl--stacked { grid-template-columns: 1fr; gap: var(--b-space-1); }
359
363
  .b-dl--stacked dd { margin-block-end: var(--b-space-3); }
360
364
 
365
+ /* Press feedback. Hover fills are gated to devices that can hover, so a
366
+ finger gets this instead: a deeper fill for exactly as long as it is
367
+ down, no transition, and gone the moment it lifts. Never sticky. */
368
+ :is(.b-list-item--interactive, a.b-list-item, button.b-list-item, .b-card--interactive):active {
369
+ background: var(--b-fill-secondary);
370
+ transition: none;
371
+ }
372
+
373
+ /* ---- Long lists ----------------------------------------------------------
374
+ Three strategies, and a rule for which is which. A console paginates: a
375
+ person is looking for one row and wants to say "page 4". A feed loads
376
+ more: a person is reading down and wants it to keep going. Never both on
377
+ one surface. Under either, --virtual lets a 5,000-row list lay out only
378
+ what is on screen: content-visibility skips the rows nobody can see and
379
+ --b-row-h tells the browser how tall they would have been, so the
380
+ scrollbar is honest. CSS only, no script, no windowing library. */
381
+ .b-list--virtual > .b-list-item,
382
+ .b-list--virtual > li,
383
+ .b-table--virtual tbody tr {
384
+ content-visibility: auto;
385
+ contain-intrinsic-block-size: auto var(--b-row-h, 3.5rem);
386
+ }
387
+ /* The sentinel at the end of a feed: a button, or with data-b-load-more an
388
+ observer that fires when it scrolls into view, then a spinner, then, when
389
+ there is nothing left, the end. */
390
+ .b-load-more {
391
+ display: grid;
392
+ place-items: center;
393
+ padding: var(--b-space-4);
394
+ min-block-size: var(--b-control-h-lg);
395
+ }
396
+ .b-load-more__busy, .b-load-more__end { display: none; }
397
+ .b-load-more.is-loading > .b-btn, .b-load-more.is-end > .b-btn { display: none; }
398
+ .b-load-more.is-loading .b-load-more__busy { display: block; }
399
+ .b-load-more.is-end .b-load-more__end { display: block; }
400
+
361
401
  /* ---- Table / Data Grid ------------------------------------------------------- */
362
402
  .b-table-wrap {
363
403
  overflow-x: auto;
@@ -415,7 +455,7 @@
415
455
  }
416
456
  }
417
457
  .b-table th {
418
- padding: var(--b-space-3) var(--b-space-4);
458
+ padding: var(--b-cell-py) var(--b-cell-px);
419
459
  text-align: start;
420
460
  font-weight: var(--b-weight-semibold);
421
461
  color: var(--b-text-secondary);
@@ -424,12 +464,14 @@
424
464
  background: var(--b-fill-quaternary);
425
465
  }
426
466
  .b-table td {
427
- padding: var(--b-space-3) var(--b-space-4);
467
+ padding: var(--b-cell-py) var(--b-cell-px);
428
468
  border-bottom: 1px solid var(--b-separator);
429
469
  }
430
470
  .b-table tbody tr:last-child td { border-bottom: none; }
431
471
  .b-table tbody tr { transition: background var(--b-duration-fast); }
432
- .b-table--hover tbody tr:hover { background: var(--b-fill-quaternary); }
472
+ @media (hover: hover) {
473
+ .b-table--hover tbody tr:hover { background: var(--b-fill-quaternary); }
474
+ }
433
475
  .b-table--striped tbody tr:nth-child(even) { background: var(--b-fill-quaternary); }
434
476
  .b-table tr.is-selected td { background: var(--b-color-accent-soft); }
435
477
  .b-table--compact :is(th, td) { padding: var(--b-space-2) var(--b-space-3); }
@@ -461,7 +503,10 @@
461
503
  opacity: 0;
462
504
  transition: opacity var(--b-duration-fast) var(--b-ease-out);
463
505
  }
464
- .b-datagrid .b-table tr:is(:hover, :focus-within) .b-row-actions { opacity: 1; }
506
+ .b-datagrid .b-table tr:focus-within .b-row-actions { opacity: 1; }
507
+ @media (hover: hover) {
508
+ .b-datagrid .b-table tr:hover .b-row-actions { opacity: 1; }
509
+ }
465
510
  /* No hover on touch, and an opacity-0 button is still tappable, so an
466
511
  invisible Delete would be a trap. */
467
512
  @media (hover: none) {
@@ -492,7 +537,9 @@
492
537
  }
493
538
  .b-tree-item[open] > summary::before { rotate: 90deg; }
494
539
  .b-tree-item__row { padding-inline-start: calc(var(--b-space-2) + 1em + var(--b-space-2)); }
495
- .b-tree :is(summary, .b-tree-item__row):hover { background: var(--b-fill-tertiary); }
540
+ @media (hover: hover) {
541
+ .b-tree :is(summary, .b-tree-item__row):hover { background: var(--b-fill-tertiary); }
542
+ }
496
543
  .b-tree .is-selected { background: var(--b-color-accent-soft); color: var(--b-color-accent-text); }
497
544
  .b-tree svg { width: 1rem; height: 1rem; color: var(--b-text-secondary); }
498
545
 
@@ -581,7 +628,9 @@
581
628
  transition: background var(--b-duration-fast);
582
629
  }
583
630
  .b-disclosure > summary::-webkit-details-marker { display: none; }
584
- .b-disclosure > summary:hover { background: var(--b-fill-quaternary); }
631
+ @media (hover: hover) {
632
+ .b-disclosure > summary:hover { background: var(--b-fill-quaternary); }
633
+ }
585
634
  .b-disclosure > summary::after {
586
635
  content: "⌄";
587
636
  color: var(--b-text-tertiary);
@@ -675,7 +724,9 @@
675
724
  cursor: pointer;
676
725
  transition: background var(--b-duration-fast), color var(--b-duration-fast);
677
726
  }
678
- .b-chip:hover { background: var(--b-fill-secondary); }
727
+ @media (hover: hover) {
728
+ .b-chip:hover { background: var(--b-fill-secondary); }
729
+ }
679
730
  .b-chip.is-selected, .b-chip[aria-pressed="true"] {
680
731
  color: var(--b-color-on-accent);
681
732
  background: var(--b-color-accent-fill);
@@ -692,7 +743,9 @@
692
743
  border-radius: 50%;
693
744
  cursor: pointer;
694
745
  }
695
- .b-chip__remove:hover { background: color-mix(in srgb, currentColor 26%, transparent); }
746
+ @media (hover: hover) {
747
+ .b-chip__remove:hover { background: color-mix(in srgb, currentColor 26%, transparent); }
748
+ }
696
749
 
697
750
  /* ---- Avatar / Avatar Group ----------------------------------------------------------------- */
698
751
  .b-avatar {
@@ -768,12 +821,14 @@
768
821
  .b-icon-tile--sm { width: 1.85rem; height: 1.85rem; border-radius: var(--b-radius-sm); }
769
822
  .b-icon-tile svg { width: 1.25rem; height: 1.25rem; }
770
823
  .b-icon-tile--sm svg { width: 1.05rem; height: 1.05rem; }
771
- .b-icon-tile--solid { color: #fff; background: var(--b-color-accent); }
772
- .b-icon-tile--green { color: #fff; background: var(--b-color-green); }
773
- .b-icon-tile--orange { color: #fff; background: var(--b-color-orange); }
774
- .b-icon-tile--red { color: #fff; background: var(--b-color-red); }
775
- .b-icon-tile--purple { color: #fff; background: var(--b-color-purple); }
776
- .b-icon-tile--teal { color: #fff; background: var(--b-color-teal); }
824
+ /* A glyph on a solid tile: the fill tokens, so white holds 3:1 on every
825
+ one of them (white on the raw green was 2.22, on the raw teal 2.57). */
826
+ .b-icon-tile--solid { color: var(--b-color-on-accent); background: var(--b-color-accent-fill); }
827
+ .b-icon-tile--green { color: var(--b-color-on-success); background: var(--b-color-success-fill); }
828
+ .b-icon-tile--orange { color: var(--b-color-on-warning); background: var(--b-color-warning-fill); }
829
+ .b-icon-tile--red { color: var(--b-color-on-danger); background: var(--b-color-danger-fill); }
830
+ .b-icon-tile--purple { color: var(--b-color-on-accent); background: color-mix(in srgb, var(--b-color-purple) 85%, black); }
831
+ .b-icon-tile--teal { color: var(--b-color-on-accent); background: color-mix(in srgb, var(--b-color-teal) 62%, black); }
777
832
  .b-icon-tile--indigo { color: #fff; background: var(--b-color-indigo); }
778
833
  .b-icon-tile--gray { color: #fff; background: var(--b-gray-1); }
779
834
 
@@ -826,7 +881,9 @@
826
881
  border-radius: var(--b-radius-sm);
827
882
  cursor: pointer;
828
883
  }
829
- .b-code__copy:hover { background: rgba(255, 255, 255, 0.16); color: #fff; }
884
+ @media (hover: hover) {
885
+ .b-code__copy:hover { background: rgba(255, 255, 255, 0.16); color: #fff; }
886
+ }
830
887
 
831
888
  /* ---- Quote -------------------------------------------------------------------------------------- */
832
889
  .b-quote {
@@ -143,7 +143,9 @@
143
143
  cursor: pointer;
144
144
  transition: background var(--b-duration-fast);
145
145
  }
146
- .b-cc__transport button:hover { background: var(--b-cc-fill); }
146
+ @media (hover: hover) {
147
+ .b-cc__transport button:hover { background: var(--b-cc-fill); }
148
+ }
147
149
  .b-cc__transport svg { width: 1.2rem; height: 1.2rem; }
148
150
 
149
151
  /* ---- Chunky slider (Display / Sound) ---------------------------------------
@@ -267,7 +269,9 @@
267
269
  cursor: pointer;
268
270
  transition: scale var(--b-duration-fast) var(--b-ease-bounce);
269
271
  }
270
- .b-lens:hover { scale: 1.05; }
272
+ @media (hover: hover) {
273
+ .b-lens:hover { scale: 1.05; }
274
+ }
271
275
  .b-lens:active { scale: 0.96; }
272
276
  .b-lens svg { width: 40%; height: 40%; }
273
277
 
@@ -33,7 +33,9 @@
33
33
  padding: 0.1rem;
34
34
  border-radius: var(--b-radius-xs);
35
35
  }
36
- .b-alert__close:hover { color: var(--b-text); }
36
+ @media (hover: hover) {
37
+ .b-alert__close:hover { color: var(--b-text); }
38
+ }
37
39
 
38
40
  /* No opinion. The default alert is info-tinted, which turns a plain
39
41
  "still working on it" strip blue and gives it a meaning it does not
@@ -96,6 +98,16 @@
96
98
  gap: var(--b-space-2);
97
99
  pointer-events: none;
98
100
  }
101
+ /* On a phone a toast spans the width and sits above the tab bar, not on
102
+ top of the Home tab, and it never fights the home indicator. */
103
+ @media (max-width: 639px) {
104
+ .b-toast-region {
105
+ inset-inline: var(--b-space-3);
106
+ align-items: stretch;
107
+ inset-block-end: calc(var(--b-bottomnav-h, 0px) + max(var(--b-space-3), env(safe-area-inset-bottom)));
108
+ }
109
+ .b-toast { min-width: 0; max-width: none; }
110
+ }
99
111
  .b-toast-region--center { inset-inline: 0; align-items: center; }
100
112
  .b-toast {
101
113
  display: flex;
@@ -201,7 +213,9 @@
201
213
  cursor: pointer;
202
214
  padding: 0.1rem;
203
215
  }
204
- .b-tip__close:hover { color: var(--b-text); }
216
+ @media (hover: hover) {
217
+ .b-tip__close:hover { color: var(--b-text); }
218
+ }
205
219
 
206
220
  /* ---- Status Indicator --------------------------------------------------------- */
207
221
  .b-status {
@@ -41,9 +41,12 @@
41
41
  /* ---- Inputs ------------------------------------------------------------ */
42
42
  .b-input, .b-textarea, .b-select {
43
43
  width: 100%;
44
- height: var(--b-control-h-md);
44
+ height: var(--b-field-h);
45
45
  padding-inline: var(--b-space-3);
46
- font-size: var(--b-text-subheadline);
46
+ font-size: var(--b-text-input);
47
+ /* A focused field scrolls clear of the sticky bar above and any action
48
+ bar below, so the keyboard never covers what somebody is typing. */
49
+ scroll-margin-block: var(--b-topbar-h) calc(var(--b-action-bar-h, 0px) + var(--b-space-4));
47
50
  color: var(--b-text);
48
51
  background: var(--b-surface);
49
52
  border: 1px solid var(--b-border);
@@ -53,13 +56,17 @@
53
56
  background var(--b-duration-fast) var(--b-ease-standard);
54
57
  }
55
58
  .b-input::placeholder, .b-textarea::placeholder { color: var(--b-text-tertiary); }
56
- .b-input:hover, .b-textarea:hover, .b-select:hover { border-color: var(--b-border-strong); }
59
+ @media (hover: hover) {
60
+ .b-input:hover, .b-textarea:hover, .b-select:hover { border-color: var(--b-border-strong); }
61
+ }
57
62
  .b-input:focus-visible, .b-textarea:focus-visible, .b-select:focus-visible {
58
63
  border-color: var(--b-color-accent);
59
64
  }
60
65
  .b-input--filled { background: var(--b-fill-tertiary); border-color: transparent; }
61
- .b-input--filled:hover { background: var(--b-fill-secondary); }
62
- .b-input--sm { height: var(--b-control-h-sm); font-size: var(--b-text-footnote); border-radius: var(--b-radius-sm); }
66
+ @media (hover: hover) {
67
+ .b-input--filled:hover { background: var(--b-fill-secondary); }
68
+ }
69
+ .b-input--sm { height: var(--b-control-h-sm); font-size: max(var(--b-text-footnote), var(--b-text-input)); border-radius: var(--b-radius-sm); }
63
70
  .b-input--lg { height: var(--b-control-h-lg); font-size: var(--b-text-callout); border-radius: var(--b-radius-lg); }
64
71
 
65
72
  .b-textarea {
@@ -105,6 +112,30 @@
105
112
  .b-input-group:has(.b-input-affix--start) > .b-input { padding-inline-start: var(--b-space-8); }
106
113
  .b-input-group:has(.b-input-affix--end) > .b-input { padding-inline-end: var(--b-space-8); }
107
114
 
115
+ /* ---- Money field --------------------------------------------------------
116
+ An amount is not a number field: type=number drops the thousands
117
+ separators and grows spinners, and a proportional digit column never
118
+ lines up. The money field is an input group whose start affix is the ISO
119
+ code (TZS, not a symbol, since TSh is ambiguous), the input is text with
120
+ inputmode=decimal so a phone shows the right keyboard, the digits are
121
+ tabular and end-aligned so a column of prices reads as a column, and an
122
+ optional __minor greys the cents. --b-money-code-w keeps the affix
123
+ column aligned across a form; barua.js groups thousands on blur where
124
+ the field carries data-b-money. */
125
+ .b-money-field > .b-input {
126
+ text-align: end;
127
+ font-variant-numeric: tabular-nums;
128
+ padding-inline-start: calc(var(--b-money-code-w, 3.25rem) + var(--b-space-3));
129
+ }
130
+ .b-money-field .b-money-field__code {
131
+ inline-size: var(--b-money-code-w, 3.25rem);
132
+ font-family: var(--b-font-mono);
133
+ font-size: var(--b-text-footnote);
134
+ font-weight: var(--b-weight-medium);
135
+ color: var(--b-text-secondary);
136
+ }
137
+ .b-money-field__minor { color: var(--b-text-tertiary); }
138
+
108
139
  /* Search field — pill + icon */
109
140
  .b-search .b-input { border-radius: var(--b-radius-full); background: var(--b-fill-tertiary); border-color: transparent; }
110
141
  .b-search .b-input:focus-visible { background: var(--b-surface); border-color: var(--b-color-accent); }
@@ -160,7 +191,9 @@
160
191
  border-radius: var(--b-radius-sm);
161
192
  cursor: pointer;
162
193
  }
163
- .b-combobox__option:hover { background: var(--b-fill-quaternary); }
194
+ @media (hover: hover) {
195
+ .b-combobox__option:hover { background: var(--b-fill-quaternary); }
196
+ }
164
197
  .b-combobox__option.is-active { background: var(--b-fill-tertiary); }
165
198
  .b-combobox__option[aria-selected="true"] {
166
199
  color: var(--b-color-accent-text);
@@ -383,7 +416,9 @@
383
416
  cursor: pointer;
384
417
  transition: background var(--b-duration-fast) var(--b-ease-standard);
385
418
  }
386
- .b-stepper-input button:hover { background: var(--b-fill-secondary); color: var(--b-text); }
419
+ @media (hover: hover) {
420
+ .b-stepper-input button:hover { background: var(--b-fill-secondary); color: var(--b-text); }
421
+ }
387
422
  .b-stepper-input button:first-child { border-inline-end: 1px solid var(--b-separator); }
388
423
  .b-stepper-input button:last-child { border-inline-start: 1px solid var(--b-separator); }
389
424
 
@@ -405,11 +440,18 @@
405
440
  transition: border-color var(--b-duration-fast) var(--b-ease-standard),
406
441
  background var(--b-duration-fast) var(--b-ease-standard);
407
442
  }
408
- .b-upload:hover, .b-upload.is-dragover {
443
+ .b-upload.is-dragover {
409
444
  border-color: var(--b-color-accent);
410
445
  background: var(--b-color-accent-soft);
411
446
  color: var(--b-color-accent-text);
412
447
  }
448
+ @media (hover: hover) {
449
+ .b-upload:hover {
450
+ border-color: var(--b-color-accent);
451
+ background: var(--b-color-accent-soft);
452
+ color: var(--b-color-accent-text);
453
+ }
454
+ }
413
455
  /* Keyboard focus on the wrapped input lights the zone, like a multiselect. */
414
456
  .b-upload:focus-within {
415
457
  border-color: var(--b-color-accent);
@@ -453,6 +495,26 @@
453
495
  .b-upload-item__meta { flex: 1; min-width: 0; }
454
496
  .b-upload-item__name { font-size: var(--b-text-footnote); font-weight: var(--b-weight-medium); }
455
497
  .b-upload-item__size { font-size: var(--b-text-caption); color: var(--b-text-tertiary); }
498
+ /* The states an upload actually has. A component whose whole job is
499
+ progress had one look: the file, its name, its size. Now it is
500
+ uploading, complete or failed, with a bar, a tick, a cancel, and a
501
+ reason and a Retry when it failed. */
502
+ .b-upload-item__progress { margin-block: var(--b-space-1); }
503
+ .b-upload-item:not(.is-uploading) .b-upload-item__progress { display: none; }
504
+ .b-upload-item__status { display: inline-flex; flex: none; color: var(--b-text-tertiary); }
505
+ .b-upload-item.is-uploading .b-upload-item__status { color: var(--b-color-accent-text); }
506
+ .b-upload-item.is-complete .b-upload-item__status { color: var(--b-color-success-text); }
507
+ .b-upload-item.is-error { border-color: var(--b-color-danger); }
508
+ .b-upload-item.is-error .b-upload-item__status { color: var(--b-color-danger-text); }
509
+ .b-upload-item__error { font-size: var(--b-text-caption); color: var(--b-color-danger-text); }
510
+ .b-upload-item__cancel { flex: none; }
511
+ .b-upload-queue__summary { margin-block-start: var(--b-space-2); }
512
+ /* A drop the zone will not take: the wrong type, too big, too many. */
513
+ .b-upload.is-rejected {
514
+ border-color: var(--b-color-danger);
515
+ background: var(--b-color-danger-soft);
516
+ color: var(--b-color-danger-text);
517
+ }
456
518
 
457
519
  /* ---- Color Picker ------------------------------------------------------- */
458
520
  .b-color-picker {
@@ -487,7 +549,9 @@
487
549
  cursor: pointer;
488
550
  transition: scale var(--b-duration-fast) var(--b-ease-bounce);
489
551
  }
490
- .b-swatch:hover { scale: 1.12; }
552
+ @media (hover: hover) {
553
+ .b-swatch:hover { scale: 1.12; }
554
+ }
491
555
  .b-swatch.is-selected {
492
556
  box-shadow: 0 0 0 2px var(--b-bg), 0 0 0 4px var(--b-color-accent);
493
557
  }