@mdbase-dev/ui 0.1.0-beta.117

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.
@@ -0,0 +1,611 @@
1
+ /*
2
+ * Shared controls. Text fields stay native and take Editor's field treatment;
3
+ * Select, menus and the app switcher are the components from ./select,
4
+ * ./popover and ./app-switcher, drawn in Editor's quiet treatment: fine
5
+ * outlines, 2px corners, no shadows. Apps set density with --control-height
6
+ * and --control-font-size; the radius stays the family's.
7
+ */
8
+ :root {
9
+ --control-height: 34px;
10
+ --control-font-size: 12px;
11
+ --control-radius: 2px;
12
+ }
13
+
14
+ .mdbase-visually-hidden {
15
+ position: absolute;
16
+ width: 1px;
17
+ height: 1px;
18
+ overflow: hidden;
19
+ clip: rect(0 0 0 0);
20
+ white-space: nowrap;
21
+ }
22
+
23
+ /* Fields: native text inputs and textareas. */
24
+ .mdbase-field,
25
+ .mdbase-select {
26
+ min-width: 0;
27
+ max-width: 100%;
28
+ min-height: var(--control-height);
29
+ border: 1px solid var(--color-border-strong);
30
+ border-radius: var(--control-radius);
31
+ outline: 0;
32
+ color: var(--color-text);
33
+ background: transparent;
34
+ font: inherit;
35
+ font-size: var(--control-font-size);
36
+ transition: border-color 150ms ease, background 150ms ease;
37
+ }
38
+
39
+ .mdbase-field {
40
+ width: 100%;
41
+ padding: 0 9px;
42
+ }
43
+
44
+ textarea.mdbase-field {
45
+ padding: 7px 9px;
46
+ line-height: 1.5;
47
+ resize: vertical;
48
+ }
49
+
50
+ .mdbase-field::placeholder {
51
+ color: var(--color-placeholder);
52
+ }
53
+
54
+ .mdbase-field:hover:not(:disabled),
55
+ .mdbase-select:hover:not(:disabled) {
56
+ border-color: var(--color-text-soft);
57
+ }
58
+
59
+ .mdbase-field:focus-visible,
60
+ .mdbase-select:focus-visible {
61
+ outline: 2px solid var(--color-accent);
62
+ outline-offset: 1px;
63
+ }
64
+
65
+ .mdbase-field:disabled,
66
+ .mdbase-select:disabled {
67
+ cursor: default;
68
+ opacity: 0.52;
69
+ }
70
+
71
+ .mdbase-field[aria-invalid="true"],
72
+ .mdbase-select[aria-invalid="true"] {
73
+ border-color: var(--color-danger);
74
+ box-shadow: 0 0 0 1px var(--color-danger-soft);
75
+ }
76
+
77
+ /* Select: the trigger reads as a field; its list opens in the top layer. */
78
+ .mdbase-select {
79
+ display: inline-flex;
80
+ align-items: center;
81
+ gap: 6px;
82
+ padding: 0 9px;
83
+ line-height: 1.2;
84
+ text-align: left;
85
+ cursor: pointer;
86
+ }
87
+
88
+ .mdbase-select-value {
89
+ min-width: 0;
90
+ flex: 1 1 auto;
91
+ overflow: hidden;
92
+ text-overflow: ellipsis;
93
+ white-space: nowrap;
94
+ }
95
+
96
+ .mdbase-select-value.is-empty {
97
+ color: var(--color-text-muted);
98
+ }
99
+
100
+ .mdbase-select-chevron {
101
+ width: 12px;
102
+ height: 12px;
103
+ flex: 0 0 auto;
104
+ fill: none;
105
+ stroke: var(--color-text-muted);
106
+ stroke-width: 2;
107
+ stroke-linecap: round;
108
+ stroke-linejoin: round;
109
+ transition: transform 150ms ease;
110
+ }
111
+
112
+ .mdbase-select[aria-expanded="true"] .mdbase-select-chevron {
113
+ transform: rotate(180deg);
114
+ }
115
+
116
+ /* Quiet: reads as text until hovered, focused or open, for controls inside rows and headings. */
117
+ .mdbase-select.is-quiet {
118
+ border-color: transparent;
119
+ background: transparent;
120
+ }
121
+
122
+ .mdbase-select.is-quiet:not(:hover, :focus-visible, [aria-expanded="true"]) .mdbase-select-chevron {
123
+ opacity: 0;
124
+ }
125
+
126
+ .mdbase-select.is-quiet:hover:not(:disabled),
127
+ .mdbase-select.is-quiet[aria-expanded="true"] {
128
+ border-color: var(--color-border);
129
+ background: var(--color-surface);
130
+ }
131
+
132
+ /* Compact: a small uppercase label, for kinds and modes beside a value. */
133
+ .mdbase-select.is-compact {
134
+ min-height: 20px;
135
+ padding: 0;
136
+ border: 0;
137
+ background: transparent;
138
+ color: var(--color-text-muted);
139
+ font-family: var(--mono);
140
+ font-size: 11px;
141
+ text-transform: uppercase;
142
+ }
143
+
144
+ .mdbase-select.is-compact .mdbase-select-chevron {
145
+ width: 9px;
146
+ height: 9px;
147
+ }
148
+
149
+ .mdbase-select-list {
150
+ position: fixed;
151
+ inset: auto;
152
+ max-width: min(420px, calc(100vw - 16px));
153
+ margin: 0;
154
+ overflow: auto;
155
+ padding: 4px;
156
+ border: 1px solid var(--color-border-strong);
157
+ border-radius: var(--control-radius);
158
+ outline: 0;
159
+ color: var(--color-text-soft);
160
+ background: var(--color-surface);
161
+ font-size: var(--control-font-size);
162
+ overscroll-behavior: contain;
163
+ }
164
+
165
+ .mdbase-select-group {
166
+ padding: 8px 10px 3px;
167
+ color: var(--color-text-muted);
168
+ font-size: 11px;
169
+ font-weight: 700;
170
+ }
171
+
172
+ .mdbase-select-option {
173
+ position: relative;
174
+ min-height: 30px;
175
+ display: flex;
176
+ align-items: center;
177
+ padding: 0 30px 0 9px;
178
+ border-radius: var(--control-radius);
179
+ overflow: hidden;
180
+ text-overflow: ellipsis;
181
+ white-space: nowrap;
182
+ cursor: pointer;
183
+ }
184
+
185
+ .mdbase-select-option.is-active {
186
+ color: var(--color-text);
187
+ background: var(--color-surface-subtle);
188
+ }
189
+
190
+ .mdbase-select-list:focus-visible .mdbase-select-option.is-active {
191
+ box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--color-accent) 45%, transparent);
192
+ }
193
+
194
+ .mdbase-select-option[aria-selected="true"] {
195
+ color: var(--color-text);
196
+ background: var(--color-selected);
197
+ }
198
+
199
+ .mdbase-select-option[aria-selected="true"]::after {
200
+ position: absolute;
201
+ right: 10px;
202
+ width: 9px;
203
+ height: 5px;
204
+ margin-top: -3px;
205
+ border-bottom: 2px solid var(--color-accent-strong);
206
+ border-left: 2px solid var(--color-accent-strong);
207
+ content: "";
208
+ transform: rotate(-45deg);
209
+ }
210
+
211
+ .mdbase-select-option[aria-disabled="true"] {
212
+ color: var(--color-text-faint);
213
+ cursor: default;
214
+ }
215
+
216
+ /* Menus anchored to a trigger (useMenuPopover). */
217
+ .mdbase-menu {
218
+ position: fixed;
219
+ inset: auto;
220
+ box-sizing: border-box;
221
+ display: flex;
222
+ flex-direction: column;
223
+ margin: 0;
224
+ overflow: auto;
225
+ padding: 5px;
226
+ border: 1px solid var(--color-border-strong);
227
+ border-radius: var(--control-radius);
228
+ outline: 0;
229
+ color: var(--color-text-soft);
230
+ background: var(--color-surface);
231
+ font-size: var(--control-font-size);
232
+ overscroll-behavior: contain;
233
+ }
234
+
235
+ .mdbase-menu-heading {
236
+ padding: 6px 8px 4px;
237
+ color: var(--color-text-muted);
238
+ font-size: 11px;
239
+ font-weight: 700;
240
+ }
241
+
242
+ .mdbase-menu-list {
243
+ display: grid;
244
+ gap: 1px;
245
+ }
246
+
247
+ .mdbase-menu-item {
248
+ min-width: 0;
249
+ display: flex;
250
+ align-items: center;
251
+ gap: 10px;
252
+ padding: 6px 8px;
253
+ border: 0;
254
+ border-radius: var(--control-radius);
255
+ color: var(--color-text-soft);
256
+ background: transparent;
257
+ font: inherit;
258
+ text-align: start;
259
+ text-decoration: none;
260
+ cursor: pointer;
261
+ }
262
+
263
+ .mdbase-menu-item:hover:not(:disabled, [aria-disabled="true"]),
264
+ .mdbase-menu .mdbase-menu-item:focus-visible {
265
+ color: var(--color-text);
266
+ background: var(--color-surface-subtle);
267
+ }
268
+
269
+ /* One focus mark inside the row, instead of an outline around it. */
270
+ .mdbase-menu .mdbase-menu-item:focus-visible {
271
+ outline: 0;
272
+ box-shadow: inset 0 0 0 2px color-mix(in oklch, var(--color-accent) 70%, transparent);
273
+ }
274
+
275
+ .mdbase-menu-item:disabled {
276
+ cursor: default;
277
+ opacity: 0.6;
278
+ }
279
+
280
+ .mdbase-menu-item.is-current {
281
+ cursor: default;
282
+ background: var(--color-selected);
283
+ }
284
+
285
+ .mdbase-menu-copy {
286
+ min-width: 0;
287
+ flex: 1 1 auto;
288
+ display: grid;
289
+ gap: 1px;
290
+ }
291
+
292
+ .mdbase-menu-copy strong,
293
+ .mdbase-menu-copy small {
294
+ overflow: hidden;
295
+ text-overflow: ellipsis;
296
+ white-space: nowrap;
297
+ }
298
+
299
+ .mdbase-menu-copy strong {
300
+ color: var(--color-text);
301
+ font-weight: 600;
302
+ }
303
+
304
+ .mdbase-menu-copy small {
305
+ color: var(--color-text-muted);
306
+ font-size: 11px;
307
+ }
308
+
309
+ .mdbase-menu-note {
310
+ margin: 6px 2px 0;
311
+ padding: 8px 8px 2px;
312
+ border-top: 1px solid var(--color-border);
313
+ color: var(--color-text-muted);
314
+ font-size: 11px;
315
+ }
316
+
317
+ /* The app switcher: the wordmark opens the app menu. */
318
+ .mdbase-app-switcher {
319
+ min-width: 0;
320
+ display: inline-flex;
321
+ align-items: center;
322
+ gap: 5px;
323
+ margin-left: -6px;
324
+ padding: 4px 6px;
325
+ border: 1px solid transparent;
326
+ border-radius: var(--control-radius);
327
+ color: inherit;
328
+ background: transparent;
329
+ font: inherit;
330
+ cursor: pointer;
331
+ transition: background 150ms ease, border-color 150ms ease;
332
+ }
333
+
334
+ .mdbase-app-switcher:hover,
335
+ .mdbase-app-switcher[aria-expanded="true"] {
336
+ border-color: var(--color-border);
337
+ background: var(--color-surface);
338
+ }
339
+
340
+ .mdbase-app-switcher-chevron {
341
+ width: 13px;
342
+ height: 13px;
343
+ flex: 0 0 auto;
344
+ fill: none;
345
+ stroke: var(--color-text-muted);
346
+ stroke-width: 1.6;
347
+ transition: transform 150ms ease;
348
+ }
349
+
350
+ .mdbase-app-switcher[aria-expanded="true"] .mdbase-app-switcher-chevron {
351
+ transform: rotate(180deg);
352
+ }
353
+
354
+ .mdbase-app-menu-mark {
355
+ width: 22px;
356
+ height: 22px;
357
+ flex: 0 0 auto;
358
+ margin-inline: 3px;
359
+ }
360
+
361
+ .mdbase-app-menu-external {
362
+ width: 15px;
363
+ height: 15px;
364
+ flex: 0 0 auto;
365
+ fill: none;
366
+ stroke: var(--color-text-muted);
367
+ stroke-width: 1.6;
368
+ }
369
+
370
+ .mdbase-app-menu-current {
371
+ flex: 0 0 auto;
372
+ padding: 2px 6px;
373
+ border: 1px solid var(--color-border);
374
+ border-radius: var(--control-radius);
375
+ color: var(--color-text-soft);
376
+ background: var(--color-surface);
377
+ font-family: var(--mono);
378
+ font-size: 11px;
379
+ letter-spacing: 0.04em;
380
+ text-transform: uppercase;
381
+ }
382
+
383
+ /* Buttons: fine outlines like the fields. A primary action is outlined in the accent
384
+ rather than filled, so the canvas stays the only large surface. */
385
+ .mdbase-button {
386
+ min-height: var(--control-height);
387
+ display: inline-flex;
388
+ align-items: center;
389
+ justify-content: center;
390
+ gap: 6px;
391
+ padding: 0 12px;
392
+ border: 1px solid var(--color-border-strong);
393
+ border-radius: var(--control-radius);
394
+ color: var(--color-text-soft);
395
+ background: transparent;
396
+ font: inherit;
397
+ font-size: var(--control-font-size);
398
+ font-weight: 700;
399
+ white-space: nowrap;
400
+ cursor: pointer;
401
+ transition: color var(--motion-quick) ease, border-color var(--motion-quick) ease, background var(--motion-quick) ease;
402
+ }
403
+
404
+ .mdbase-button:hover:not(:disabled) {
405
+ border-color: var(--color-text-soft);
406
+ color: var(--color-text);
407
+ background: var(--color-surface-subtle);
408
+ }
409
+
410
+ .mdbase-button.is-primary {
411
+ border-color: var(--color-accent);
412
+ color: var(--color-accent);
413
+ }
414
+
415
+ .mdbase-button.is-primary:hover:not(:disabled) {
416
+ border-color: var(--color-accent-strong);
417
+ color: var(--color-accent-strong);
418
+ }
419
+
420
+ .mdbase-button.is-danger {
421
+ border-color: var(--color-danger);
422
+ color: var(--color-danger);
423
+ }
424
+
425
+ .mdbase-button.is-danger:hover:not(:disabled) {
426
+ background: var(--color-danger-soft);
427
+ }
428
+
429
+ .mdbase-button:disabled {
430
+ cursor: default;
431
+ opacity: 0.45;
432
+ }
433
+
434
+ .mdbase-button svg {
435
+ width: 15px;
436
+ height: 15px;
437
+ flex: 0 0 auto;
438
+ }
439
+
440
+ .mdbase-icon-button {
441
+ width: 32px;
442
+ height: 32px;
443
+ flex: 0 0 auto;
444
+ display: inline-grid;
445
+ place-items: center;
446
+ padding: 0;
447
+ border: 0;
448
+ border-radius: var(--control-radius);
449
+ color: var(--color-text-soft);
450
+ background: transparent;
451
+ cursor: pointer;
452
+ transition: color var(--motion-quick) ease, background var(--motion-quick) ease;
453
+ }
454
+
455
+ .mdbase-icon-button:hover:not(:disabled),
456
+ .mdbase-icon-button[aria-pressed="true"],
457
+ .mdbase-icon-button[aria-expanded="true"] {
458
+ color: var(--color-text);
459
+ background: var(--color-surface-subtle);
460
+ }
461
+
462
+ .mdbase-icon-button:disabled {
463
+ cursor: default;
464
+ opacity: 0.45;
465
+ }
466
+
467
+ .mdbase-icon-button svg {
468
+ width: 17px;
469
+ height: 17px;
470
+ }
471
+
472
+ .mdbase-button:focus-visible,
473
+ .mdbase-icon-button:focus-visible {
474
+ outline: 2px solid var(--color-accent);
475
+ outline-offset: 2px;
476
+ }
477
+
478
+ /* Dialogs: a native <dialog> in the top layer, drawn like a popover. */
479
+ .mdbase-dialog {
480
+ width: min(40rem, calc(100vw - 32px));
481
+ max-height: calc(100dvh - 48px);
482
+ padding: 0;
483
+ border: 1px solid var(--color-border-strong);
484
+ border-radius: var(--control-radius);
485
+ color: var(--color-text);
486
+ background: var(--color-surface);
487
+ box-shadow: none;
488
+ }
489
+
490
+ .mdbase-dialog[open] {
491
+ animation: mdbase-dialog-in var(--motion-base) var(--ease-out);
492
+ }
493
+
494
+ .mdbase-dialog::backdrop {
495
+ background: var(--color-scrim);
496
+ }
497
+
498
+ @keyframes mdbase-dialog-in {
499
+ from { opacity: 0; transform: translateY(6px); }
500
+ }
501
+
502
+ .mdbase-dialog-body {
503
+ display: grid;
504
+ gap: 1rem;
505
+ padding: 18px 22px 22px;
506
+ }
507
+
508
+ .mdbase-dialog-header {
509
+ display: flex;
510
+ align-items: center;
511
+ justify-content: space-between;
512
+ gap: 12px;
513
+ margin: -4px -8px 0 0;
514
+ }
515
+
516
+ .mdbase-dialog-header h2 {
517
+ margin: 0;
518
+ color: var(--color-text);
519
+ font-size: 17px;
520
+ letter-spacing: -0.01em;
521
+ text-transform: none;
522
+ }
523
+
524
+ /* Popovers arrive quickly and settle into place. Apps mark their own with .mdbase-popover. */
525
+ :is(.mdbase-select-list, .mdbase-menu, .mdbase-popover):popover-open {
526
+ animation: mdbase-popover-in var(--motion-quick) var(--ease-out);
527
+ }
528
+
529
+ @keyframes mdbase-popover-in {
530
+ from { opacity: 0; transform: translateY(-3px); }
531
+ }
532
+
533
+ /* Save notices: small, quiet, and gone once there is nothing to act on. */
534
+ .mdbase-save-notice {
535
+ display: inline-flex;
536
+ align-items: center;
537
+ gap: 4px;
538
+ color: var(--color-text-muted);
539
+ font-family: var(--mono);
540
+ font-size: 11px;
541
+ white-space: nowrap;
542
+ }
543
+
544
+ .mdbase-save-notice svg {
545
+ width: 11px;
546
+ height: 11px;
547
+ fill: none;
548
+ stroke: var(--color-success);
549
+ stroke-width: 1.8;
550
+ stroke-linecap: round;
551
+ stroke-linejoin: round;
552
+ }
553
+
554
+ .mdbase-save-notice.is-attention {
555
+ color: var(--color-danger);
556
+ }
557
+
558
+ /* A confirmation holds, then fades away; hovering or focusing its host brings it back. */
559
+ .mdbase-settle {
560
+ animation: mdbase-settle var(--motion-settle) ease forwards;
561
+ }
562
+
563
+ .mdbase-settle-host:is(:hover, :focus-within) .mdbase-settle {
564
+ animation: none;
565
+ opacity: 1;
566
+ }
567
+
568
+ @keyframes mdbase-settle {
569
+ 0%, 55% { opacity: 1; }
570
+ 100% { opacity: 0; }
571
+ }
572
+
573
+ @media (pointer: coarse) {
574
+ /* A fingertip target. An app that sets its own density keeps it. */
575
+ :root {
576
+ --control-height: 40px;
577
+ }
578
+
579
+ .mdbase-select-option {
580
+ min-height: 40px;
581
+ }
582
+
583
+ .mdbase-menu-item {
584
+ min-height: 48px;
585
+ }
586
+ }
587
+
588
+ @media (prefers-reduced-motion: reduce) {
589
+ :is(.mdbase-select-list, .mdbase-menu, .mdbase-popover):popover-open {
590
+ animation: none;
591
+ }
592
+
593
+ .mdbase-dialog[open] {
594
+ animation: none;
595
+ }
596
+
597
+ /* Without the fade, a confirmation still shows for the same time, then goes. */
598
+ .mdbase-settle {
599
+ animation-timing-function: steps(1, end);
600
+ }
601
+
602
+ .mdbase-button,
603
+ .mdbase-icon-button,
604
+ .mdbase-field,
605
+ .mdbase-select,
606
+ .mdbase-select-chevron,
607
+ .mdbase-app-switcher,
608
+ .mdbase-app-switcher-chevron {
609
+ transition: none;
610
+ }
611
+ }
package/css/fonts.css ADDED
@@ -0,0 +1,6 @@
1
+ /* Latin subsets of the two mdbase families. Browsers fetch a weight only when text uses it. */
2
+ @import "@fontsource/atkinson-hyperlegible/latin-400.css";
3
+ @import "@fontsource/atkinson-hyperlegible/latin-700.css";
4
+ @import "@fontsource/azeret-mono/latin-400.css";
5
+ @import "@fontsource/azeret-mono/latin-500.css";
6
+ @import "@fontsource/azeret-mono/latin-600.css";