barua-ui 0.12.1 → 0.13.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.
@@ -34,6 +34,10 @@
34
34
  display: flex;
35
35
  align-items: center;
36
36
  gap: var(--b-space-2);
37
+ /* A brand that yields width drops its mark to nothing and wraps its name
38
+ onto two lines. It is the one thing in the bar that never shrinks. */
39
+ flex: none;
40
+ white-space: nowrap;
37
41
  font-weight: var(--b-weight-bold);
38
42
  font-size: var(--b-text-callout);
39
43
  color: var(--b-text);
@@ -43,8 +47,14 @@
43
47
  display: flex;
44
48
  align-items: center;
45
49
  gap: var(--b-space-1);
50
+ /* The rail is what gives way when the bar is short of room; without this
51
+ it held its content width and pushed the trailing actions off the
52
+ screen, where a notifications button nobody can reach is not chrome. */
53
+ min-width: 0;
46
54
  }
47
55
  .b-topnav__link {
56
+ flex: none;
57
+ white-space: nowrap;
48
58
  padding: var(--b-space-1) var(--b-space-3);
49
59
  font-size: var(--b-text-subheadline);
50
60
  font-weight: var(--b-weight-medium);
@@ -54,6 +64,35 @@
54
64
  }
55
65
  .b-topnav__link:hover { color: var(--b-text); background: var(--b-fill-tertiary); text-decoration: none; }
56
66
  .b-topnav__link.is-active { color: var(--b-color-accent-text); background: var(--b-color-accent-soft); }
67
+ /* Trailing controls keep their size. A button that shrinks below its touch
68
+ target is not a button anyone can press. */
69
+ .b-topnav > :is(.b-btn, .b-avatar, .b-dropdown, .b-badge, .b-input-group, .b-status) { flex: none; }
70
+ /* The phone way in: a menu button that only exists on a phone, for a bar
71
+ whose destinations live in a .b-sidebar or .b-drawer rather than on it. */
72
+ .b-topnav__menu { display: none; }
73
+ @media (max-width: 767px) {
74
+ .b-topnav {
75
+ gap: var(--b-space-2);
76
+ padding-inline: var(--b-space-3);
77
+ flex-wrap: wrap;
78
+ row-gap: 0;
79
+ height: auto;
80
+ min-height: var(--b-topbar-h);
81
+ }
82
+ .b-topnav__menu { display: inline-flex; }
83
+ /* Inline destinations drop to a full-bleed strip under the brand row that
84
+ scrolls sideways, the way .b-tabs does. Nothing leaves the screen. */
85
+ .b-topnav__links {
86
+ order: 1;
87
+ flex: 1 0 100%;
88
+ margin-inline: calc(var(--b-space-3) * -1);
89
+ padding-inline: var(--b-space-3);
90
+ padding-block: var(--b-space-1) var(--b-space-2);
91
+ overflow-x: auto;
92
+ scrollbar-width: none;
93
+ }
94
+ .b-topnav__links::-webkit-scrollbar { display: none; }
95
+ }
57
96
 
58
97
  /* ---- Sidebar ------------------------------------------------------------ */
59
98
  .b-sidebar {
@@ -204,7 +243,8 @@
204
243
  /* ---- Tab Bar (segmented, iOS-style) ------------------------------------- */
205
244
  .b-segmented {
206
245
  display: inline-flex;
207
- padding: 2px;
246
+ padding: 1px;
247
+ border: 1px solid transparent;
208
248
  gap: 2px;
209
249
  background: var(--b-fill-tertiary);
210
250
  border-radius: var(--b-radius-md);
@@ -218,12 +258,12 @@
218
258
  box-shadow: var(--b-glass-ambient);
219
259
  }
220
260
  .b-segmented__item {
221
- padding: calc(var(--b-space-1) + 1px) var(--b-space-4);
261
+ padding: var(--b-space-1) calc(var(--b-space-4) - 1px);
222
262
  font-size: var(--b-text-footnote);
223
263
  font-weight: var(--b-weight-medium);
224
264
  color: var(--b-text-secondary);
225
265
  background: none;
226
- border: none;
266
+ border: 1px solid transparent;
227
267
  border-radius: calc(var(--b-radius-md) - 2px);
228
268
  cursor: pointer;
229
269
  transition: background var(--b-duration-fast), color var(--b-duration-fast),
@@ -300,7 +340,7 @@
300
340
  color: var(--b-text-quaternary);
301
341
  padding-inline: var(--b-space-1);
302
342
  }
303
- .b-breadcrumbs a { color: var(--b-text-secondary); }
343
+ .b-breadcrumbs a:not(.b-btn) { color: var(--b-text-secondary); }
304
344
  .b-breadcrumbs a:hover { color: var(--b-text); text-decoration: none; }
305
345
  .b-breadcrumbs [aria-current="page"] { color: var(--b-text); font-weight: var(--b-weight-medium); }
306
346
 
@@ -332,7 +372,7 @@
332
372
  .b-pagination__item:hover { background: var(--b-fill-tertiary); color: var(--b-text); text-decoration: none; }
333
373
  .b-pagination__item.is-active, .b-pagination__item[aria-current="page"] {
334
374
  color: var(--b-color-on-accent);
335
- background: var(--b-color-accent);
375
+ background: var(--b-color-accent-fill);
336
376
  }
337
377
  .b-pagination__ellipsis { color: var(--b-text-quaternary); padding-inline: var(--b-space-1); }
338
378
 
@@ -381,7 +421,7 @@
381
421
  .b-steps li.is-complete::before {
382
422
  content: "✓";
383
423
  color: var(--b-color-on-accent);
384
- background: var(--b-color-accent);
424
+ background: var(--b-color-accent-fill);
385
425
  }
386
426
  .b-steps li.is-complete:not(:last-child)::after { background: var(--b-color-accent); }
387
427
  .b-steps li.is-active { color: var(--b-text); font-weight: var(--b-weight-semibold); }
@@ -413,7 +453,7 @@
413
453
  }
414
454
  .b-menubar__item:hover, .b-menubar__item.is-active,
415
455
  .b-menubar__item[aria-expanded="true"] {
416
- background: var(--b-color-accent);
456
+ background: var(--b-color-accent-fill);
417
457
  color: var(--b-color-on-accent);
418
458
  }
419
459
 
@@ -468,7 +508,7 @@
468
508
  white-space: nowrap;
469
509
  }
470
510
  .b-menu__item:hover, .b-menu__item.is-active {
471
- background: var(--b-color-accent);
511
+ background: var(--b-color-accent-fill);
472
512
  color: var(--b-color-on-accent);
473
513
  text-decoration: none;
474
514
  }
@@ -478,7 +518,7 @@
478
518
  background: none;
479
519
  cursor: not-allowed;
480
520
  }
481
- .b-menu__item--danger { color: var(--b-color-danger); }
521
+ .b-menu__item--danger { color: var(--b-color-danger-text); }
482
522
  .b-menu__item--danger:hover { background: var(--b-color-danger); color: #fff; }
483
523
  .b-menu__item svg { width: 1rem; height: 1rem; flex: none; opacity: 0.85; }
484
524
  .b-menu__shortcut {
@@ -504,7 +544,7 @@
504
544
  }
505
545
  .b-menu__sub:hover > .b-menu,
506
546
  .b-menu__sub:focus-within > .b-menu { display: block; }
507
- .b-menu__sub:hover > .b-menu__item { background: var(--b-color-accent); color: var(--b-color-on-accent); }
547
+ .b-menu__sub:hover > .b-menu__item { background: var(--b-color-accent-fill); color: var(--b-color-on-accent); }
508
548
 
509
549
  .b-menu__separator {
510
550
  height: 1px;
@@ -546,7 +586,13 @@
546
586
  }
547
587
 
548
588
  /* ---- Command Menu / Command Palette ---------------------------------------- */
549
- .b-cmdk {
589
+ /* .b-cmdk is composed onto the platform dialog (<dialog class="b-modal b-cmdk">).
590
+ .b-modal sits later in the import order at equal specificity, so without
591
+ the composed form the palette rendered as a 448px opaque modal instead of
592
+ the 600px heavy glass documented here. The surface wins by specificity;
593
+ .b-modal still supplies the open/close transition and ::backdrop. */
594
+ .b-cmdk,
595
+ .b-modal.b-cmdk {
550
596
  width: min(37.5rem, calc(100vw - 2rem));
551
597
  padding: 0;
552
598
  user-select: none;
@@ -577,7 +623,6 @@
577
623
  outline: none;
578
624
  user-select: text;
579
625
  }
580
- .b-cmdk__input:focus-visible { box-shadow: none; }
581
626
  .b-cmdk__list {
582
627
  max-height: min(20rem, 50vh);
583
628
  overflow: auto;
@@ -638,6 +683,10 @@
638
683
  max-height: none;
639
684
  height: 100dvh;
640
685
  overflow: hidden;
686
+ /* A column, so a long body scrolls inside the drawer instead of being
687
+ clipped by it: the header keeps its height and the body takes the rest. */
688
+ display: flex;
689
+ flex-direction: column;
641
690
  transition: translate var(--b-duration-slow) var(--b-ease-spring),
642
691
  display var(--b-duration-slow) allow-discrete,
643
692
  overlay var(--b-duration-slow) allow-discrete;
@@ -676,6 +725,7 @@
676
725
  backdrop-filter: blur(var(--b-blur-xs));
677
726
  }
678
727
  .b-drawer__header {
728
+ flex: none;
679
729
  display: flex;
680
730
  align-items: center;
681
731
  justify-content: space-between;
@@ -683,5 +733,5 @@
683
733
  border-bottom: 1px solid var(--b-separator);
684
734
  font-weight: var(--b-weight-semibold);
685
735
  }
686
- .b-drawer__body { padding: var(--b-space-3); overflow-y: auto; }
736
+ .b-drawer__body { padding: var(--b-space-3); flex: 1 1 auto; min-height: 0; overflow-y: auto; }
687
737
  }
@@ -98,7 +98,9 @@
98
98
  .b-modal__close:hover { background: var(--b-fill-secondary); color: var(--b-text); }
99
99
 
100
100
  /* ---- Alert Dialog (centered, compact) --------------------------------------- */
101
- .b-alert-dialog { width: min(18.5rem, calc(100vw - 2rem)); text-align: center; }
101
+ /* Composed onto .b-modal like .b-cmdk; explicit so a reorder cannot break it. */
102
+ .b-alert-dialog,
103
+ .b-modal.b-alert-dialog { width: min(18.5rem, calc(100vw - 2rem)); text-align: center; }
102
104
  .b-alert-dialog .b-modal__body {
103
105
  padding: var(--b-space-6) var(--b-space-5) var(--b-space-4);
104
106
  }
@@ -124,7 +126,7 @@
124
126
  }
125
127
  .b-alert-dialog__actions button:hover { background: var(--b-fill-quaternary); }
126
128
  .b-alert-dialog__actions button + button { border-top: 1px solid var(--b-separator); }
127
- .b-alert-dialog__actions .is-destructive { color: var(--b-color-danger); }
129
+ .b-alert-dialog__actions .is-destructive { color: var(--b-color-danger-text); }
128
130
  .b-alert-dialog__actions .is-primary { font-weight: var(--b-weight-semibold); }
129
131
  .b-alert-dialog__actions--row { flex-direction: row; }
130
132
  .b-alert-dialog__actions--row button { flex: 1; }
@@ -144,6 +146,12 @@
144
146
  max-height: none;
145
147
  height: 100dvh;
146
148
  padding: 0;
149
+ /* A column: the header keeps its height and the body is the one pane that
150
+ scrolls. With the body at height:100% under a clipped dialog, a long
151
+ sheet simply could not be scrolled to its end. */
152
+ display: flex;
153
+ flex-direction: column;
154
+ overflow: hidden;
147
155
  background: var(--b-elevated);
148
156
  border: none;
149
157
  border-inline-start: 1px solid var(--b-hairline);
@@ -176,13 +184,14 @@
176
184
  transition: opacity var(--b-duration-normal) var(--b-ease-out);
177
185
  }
178
186
  .b-sheet__header {
187
+ flex: none;
179
188
  display: flex;
180
189
  align-items: center;
181
190
  justify-content: space-between;
182
191
  padding: var(--b-space-4) var(--b-space-5);
183
192
  border-bottom: 1px solid var(--b-separator);
184
193
  }
185
- .b-sheet__body { padding: var(--b-space-5); overflow-y: auto; height: 100%; }
194
+ .b-sheet__body { padding: var(--b-space-5); flex: 1 1 auto; min-height: 0; overflow-y: auto; }
186
195
 
187
196
  /* ---- Bottom Sheet --------------------------------------------------------------- */
188
197
  .b-bottom-sheet {
@@ -194,6 +203,8 @@
194
203
  width: min(34rem, 100vw);
195
204
  max-height: 85dvh;
196
205
  padding: 0;
206
+ display: flex;
207
+ flex-direction: column;
197
208
  background: var(--b-material-thick-bg);
198
209
  -webkit-backdrop-filter: var(--b-glass-heavy);
199
210
  backdrop-filter: var(--b-glass-heavy);
@@ -223,6 +234,7 @@
223
234
  }
224
235
  .b-bottom-sheet::backdrop { background: var(--b-scrim); }
225
236
  .b-bottom-sheet__grabber {
237
+ flex: none;
226
238
  width: 2.25rem;
227
239
  height: 0.3rem;
228
240
  margin: var(--b-space-2) auto 0;
@@ -232,6 +244,8 @@
232
244
  .b-bottom-sheet__body {
233
245
  padding: var(--b-space-4) var(--b-space-5);
234
246
  padding-bottom: max(var(--b-space-5), env(safe-area-inset-bottom));
247
+ flex: 1 1 auto;
248
+ min-height: 0;
235
249
  overflow-y: auto;
236
250
  }
237
251
 
@@ -292,7 +306,7 @@
292
306
  }
293
307
  .b-action-sheet__group button:first-child { border-top: none; }
294
308
  .b-action-sheet__group button:hover { background: var(--b-fill-quaternary); }
295
- .b-action-sheet__group button.is-destructive { color: var(--b-color-danger); }
309
+ .b-action-sheet__group button.is-destructive { color: var(--b-color-danger-text); }
296
310
  .b-action-sheet__cancel {
297
311
  margin-block-start: var(--b-space-2);
298
312
  font-weight: var(--b-weight-semibold);
@@ -569,6 +583,8 @@
569
583
  max-height: none;
570
584
  margin: 0;
571
585
  padding: 0;
586
+ display: flex;
587
+ flex-direction: column;
572
588
  background: var(--b-bg);
573
589
  border: none;
574
590
  transition: opacity var(--b-duration-normal) var(--b-ease-out),
@@ -581,11 +597,12 @@
581
597
  .b-fullscreen[open] { opacity: 0; }
582
598
  }
583
599
  .b-fullscreen__bar {
600
+ flex: none;
584
601
  display: flex;
585
602
  align-items: center;
586
603
  justify-content: space-between;
587
604
  padding: var(--b-space-3) var(--b-space-5);
588
605
  border-bottom: 1px solid var(--b-separator);
589
606
  }
590
- .b-fullscreen__body { padding: var(--b-space-6); overflow: auto; height: calc(100dvh - var(--b-topbar-h)); }
607
+ .b-fullscreen__body { padding: var(--b-space-6); flex: 1 1 auto; min-height: 0; overflow: auto; }
591
608
  }
@@ -25,21 +25,56 @@
25
25
  .b-workspace__main {
26
26
  grid-area: main;
27
27
  overflow: auto;
28
- padding: var(--b-space-6);
29
- }
28
+ padding-block: var(--b-space-6);
29
+ /* The pane's margin, and the only one: 16px on a phone, 24px on a desktop.
30
+ A container inside it does not repeat it, the same rule as
31
+ .b-chapter > .b-container: at 390 the shell, the container and a tip each
32
+ took a gutter and the tip's words were left 200px. */
33
+ padding-inline: clamp(var(--b-space-4), 3vw, var(--b-space-6));
34
+ }
35
+ .b-workspace__main > .b-container { padding-inline: 0; }
30
36
  .b-workspace > .b-inspector { grid-area: inspector; }
31
37
  .b-workspace > .b-statusbar { grid-area: statusbar; }
38
+ /* On a phone the shell is one column: chrome above, the work surface below.
39
+ Side panes leave the grid and a .b-topnav__menu button opens the sidebar
40
+ as a .b-drawer instead. Without this every page had to remember
41
+ .b-hide-mobile itself, and the ones that forgot left main a 150px slit
42
+ beside a full-width sidebar. The screen still ends at the screen: the
43
+ shell keeps 100dvh and main stays the one scroller. */
44
+ @media (max-width: 767px) {
45
+ .b-workspace {
46
+ grid-template-columns: minmax(0, 1fr);
47
+ grid-template-areas:
48
+ "topbar"
49
+ "main"
50
+ "statusbar";
51
+ }
52
+ .b-workspace > .b-sidebar,
53
+ .b-workspace > .b-rail,
54
+ .b-workspace > .b-inspector { display: none; }
55
+ .b-workspace__main { padding-block: var(--b-space-4); }
56
+ }
32
57
 
33
58
  /* Dashboard grid */
34
59
  .b-dashboard {
35
60
  display: grid;
36
61
  gap: var(--b-space-4);
37
62
  grid-template-columns: repeat(12, minmax(0, 1fr));
63
+ /* Spans answer to the grid's own width, not the viewport. Inside a
64
+ workspace the main pane is the viewport minus a rail, a sidebar and an
65
+ inspector, and a viewport query handed a 12-column layout to a 392px
66
+ pane at exactly the width the side panes were still docked. Same
67
+ sm/md ladder as .b-cq, so one set of breakpoints means one thing. */
68
+ container-type: inline-size;
38
69
  }
39
70
  .b-dashboard > * { grid-column: span 12; }
40
- @media (min-width: 768px) {
41
- /* The full scale: a twelve-column grid that only answers to five of the
42
- twelve is a trap — b-span-7 silently did nothing. */
71
+ /* sm: anything up to a half becomes a half; wider spans stay full. */
72
+ @container (min-width: 24rem) {
73
+ .b-dashboard > :is(.b-span-2, .b-span-3, .b-span-4, .b-span-5, .b-span-6) { grid-column: span 6; }
74
+ }
75
+ /* md: the full scale. A twelve-column grid that only answers to five of the
76
+ twelve is a trap; b-span-7 silently did nothing. */
77
+ @container (min-width: 40rem) {
43
78
  .b-dashboard > .b-span-2 { grid-column: span 2; }
44
79
  .b-dashboard > .b-span-3 { grid-column: span 3; }
45
80
  .b-dashboard > .b-span-4 { grid-column: span 4; }
@@ -153,12 +188,16 @@
153
188
  color: var(--b-text-tertiary);
154
189
  font-variant-numeric: tabular-nums;
155
190
  }
156
- .b-task-row__due.is-overdue { color: var(--b-color-danger); font-weight: var(--b-weight-medium); }
191
+ .b-task-row__due.is-overdue { color: var(--b-color-danger-text); font-weight: var(--b-weight-medium); }
157
192
 
158
193
  /* ---- Calendar View (month grid) -------------------------------------------------- */
159
194
  .b-calview {
160
195
  display: grid;
161
- grid-template-columns: repeat(7, 1fr);
196
+ /* A bare 1fr is minmax(auto, 1fr): a nowrap .b-event set the column's
197
+ minimum, the seven of them overflowed the clipped grid on a phone and
198
+ Friday to Sunday were simply gone. minmax(0, 1fr) lets a column be
199
+ narrower than its longest event, which then ellipsises as designed. */
200
+ grid-template-columns: repeat(7, minmax(0, 1fr));
162
201
  background: var(--b-surface);
163
202
  border: 1px solid var(--b-separator);
164
203
  border-radius: var(--b-radius-xl);
@@ -176,6 +215,7 @@
176
215
  border-bottom: 1px solid var(--b-separator);
177
216
  }
178
217
  .b-calview__cell {
218
+ min-width: 0;
179
219
  min-height: 6rem;
180
220
  padding: var(--b-space-1);
181
221
  border-bottom: 1px solid var(--b-separator);
@@ -192,12 +232,12 @@
192
232
  color: var(--b-text-secondary);
193
233
  }
194
234
  .b-calview__cell.is-today .b-calview__date {
195
- background: var(--b-color-accent);
235
+ background: var(--b-color-accent-fill);
196
236
  color: var(--b-color-on-accent);
197
237
  font-weight: var(--b-weight-semibold);
198
238
  }
199
239
  .b-calview__cell.is-other-month { background: var(--b-fill-quaternary); }
200
- .b-calview__cell.is-other-month .b-calview__date { color: var(--b-text-quaternary); }
240
+ .b-calview__cell.is-other-month .b-calview__date { color: var(--b-text-tertiary); }
201
241
  .b-event {
202
242
  display: block;
203
243
  margin-block-start: 2px;
@@ -213,9 +253,9 @@
213
253
  white-space: nowrap;
214
254
  cursor: pointer;
215
255
  }
216
- .b-event--green { color: var(--b-color-success); background: var(--b-color-success-soft); border-color: var(--b-color-success); }
217
- .b-event--orange { color: light-dark(#b46200, var(--b-color-warning)); background: var(--b-color-warning-soft); border-color: var(--b-color-warning); }
218
- .b-event--purple { color: var(--b-color-purple); background: var(--b-color-purple-soft); border-color: var(--b-color-purple); }
256
+ .b-event--green { color: var(--b-color-success-text); background: var(--b-color-success-soft); border-color: var(--b-color-success-text); }
257
+ .b-event--orange { color: var(--b-color-warning-text); background: var(--b-color-warning-soft); border-color: var(--b-color-warning); }
258
+ .b-event--purple { color: var(--b-color-purple-text); background: var(--b-color-purple-soft); border-color: var(--b-color-purple-text); }
219
259
 
220
260
  /* ---- Gantt View --------------------------------------------------------------------- */
221
261
  .b-gantt {
@@ -9,6 +9,9 @@
9
9
  @layer components {
10
10
  /* ---- Calendar ------------------------------------------------------------- */
11
11
  .b-calendar {
12
+ /* The cell, named once, so a coarse pointer can grow it to the touch
13
+ target and the selected day stays a circle. */
14
+ --b-calendar-cell: 2.25rem;
12
15
  display: inline-flex;
13
16
  flex-direction: column;
14
17
  gap: var(--b-space-2);
@@ -27,7 +30,7 @@
27
30
  .b-calendar__month { font-weight: var(--b-weight-semibold); font-size: var(--b-text-subheadline); }
28
31
  .b-calendar__grid {
29
32
  display: grid;
30
- grid-template-columns: repeat(7, 2.25rem);
33
+ grid-template-columns: repeat(7, var(--b-calendar-cell));
31
34
  gap: 2px;
32
35
  }
33
36
  .b-calendar__weekday {
@@ -42,7 +45,7 @@
42
45
  .b-calendar__day {
43
46
  display: grid;
44
47
  place-items: center;
45
- height: 2.25rem;
48
+ height: var(--b-calendar-cell);
46
49
  font-size: var(--b-text-footnote);
47
50
  font-variant-numeric: tabular-nums;
48
51
  color: var(--b-text);
@@ -53,14 +56,14 @@
53
56
  transition: background var(--b-duration-fast);
54
57
  }
55
58
  .b-calendar__day:hover { background: var(--b-fill-tertiary); }
56
- .b-calendar__day.is-muted { color: var(--b-text-quaternary); }
59
+ .b-calendar__day.is-muted { color: var(--b-text-tertiary); }
57
60
  .b-calendar__day.is-today {
58
61
  color: var(--b-color-accent-text);
59
62
  font-weight: var(--b-weight-bold);
60
63
  }
61
64
  .b-calendar__day.is-selected {
62
65
  color: var(--b-color-on-accent);
63
- background: var(--b-color-accent);
66
+ background: var(--b-color-accent-fill);
64
67
  font-weight: var(--b-weight-semibold);
65
68
  }
66
69
  .b-calendar__day.is-in-range { background: var(--b-color-accent-soft); border-radius: 0; }
@@ -270,7 +273,8 @@
270
273
  }
271
274
  .b-multiselect:focus-within {
272
275
  border-color: var(--b-color-accent);
273
- box-shadow: var(--b-focus-ring);
276
+ outline: var(--b-focus-ring);
277
+ outline-offset: var(--b-focus-ring-offset);
274
278
  }
275
279
  .b-multiselect input {
276
280
  flex: 1;
@@ -281,7 +285,6 @@
281
285
  font-size: var(--b-text-subheadline);
282
286
  padding: var(--b-space-1);
283
287
  }
284
- .b-multiselect input:focus-visible { box-shadow: none; }
285
288
  .b-multiselect .b-chip { height: 1.5rem; font-size: var(--b-text-caption); }
286
289
 
287
290
  /* ---- File Browser / Folder Tree ------------------------------------------------------- */
@@ -324,7 +327,7 @@
324
327
  .b-file-tile:hover { background: var(--b-fill-quaternary); }
325
328
  .b-file-tile.is-selected { background: var(--b-color-accent-soft); }
326
329
  .b-file-tile.is-selected .b-file-tile__name {
327
- background: var(--b-color-accent);
330
+ background: var(--b-color-accent-fill);
328
331
  color: var(--b-color-on-accent);
329
332
  border-radius: var(--b-radius-xs);
330
333
  padding-inline: 0.25rem;
@@ -363,5 +366,23 @@
363
366
  white-space: nowrap;
364
367
  }
365
368
  .b-log--audit .b-log__entry { grid-template-columns: 7.5rem max-content 1fr max-content; }
369
+ /* Four fixed columns are a desktop shape: at 390 they overflowed the page
370
+ by 11px and were clipped inside a card with nothing to scroll. On a phone
371
+ the entry becomes three rows: who and when, what, and where from. */
372
+ @media (max-width: 40rem) {
373
+ .b-log--audit .b-log__entry {
374
+ grid-template-columns: max-content 1fr;
375
+ grid-template-areas:
376
+ "actor time"
377
+ "action action"
378
+ "ip ip";
379
+ row-gap: var(--b-space-1);
380
+ align-items: baseline;
381
+ }
382
+ .b-log--audit .b-log__actor { grid-area: actor; }
383
+ .b-log--audit .b-log__time { grid-area: time; justify-self: end; }
384
+ .b-log--audit .b-log__action { grid-area: action; }
385
+ .b-log--audit .b-log__ip { grid-area: ip; }
386
+ }
366
387
  .b-log__ip { font-family: var(--b-font-mono); font-size: var(--b-text-caption); color: var(--b-text-tertiary); }
367
388
  }
package/css/tokens.css CHANGED
@@ -38,8 +38,16 @@
38
38
  --b-color-accent-hover: light-dark(#0066e0, #3395ff);
39
39
  --b-color-accent-active: light-dark(#0052b4, #66afff);
40
40
  --b-color-accent-soft: light-dark(rgba(10, 122, 255, 0.12), rgba(10, 132, 255, 0.18));
41
- --b-color-accent-text: light-dark(#0066e0, #6cb2ff);
41
+ --b-color-accent-text: light-dark(#0064dc, #6cb2ff); /* 4.63:1 on the darkest light surface */
42
42
  --b-color-on-accent: #ffffff;
43
+ /* Fills that carry --b-color-on-accent text. One step darker than the
44
+ accent so white holds 4.5:1 in both schemes (5.28 light, 4.76 dark;
45
+ the accent itself was 4.01 and 3.65). States go darker, never lighter:
46
+ a lighter fill under white ink loses contrast. The accent stays what it
47
+ was for strokes, rings, links and tinted text, where 3:1 is the bar. */
48
+ --b-color-accent-fill: light-dark(#0066e0, #0a6cf0);
49
+ --b-color-accent-fill-hover: light-dark(#0052b4, #0066e0);
50
+ --b-color-accent-fill-active: light-dark(#003d87, #0052b4);
43
51
 
44
52
  /* ------------------------------------------------------------------ *
45
53
  * Color — Apple system palette
@@ -57,6 +65,17 @@
57
65
  --b-color-pink: light-dark(#ff2d55, #ff375f);
58
66
  --b-color-brown: light-dark(#a2845e, #ac8e68);
59
67
 
68
+ /* Inks. The hues above are fills; text set in a hue needs a darker cousin
69
+ in light and a lighter one in dark, exactly as --b-color-accent-text
70
+ does for the blue. Green as text was 2.22:1, red 3.55:1, and the retints
71
+ handed the raw hue to every link. Each pair clears 4.5:1 on white, the
72
+ canvas, the elevated card and its own -soft fill. */
73
+ --b-color-green-text: light-dark(#1a7a38, #4cd964);
74
+ --b-color-teal-text: light-dark(#0a6f82, #5fd6ea);
75
+ --b-color-pink-text: light-dark(#d1173f, #ff6b85);
76
+ --b-color-indigo-text: light-dark(#4a48c4, #9d9bff);
77
+ --b-color-purple-text: light-dark(#8a2fb8, #d18cff);
78
+
60
79
  --b-color-red-soft: light-dark(rgba(255, 59, 48, 0.12), rgba(255, 69, 58, 0.18));
61
80
  --b-color-orange-soft: light-dark(rgba(255, 149, 0, 0.14), rgba(255, 159, 10, 0.18));
62
81
  --b-color-yellow-soft: light-dark(rgba(255, 204, 0, 0.18), rgba(255, 214, 10, 0.18));
@@ -64,6 +83,7 @@
64
83
  --b-color-indigo-soft: light-dark(rgba(88, 86, 214, 0.12), rgba(94, 92, 230, 0.18));
65
84
  --b-color-purple-soft: light-dark(rgba(175, 82, 222, 0.12), rgba(191, 90, 242, 0.18));
66
85
  --b-color-pink-soft: light-dark(rgba(255, 45, 85, 0.12), rgba(255, 55, 95, 0.18));
86
+ --b-color-teal-soft: light-dark(rgba(48, 176, 199, 0.14), rgba(64, 203, 224, 0.18));
67
87
 
68
88
  /* Semantic aliases */
69
89
  --b-color-success: var(--b-color-green);
@@ -74,6 +94,14 @@
74
94
  --b-color-warning-soft: var(--b-color-orange-soft);
75
95
  --b-color-danger-soft: var(--b-color-red-soft);
76
96
  --b-color-info-soft: var(--b-color-accent-soft);
97
+ --b-color-success-text: var(--b-color-green-text);
98
+ --b-color-warning-text: light-dark(#8a4b00, #ffb340);
99
+ --b-color-danger-text: light-dark(#c9241a, #ff6961);
100
+ /* Solid status fills that carry white text: the raw hues were 2.22 (green)
101
+ and 3.55 (red) under white. */
102
+ --b-color-success-fill: light-dark(#1a8438, #1f8839);
103
+ --b-color-danger-fill: light-dark(#d70015, #e0261c);
104
+ --b-color-warning-fill: light-dark(#995900, #a66100);
77
105
 
78
106
  /* ------------------------------------------------------------------ *
79
107
  * Color — grays (Apple systemGray 1–6)
@@ -100,8 +128,13 @@
100
128
  * Color — text (Apple label hierarchy)
101
129
  * ------------------------------------------------------------------ */
102
130
  --b-text: light-dark(rgba(10, 12, 20, 0.92), rgba(245, 246, 250, 0.96));
103
- --b-text-secondary: light-dark(rgba(20, 24, 38, 0.62), rgba(235, 235, 245, 0.64));
104
- --b-text-tertiary: light-dark(rgba(20, 24, 38, 0.40), rgba(235, 235, 245, 0.42));
131
+ /* The ladder is tuned so tertiary still clears 4.5:1 on the darkest
132
+ light surface (#ececf2) and the darkest dark one (#303038): at 0.40 it
133
+ rendered at 2.53:1 and was the ink of every placeholder, timestamp and
134
+ hint in the system. Quaternary is for glyphs and watermarks, never for
135
+ something a person has to read. */
136
+ --b-text-secondary: light-dark(rgba(20, 24, 38, 0.74), rgba(235, 235, 245, 0.76));
137
+ --b-text-tertiary: light-dark(rgba(20, 24, 38, 0.62), rgba(235, 235, 245, 0.58));
105
138
  --b-text-quaternary: light-dark(rgba(20, 24, 38, 0.22), rgba(235, 235, 245, 0.24));
106
139
 
107
140
  /* ------------------------------------------------------------------ *
@@ -317,7 +350,17 @@
317
350
  --b-control-h-lg: 2.75rem; /* 44 — default touch target */
318
351
  --b-control-h-xl: 3.25rem; /* 52 */
319
352
  --b-touch-target: 44px;
320
- --b-focus-ring: 0 0 0 3.5px color-mix(in srgb, var(--b-color-accent) 30%, transparent);
353
+ /* The focus ring is an outline, not a shadow. A shadow ring lost to every
354
+ component that owns box-shadow for its material, which is most of them,
355
+ so the primary button, the FAB and every button in liquid mode never
356
+ showed focus at all; an outline competes with nothing. It is opaque
357
+ (2px of the accent, 2px off the control) because the old 30% halo sat at
358
+ 1.46:1 against the page, below the 3:1 an indicator needs. Components
359
+ flush inside a clipping container pull it inward with the offset. */
360
+ --b-focus-ring-color: var(--b-color-accent);
361
+ --b-focus-ring-width: 2px;
362
+ --b-focus-ring-offset: 2px;
363
+ --b-focus-ring: var(--b-focus-ring-width) solid var(--b-focus-ring-color);
321
364
  --b-container-sm: 40rem;
322
365
  --b-container-md: 48rem;
323
366
  --b-container-lg: 64rem;
@@ -339,6 +382,11 @@
339
382
  /* Explicit theme overrides — win over system preference */
340
383
  [data-theme="light"] { color-scheme: light; }
341
384
  [data-theme="dark"] { color-scheme: dark; }
385
+ /* Re-anchor the ink inside a themed subtree. `color` arrives from the page
386
+ around it as an already-resolved colour, so redeclaring the tokens flipped
387
+ the surfaces but not the words on them: a light island in a dark page kept
388
+ white text on its new white cards. Same rule the wallpaper scopes carry. */
389
+ [data-theme], .b-chapter--dark { color: var(--b-text); }
342
390
 
343
391
  /* ---- Baked glass -------------------------------------------------------
344
392
  *
@@ -427,11 +475,15 @@
427
475
  }
428
476
 
429
477
  /* Accent re-tinting: <html data-accent="purple"> etc. */
430
- [data-accent="indigo"] { --b-color-accent: var(--b-color-indigo); --b-color-accent-hover: color-mix(in srgb, var(--b-color-indigo) 85%, black); --b-color-accent-soft: var(--b-color-indigo-soft); --b-color-accent-text: var(--b-color-indigo); }
431
- [data-accent="purple"] { --b-color-accent: var(--b-color-purple); --b-color-accent-hover: color-mix(in srgb, var(--b-color-purple) 85%, black); --b-color-accent-soft: var(--b-color-purple-soft); --b-color-accent-text: var(--b-color-purple); }
432
- [data-accent="pink"] { --b-color-accent: var(--b-color-pink); --b-color-accent-hover: color-mix(in srgb, var(--b-color-pink) 85%, black); --b-color-accent-soft: var(--b-color-pink-soft); --b-color-accent-text: var(--b-color-pink); }
433
- [data-accent="teal"] { --b-color-accent: var(--b-color-teal); --b-color-accent-hover: color-mix(in srgb, var(--b-color-teal) 85%, black); --b-color-accent-soft: rgba(48, 176, 199, 0.14); --b-color-accent-text: var(--b-color-teal); }
434
- [data-accent="green"] { --b-color-accent: var(--b-color-green); --b-color-accent-hover: color-mix(in srgb, var(--b-color-green) 85%, black); --b-color-accent-soft: var(--b-color-green-soft); --b-color-accent-text: var(--b-color-green); }
478
+ /* Each retint hands the ink jobs to the -text pair and the fill jobs to a
479
+ darker mix, so a green accent does not put 2.2:1 links and 2.2:1 buttons
480
+ on the page. Teal and green are too light to carry the focus ring; they
481
+ supply a darker ring colour. */
482
+ [data-accent="indigo"] { --b-color-accent: var(--b-color-indigo); --b-color-accent-hover: color-mix(in srgb, var(--b-color-indigo) 85%, black); --b-color-accent-soft: var(--b-color-indigo-soft); --b-color-accent-text: var(--b-color-indigo-text); --b-color-accent-fill: var(--b-color-indigo); --b-color-accent-fill-hover: color-mix(in srgb, var(--b-color-indigo) 85%, black); --b-color-accent-fill-active: color-mix(in srgb, var(--b-color-indigo) 75%, black); }
483
+ [data-accent="purple"] { --b-color-accent: var(--b-color-purple); --b-color-accent-hover: color-mix(in srgb, var(--b-color-purple) 85%, black); --b-color-accent-soft: var(--b-color-purple-soft); --b-color-accent-text: var(--b-color-purple-text); --b-color-accent-fill: color-mix(in srgb, var(--b-color-purple) 85%, black); --b-color-accent-fill-hover: color-mix(in srgb, var(--b-color-purple) 75%, black); --b-color-accent-fill-active: color-mix(in srgb, var(--b-color-purple) 65%, black); }
484
+ [data-accent="pink"] { --b-color-accent: var(--b-color-pink); --b-color-accent-hover: color-mix(in srgb, var(--b-color-pink) 85%, black); --b-color-accent-soft: var(--b-color-pink-soft); --b-color-accent-text: var(--b-color-pink-text); --b-color-accent-fill: color-mix(in srgb, var(--b-color-pink) 85%, black); --b-color-accent-fill-hover: color-mix(in srgb, var(--b-color-pink) 75%, black); --b-color-accent-fill-active: color-mix(in srgb, var(--b-color-pink) 65%, black); }
485
+ [data-accent="teal"] { --b-color-accent: var(--b-color-teal); --b-color-accent-hover: color-mix(in srgb, var(--b-color-teal) 85%, black); --b-color-accent-soft: var(--b-color-teal-soft); --b-color-accent-text: var(--b-color-teal-text); --b-color-accent-fill: color-mix(in srgb, var(--b-color-teal) 60%, black); --b-color-accent-fill-hover: color-mix(in srgb, var(--b-color-teal) 50%, black); --b-color-accent-fill-active: color-mix(in srgb, var(--b-color-teal) 40%, black); --b-focus-ring-color: var(--b-color-accent-hover); }
486
+ [data-accent="green"] { --b-color-accent: var(--b-color-green); --b-color-accent-hover: color-mix(in srgb, var(--b-color-green) 85%, black); --b-color-accent-soft: var(--b-color-green-soft); --b-color-accent-text: var(--b-color-green-text); --b-color-accent-fill: color-mix(in srgb, var(--b-color-green) 65%, black); --b-color-accent-fill-hover: color-mix(in srgb, var(--b-color-green) 55%, black); --b-color-accent-fill-active: color-mix(in srgb, var(--b-color-green) 45%, black); --b-focus-ring-color: light-dark(color-mix(in srgb, var(--b-color-green) 70%, black), var(--b-color-green)); }
435
487
 
436
488
  /* ---- Glass keyframes (Liquid Glass) ----------------------------------- */
437
489
  /* Shimmer — light flash sweeping across on press/interaction */
package/css/utilities.css CHANGED
@@ -442,9 +442,9 @@
442
442
  .b-text-secondary { color: var(--b-text-secondary); }
443
443
  .b-text-tertiary { color: var(--b-text-tertiary); }
444
444
  .b-text-accent { color: var(--b-color-accent-text); }
445
- .b-text-success { color: var(--b-color-success); }
446
- .b-text-warning { color: var(--b-color-warning); }
447
- .b-text-danger { color: var(--b-color-danger); }
445
+ .b-text-success { color: var(--b-color-success-text); }
446
+ .b-text-warning { color: var(--b-color-warning-text); }
447
+ .b-text-danger { color: var(--b-color-danger-text); }
448
448
  .b-text-start { text-align: start; }
449
449
  .b-text-center { text-align: center; }
450
450
  .b-text-end { text-align: end; }
@@ -628,6 +628,19 @@
628
628
  width: max(100%, var(--b-touch-target));
629
629
  height: max(100%, var(--b-touch-target));
630
630
  }
631
+ /* ---- Coarse pointer: the primitives meet the touch target ----------------
632
+ A segmented item at 30px, a chip at 30px, a menu row at 39px are fine
633
+ under a cursor and misses under a thumb. On a coarse pointer they grow to
634
+ the target without changing shape; a 44px calendar cell keeps its circle
635
+ and the dropdown that holds it may not push off a 390 screen. */
636
+ @media (pointer: coarse) {
637
+ :is(.b-segmented__item, .b-chip, .b-menu__item, .b-sidebar__item,
638
+ .b-topnav__link, .b-pagination__item) {
639
+ min-block-size: var(--b-touch-target);
640
+ }
641
+ .b-calendar { --b-calendar-cell: var(--b-touch-target); }
642
+ .b-dropdown > .b-menu { max-inline-size: calc(100vw - 2 * var(--b-space-4)); }
643
+ }
631
644
 
632
645
  /* ---- Responsive visibility --------------------------------------------- */
633
646
  @media (max-width: 767px) {