@mdbase-dev/ui 0.1.0-beta.125 → 0.1.0-beta.126

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/brand.css CHANGED
@@ -38,7 +38,7 @@
38
38
  }
39
39
 
40
40
  .mdbase-mark .mdbase-mark-progress-fill {
41
- transition: width 240ms cubic-bezier(0.22, 1, 0.36, 1);
41
+ transition: width var(--motion-slow) var(--ease-out);
42
42
  }
43
43
 
44
44
  @media (prefers-reduced-motion: reduce) {
@@ -53,8 +53,8 @@
53
53
  gap: 8px;
54
54
  color: var(--color-text);
55
55
  font-family: var(--mono);
56
- font-size: 12px;
57
- font-weight: 500;
56
+ font-size: var(--font-size-caption);
57
+ font-weight: var(--font-weight-medium);
58
58
  line-height: 1;
59
59
  letter-spacing: -0.02em;
60
60
  }
@@ -72,19 +72,19 @@
72
72
  gap: 0.65ch;
73
73
  white-space: nowrap;
74
74
  transition:
75
- opacity 140ms cubic-bezier(0.22, 1, 0.36, 1),
76
- transform 140ms cubic-bezier(0.22, 1, 0.36, 1),
77
- display 140ms allow-discrete;
75
+ opacity var(--motion-quick) var(--ease-out),
76
+ transform var(--motion-quick) var(--ease-out),
77
+ display var(--motion-quick) allow-discrete;
78
78
  }
79
79
 
80
80
  .wordmark strong {
81
81
  display: inline-block;
82
82
  color: var(--color-text-muted);
83
- font-weight: 500;
83
+ font-weight: var(--font-weight-medium);
84
84
  transition:
85
- opacity 140ms cubic-bezier(0.22, 1, 0.36, 1),
86
- transform 140ms cubic-bezier(0.22, 1, 0.36, 1),
87
- display 140ms allow-discrete;
85
+ opacity var(--motion-quick) var(--ease-out),
86
+ transform var(--motion-quick) var(--ease-out),
87
+ display var(--motion-quick) allow-discrete;
88
88
  }
89
89
 
90
90
  @starting-style {
package/css/controls.css CHANGED
@@ -2,13 +2,13 @@
2
2
  * Shared controls. Text fields stay native and take Editor's field treatment;
3
3
  * Select, menus and the app switcher are the components from ./select,
4
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.
5
+ * outlines and token corners. Apps set density with --control-height
6
+ * and --control-font-size; action hierarchy and focus treatment stay shared.
7
7
  */
8
8
  :root {
9
9
  --control-height: 34px;
10
- --control-font-size: 12px;
11
- --control-radius: 2px;
10
+ --control-font-size: var(--font-size-ui);
11
+ --control-radius: var(--radius-sm);
12
12
  }
13
13
 
14
14
  .mdbase-visually-hidden {
@@ -33,7 +33,7 @@
33
33
  background: transparent;
34
34
  font: inherit;
35
35
  font-size: var(--control-font-size);
36
- transition: border-color 150ms ease, background 150ms ease;
36
+ transition: border-color var(--motion-quick) var(--ease-standard), background var(--motion-quick) var(--ease-standard);
37
37
  }
38
38
 
39
39
  .mdbase-field {
@@ -71,7 +71,8 @@ textarea.mdbase-field {
71
71
  .mdbase-field[aria-invalid="true"],
72
72
  .mdbase-select[aria-invalid="true"] {
73
73
  border-color: var(--color-danger);
74
- box-shadow: 0 0 0 1px var(--color-danger-soft);
74
+ outline: 1px solid var(--color-danger-soft);
75
+ outline-offset: -1px;
75
76
  }
76
77
 
77
78
  /* Select: the trigger reads as a field; its list opens in the top layer. */
@@ -106,7 +107,7 @@ textarea.mdbase-field {
106
107
  stroke-width: 2;
107
108
  stroke-linecap: round;
108
109
  stroke-linejoin: round;
109
- transition: transform 150ms ease;
110
+ transition: transform var(--motion-quick) var(--ease-standard);
110
111
  }
111
112
 
112
113
  .mdbase-select[aria-expanded="true"] .mdbase-select-chevron {
@@ -129,16 +130,14 @@ textarea.mdbase-field {
129
130
  background: var(--color-surface);
130
131
  }
131
132
 
132
- /* Compact: a small uppercase label, for kinds and modes beside a value. */
133
+ /* Compact: a sentence-case label for kinds and modes beside a value. */
133
134
  .mdbase-select.is-compact {
134
135
  min-height: 20px;
135
136
  padding: 0;
136
137
  border: 0;
137
138
  background: transparent;
138
139
  color: var(--color-text-muted);
139
- font-family: var(--mono);
140
- font-size: 11px;
141
- text-transform: uppercase;
140
+ font-size: var(--font-size-caption);
142
141
  }
143
142
 
144
143
  .mdbase-select.is-compact .mdbase-select-chevron {
@@ -165,8 +164,8 @@ textarea.mdbase-field {
165
164
  .mdbase-select-group {
166
165
  padding: 8px 10px 3px;
167
166
  color: var(--color-text-muted);
168
- font-size: 11px;
169
- font-weight: 700;
167
+ font-size: var(--font-size-caption);
168
+ font-weight: var(--font-weight-semibold);
170
169
  }
171
170
 
172
171
  .mdbase-select-option {
@@ -188,7 +187,8 @@ textarea.mdbase-field {
188
187
  }
189
188
 
190
189
  .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);
190
+ outline: 1px solid var(--color-accent);
191
+ outline-offset: -1px;
192
192
  }
193
193
 
194
194
  .mdbase-select-option[aria-selected="true"] {
@@ -235,8 +235,8 @@ textarea.mdbase-field {
235
235
  .mdbase-menu-heading {
236
236
  padding: 6px 8px 4px;
237
237
  color: var(--color-text-muted);
238
- font-size: 11px;
239
- font-weight: 700;
238
+ font-size: var(--font-size-caption);
239
+ font-weight: var(--font-weight-semibold);
240
240
  }
241
241
 
242
242
  .mdbase-menu-list {
@@ -269,7 +269,8 @@ textarea.mdbase-field {
269
269
  /* One focus mark inside the row, instead of an outline around it. */
270
270
  .mdbase-menu .mdbase-menu-item:focus-visible {
271
271
  outline: 0;
272
- box-shadow: inset 0 0 0 2px color-mix(in oklch, var(--color-accent) 70%, transparent);
272
+ outline: 1px solid var(--color-accent);
273
+ outline-offset: -1px;
273
274
  }
274
275
 
275
276
  .mdbase-menu-item:disabled {
@@ -298,12 +299,12 @@ textarea.mdbase-field {
298
299
 
299
300
  .mdbase-menu-copy strong {
300
301
  color: var(--color-text);
301
- font-weight: 600;
302
+ font-weight: var(--font-weight-semibold);
302
303
  }
303
304
 
304
305
  .mdbase-menu-copy small {
305
306
  color: var(--color-text-muted);
306
- font-size: 11px;
307
+ font-size: var(--font-size-caption);
307
308
  }
308
309
 
309
310
  .mdbase-menu-note {
@@ -311,7 +312,7 @@ textarea.mdbase-field {
311
312
  padding: 8px 8px 2px;
312
313
  border-top: 1px solid var(--color-border);
313
314
  color: var(--color-text-muted);
314
- font-size: 11px;
315
+ font-size: var(--font-size-caption);
315
316
  }
316
317
 
317
318
  /* The app switcher: the wordmark opens the app menu. */
@@ -328,7 +329,7 @@ textarea.mdbase-field {
328
329
  background: transparent;
329
330
  font: inherit;
330
331
  cursor: pointer;
331
- transition: background 150ms ease, border-color 150ms ease;
332
+ transition: background var(--motion-quick) var(--ease-standard), border-color var(--motion-quick) var(--ease-standard);
332
333
  }
333
334
 
334
335
  .mdbase-app-switcher:hover,
@@ -344,7 +345,7 @@ textarea.mdbase-field {
344
345
  fill: none;
345
346
  stroke: var(--color-text-muted);
346
347
  stroke-width: 1.6;
347
- transition: transform 150ms ease;
348
+ transition: transform var(--motion-quick) var(--ease-standard);
348
349
  }
349
350
 
350
351
  .mdbase-app-switcher[aria-expanded="true"] .mdbase-app-switcher-chevron {
@@ -374,14 +375,10 @@ textarea.mdbase-field {
374
375
  border-radius: var(--control-radius);
375
376
  color: var(--color-text-soft);
376
377
  background: var(--color-surface);
377
- font-family: var(--mono);
378
- font-size: 11px;
379
- letter-spacing: 0.04em;
380
- text-transform: uppercase;
378
+ font-size: var(--font-size-caption);
381
379
  }
382
380
 
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. */
381
+ /* One filled committing action; tonal secondary actions and text-led tertiary actions. */
385
382
  .mdbase-button {
386
383
  min-height: var(--control-height);
387
384
  display: inline-flex;
@@ -389,16 +386,16 @@ textarea.mdbase-field {
389
386
  justify-content: center;
390
387
  gap: 6px;
391
388
  padding: 0 12px;
392
- border: 1px solid var(--color-border-strong);
389
+ border: 1px solid transparent;
393
390
  border-radius: var(--control-radius);
394
- color: var(--color-text-soft);
395
- background: transparent;
391
+ color: var(--color-text);
392
+ background: var(--color-surface-subtle);
396
393
  font: inherit;
397
394
  font-size: var(--control-font-size);
398
- font-weight: 700;
395
+ font-weight: var(--font-weight-semibold);
399
396
  white-space: nowrap;
400
397
  cursor: pointer;
401
- transition: color var(--motion-quick) ease, border-color var(--motion-quick) ease, background var(--motion-quick) ease;
398
+ transition: color var(--motion-quick) var(--ease-standard), border-color var(--motion-quick) var(--ease-standard), background var(--motion-quick) var(--ease-standard);
402
399
  }
403
400
 
404
401
  .mdbase-button:hover:not(:disabled) {
@@ -409,12 +406,25 @@ textarea.mdbase-field {
409
406
 
410
407
  .mdbase-button.is-primary {
411
408
  border-color: var(--color-accent);
412
- color: var(--color-accent);
409
+ color: var(--color-on-accent);
410
+ background: var(--color-accent);
413
411
  }
414
412
 
415
413
  .mdbase-button.is-primary:hover:not(:disabled) {
416
414
  border-color: var(--color-accent-strong);
417
- color: var(--color-accent-strong);
415
+ color: var(--color-on-accent);
416
+ background: var(--color-accent-strong);
417
+ }
418
+
419
+ .mdbase-button.is-tertiary {
420
+ color: var(--color-text-soft);
421
+ background: transparent;
422
+ }
423
+
424
+ .mdbase-button.is-tertiary:hover:not(:disabled) {
425
+ border-color: transparent;
426
+ color: var(--color-text);
427
+ background: var(--color-surface-subtle);
418
428
  }
419
429
 
420
430
  .mdbase-button.is-danger {
@@ -427,8 +437,19 @@ textarea.mdbase-field {
427
437
  }
428
438
 
429
439
  .mdbase-button:disabled {
430
- cursor: default;
431
- opacity: 0.45;
440
+ border-color: var(--color-border);
441
+ color: var(--color-text-muted);
442
+ background: var(--color-surface-subtle);
443
+ cursor: not-allowed;
444
+ }
445
+
446
+ .mdbase-button.is-tertiary:disabled {
447
+ border-color: transparent;
448
+ background: transparent;
449
+ }
450
+
451
+ .mdbase-button:disabled svg {
452
+ opacity: 0.5;
432
453
  }
433
454
 
434
455
  .mdbase-button svg {
@@ -449,7 +470,7 @@ textarea.mdbase-field {
449
470
  color: var(--color-text-soft);
450
471
  background: transparent;
451
472
  cursor: pointer;
452
- transition: color var(--motion-quick) ease, background var(--motion-quick) ease;
473
+ transition: color var(--motion-quick) var(--ease-standard), background var(--motion-quick) var(--ease-standard);
453
474
  }
454
475
 
455
476
  .mdbase-icon-button:hover:not(:disabled),
@@ -475,6 +496,135 @@ textarea.mdbase-field {
475
496
  outline-offset: 2px;
476
497
  }
477
498
 
499
+ /* Switch: native button semantics (role="switch", aria-checked), one track and thumb. */
500
+ .mdbase-switch {
501
+ position: relative;
502
+ width: 36px;
503
+ height: 22px;
504
+ flex: 0 0 auto;
505
+ display: inline-flex;
506
+ align-items: center;
507
+ padding: 2px;
508
+ border: 1px solid var(--color-border-strong);
509
+ border-radius: var(--radius-lg);
510
+ background: var(--color-surface-subtle);
511
+ cursor: pointer;
512
+ transition: background var(--motion-base) var(--ease-out), border-color var(--motion-base) var(--ease-out);
513
+ }
514
+
515
+ .mdbase-switch > span {
516
+ width: 16px;
517
+ height: 16px;
518
+ border-radius: 50%;
519
+ background: var(--color-text-soft);
520
+ pointer-events: none;
521
+ transition: transform var(--motion-base) var(--ease-out), background var(--motion-base) var(--ease-out);
522
+ }
523
+
524
+ .mdbase-switch[aria-checked="true"] {
525
+ border-color: var(--color-accent);
526
+ background: var(--color-accent);
527
+ }
528
+
529
+ .mdbase-switch[aria-checked="true"] > span {
530
+ transform: translateX(14px);
531
+ background: var(--color-on-accent);
532
+ }
533
+
534
+ .mdbase-switch:disabled {
535
+ border-color: var(--color-border);
536
+ background: var(--color-surface-subtle);
537
+ cursor: not-allowed;
538
+ opacity: 0.5;
539
+ }
540
+
541
+ /* Checkbox: native inputs keep Space, label activation, form state and indeterminate support.
542
+ Writer's Markdown checkbox button uses the same appearance with aria-checked. */
543
+ .mdbase-checkbox {
544
+ appearance: none;
545
+ position: relative;
546
+ width: 18px;
547
+ height: 18px;
548
+ flex: 0 0 auto;
549
+ display: inline-grid;
550
+ place-items: center;
551
+ margin: 0;
552
+ padding: 0;
553
+ border: 1px solid var(--color-border-strong);
554
+ border-radius: var(--radius-sm);
555
+ background: var(--color-surface);
556
+ color: var(--color-on-accent);
557
+ vertical-align: middle;
558
+ cursor: pointer;
559
+ transition: background var(--motion-quick) var(--ease-out), border-color var(--motion-quick) var(--ease-out);
560
+ }
561
+
562
+ .mdbase-checkbox:checked,
563
+ .mdbase-checkbox:indeterminate,
564
+ .mdbase-checkbox[aria-checked="true"] {
565
+ border-color: var(--color-accent);
566
+ background: var(--color-accent);
567
+ }
568
+
569
+ .mdbase-checkbox:checked::after,
570
+ .mdbase-checkbox[aria-checked="true"]::after {
571
+ width: 4px;
572
+ height: 8px;
573
+ margin-top: -2px;
574
+ border-right: 2px solid currentColor;
575
+ border-bottom: 2px solid currentColor;
576
+ content: "";
577
+ transform: rotate(45deg);
578
+ }
579
+
580
+ .mdbase-checkbox:indeterminate::after {
581
+ width: 8px;
582
+ border-top: 2px solid currentColor;
583
+ content: "";
584
+ }
585
+
586
+ .mdbase-checkbox:hover:not(:disabled) {
587
+ border-color: var(--color-accent);
588
+ }
589
+
590
+ .mdbase-checkbox:disabled {
591
+ cursor: not-allowed;
592
+ opacity: 0.5;
593
+ }
594
+
595
+ .mdbase-checkbox:focus-visible,
596
+ .mdbase-switch:focus-visible {
597
+ outline: 2px solid var(--color-accent);
598
+ outline-offset: 2px;
599
+ }
600
+
601
+ @media (pointer: coarse) {
602
+ /* Expand the hit target, not the visual control or its containing row. */
603
+ .mdbase-checkbox::before,
604
+ .mdbase-switch::before {
605
+ position: absolute;
606
+ inset: -13px;
607
+ content: "";
608
+ }
609
+ }
610
+
611
+ @media (forced-colors: active) {
612
+ .mdbase-checkbox,
613
+ .mdbase-switch {
614
+ border-color: ButtonText;
615
+ background: ButtonFace;
616
+ forced-color-adjust: none;
617
+ }
618
+ .mdbase-checkbox:is(:checked, :indeterminate, [aria-checked="true"]),
619
+ .mdbase-switch[aria-checked="true"] {
620
+ border-color: Highlight;
621
+ background: Highlight;
622
+ color: HighlightText;
623
+ }
624
+ .mdbase-switch > span { background: ButtonText; }
625
+ .mdbase-switch[aria-checked="true"] > span { background: HighlightText; }
626
+ }
627
+
478
628
  /* Dialogs: a native <dialog> in the top layer, drawn like a popover. */
479
629
  .mdbase-dialog {
480
630
  width: min(40rem, calc(100vw - 32px));
@@ -516,7 +666,7 @@ textarea.mdbase-field {
516
666
  .mdbase-dialog-header h2 {
517
667
  margin: 0;
518
668
  color: var(--color-text);
519
- font-size: 17px;
669
+ font-size: var(--font-size-prose);
520
670
  letter-spacing: -0.01em;
521
671
  text-transform: none;
522
672
  }
@@ -536,8 +686,7 @@ textarea.mdbase-field {
536
686
  align-items: center;
537
687
  gap: 4px;
538
688
  color: var(--color-text-muted);
539
- font-family: var(--mono);
540
- font-size: 11px;
689
+ font-size: var(--font-size-caption);
541
690
  white-space: nowrap;
542
691
  }
543
692
 
@@ -605,7 +754,10 @@ textarea.mdbase-field {
605
754
  .mdbase-select,
606
755
  .mdbase-select-chevron,
607
756
  .mdbase-app-switcher,
608
- .mdbase-app-switcher-chevron {
757
+ .mdbase-app-switcher-chevron,
758
+ .mdbase-checkbox,
759
+ .mdbase-switch,
760
+ .mdbase-switch > span {
609
761
  transition: none;
610
762
  }
611
763
  }
package/css/feedback.css CHANGED
@@ -1,56 +1,56 @@
1
1
  .mdbase-feedback-app { display: contents; }
2
2
  .mdbase-feedback-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
3
- .mdbase-feedback-trigger { display: inline-flex; align-items: center; gap: 8px; padding: 8px 10px; border: 0; border-radius: 4px; background: transparent; color: var(--color-text-soft); font: 12px/1.4 var(--sans); cursor: pointer; }
3
+ .mdbase-feedback-trigger { display: inline-flex; align-items: center; gap: 8px; padding: 8px 10px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--color-text-soft); font: var(--font-size-caption)/1.4 var(--sans); cursor: pointer; }
4
4
  .mdbase-feedback-trigger:hover { background: var(--color-selected); color: var(--color-text); }
5
5
  .mdbase-feedback-bug { width: 22px; height: 22px; flex-shrink: 0; }
6
6
  @keyframes mdbase-feedback-wiggle { 0%,100% { transform: rotate(0); } 25% { transform: rotate(-14deg); } 75% { transform: rotate(14deg); } }
7
7
  @media (prefers-reduced-motion: no-preference) { .mdbase-feedback-bug.is-wiggling { animation: mdbase-feedback-wiggle 280ms ease-in-out 2; } }
8
- .mdbase-feedback-dialog { position: fixed; inset: 0 0 0 auto; margin: 0; padding: 0; width: 440px; max-width: 100vw; height: 100dvh; max-height: 100dvh; border: 0; border-left: 1px solid var(--color-border-strong); background: var(--color-canvas); color: var(--color-text); font: 13px/1.5 var(--sans); overflow: hidden; }
8
+ .mdbase-feedback-dialog { position: fixed; inset: 0 0 0 auto; margin: 0; padding: 0; width: 440px; max-width: 100vw; height: 100dvh; max-height: 100dvh; border: 0; border-left: 1px solid var(--color-border-strong); background: var(--color-canvas); color: var(--color-text); font: var(--font-size-ui)/1.5 var(--sans); overflow: hidden; }
9
9
  .mdbase-feedback-dialog[open] { display: flex; flex-direction: column; }
10
10
  .mdbase-feedback-dialog:not([open]) { display: none; }
11
11
  .mdbase-feedback-dialog::backdrop { background: var(--color-scrim); }
12
12
  .mdbase-feedback-dialog *, .mdbase-feedback-markup * { box-sizing: border-box; }
13
13
  .mdbase-feedback-dialog :is(button,input,textarea), .mdbase-feedback-markup button { font: inherit; }
14
14
  .mdbase-feedback-dialog :focus-visible, .mdbase-feedback-markup :focus-visible, .mdbase-feedback-trigger:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }
15
- .mdbase-feedback-dialog button, .mdbase-feedback-markup .mdbase-feedback-actions button, .mdbase-feedback-tools button { border: 1px solid var(--color-border-strong); border-radius: 4px; padding: 7px 11px; background: var(--color-canvas); color: var(--color-text); cursor: pointer; }
15
+ .mdbase-feedback-dialog button, .mdbase-feedback-markup .mdbase-feedback-actions button, .mdbase-feedback-tools button { border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); padding: 7px 11px; background: var(--color-canvas); color: var(--color-text); cursor: pointer; }
16
16
  .mdbase-feedback-dialog button:hover, .mdbase-feedback-markup button:hover { background: var(--color-selected); }
17
17
  .mdbase-feedback-dialog button:disabled, .mdbase-feedback-markup button:disabled { opacity: .5; cursor: default; }
18
18
  .mdbase-feedback-header { display: flex; justify-content: space-between; align-items: center; gap: 15px; padding: 27px; border-bottom: 1px solid var(--color-border); flex-shrink: 0; }
19
- .mdbase-feedback-header h2 { font-size: 23px; line-height: 1.25; letter-spacing: -.5px; margin: 0; color: var(--color-text); }
20
- .mdbase-feedback-header button { border: 0; width: 34px; height: 34px; padding: 0; font-size: 23px; color: var(--color-text-muted); }
19
+ .mdbase-feedback-header h2 { font-size: var(--font-size-heading); line-height: 1.25; letter-spacing: -.5px; margin: 0; color: var(--color-text); }
20
+ .mdbase-feedback-header button { border: 0; width: 34px; height: 34px; padding: 0; font-size: var(--font-size-heading); color: var(--color-text-muted); }
21
21
  .mdbase-feedback-dialog form { display: flex; flex-direction: column; flex: 1; overflow: hidden; margin: 0; }
22
22
  .mdbase-feedback-body { flex: 1; overflow-y: auto; padding: 23px 27px; }
23
23
  .mdbase-feedback-topics { display: grid; gap: 6px; border: 0; padding: 0; margin: 0 0 23px; }
24
- .mdbase-feedback-topics legend { margin-bottom: 10px; font-size: 12px; font-weight: 700; }
25
- .mdbase-feedback-topics label { display: flex; align-items: center; gap: 9px; padding: 9px 11px; border: 1px solid var(--color-border); border-radius: 4px; font-size: 12px; cursor: pointer; }
24
+ .mdbase-feedback-topics legend { margin-bottom: 10px; font-size: var(--font-size-caption); font-weight: var(--font-weight-semibold); }
25
+ .mdbase-feedback-topics label { display: flex; align-items: center; gap: 9px; padding: 9px 11px; border: 1px solid var(--color-border); border-radius: var(--radius-sm); font-size: var(--font-size-caption); cursor: pointer; }
26
26
  .mdbase-feedback-topics label:has(:checked) { border-color: var(--color-accent); background: var(--color-accent-soft); }
27
- .mdbase-feedback-dialog input:is([type=radio],[type=checkbox]) { accent-color: var(--color-accent); margin: 0; width: 14px; height: 14px; flex-shrink: 0; }
28
- .mdbase-feedback-field { display: flex; flex-direction: column; gap: 8px; margin-bottom: 20px; font-weight: 700; }
29
- .mdbase-feedback-field small { margin-left: 6px; font-weight: 400; font-size: 11px; color: var(--color-text-muted); }
30
- .mdbase-feedback-field :is(input,textarea) { width: 100%; background: var(--color-canvas); border: 1px solid var(--color-border-strong); border-radius: 4px; color: var(--color-text); padding: 10px 11px; font-weight: 400; }
27
+ .mdbase-feedback-dialog input[type=radio] { accent-color: var(--color-accent); margin: 0; width: 14px; height: 14px; flex-shrink: 0; }
28
+ .mdbase-feedback-field { display: flex; flex-direction: column; gap: 8px; margin-bottom: 20px; font-weight: var(--font-weight-semibold); }
29
+ .mdbase-feedback-field small { margin-left: 6px; font-weight: var(--font-weight-regular); font-size: var(--font-size-caption); color: var(--color-text-muted); }
30
+ .mdbase-feedback-field :is(input,textarea) { width: 100%; background: var(--color-canvas); border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); color: var(--color-text); padding: 10px 11px; font-weight: var(--font-weight-regular); }
31
31
  .mdbase-feedback-field textarea { min-height: 125px; resize: vertical; }
32
32
  .mdbase-feedback-field :is(input,textarea)::placeholder { color: var(--color-text-muted); }
33
33
  .mdbase-feedback-screenshot { margin: 0 0 20px; }
34
34
  .mdbase-feedback-dialog .mdbase-feedback-preview { display: block; position: relative; padding: 0; width: 100%; overflow: hidden; }
35
35
  .mdbase-feedback-preview img { display: block; width: 100%; height: 165px; object-fit: contain; }
36
- .mdbase-feedback-preview span { position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%); background: var(--color-canvas); border: 1px solid var(--color-border-strong); border-radius: 4px; padding: 4px 8px; font-size: 11px; white-space: nowrap; }
37
- .mdbase-feedback-screenshot-actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px; font-size: 11px; }
36
+ .mdbase-feedback-preview span { position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%); background: var(--color-canvas); border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); padding: 4px 8px; font-size: var(--font-size-caption); white-space: nowrap; }
37
+ .mdbase-feedback-screenshot-actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px; font-size: var(--font-size-caption); }
38
38
  .mdbase-feedback-screenshot-actions label { display: flex; align-items: center; gap: 7px; }
39
39
  .mdbase-feedback-upload { position: relative; color: var(--color-accent); cursor: pointer; padding: 6px 0; }
40
40
  .mdbase-feedback-upload input { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
41
41
  .mdbase-feedback-upload:focus-within { outline: 2px solid var(--color-accent); outline-offset: 3px; }
42
- .mdbase-feedback-check { display: flex; gap: 8px; align-items: center; font-size: 12px; }
42
+ .mdbase-feedback-check { display: flex; gap: 8px; align-items: center; font-size: var(--font-size-caption); }
43
43
  .mdbase-feedback-details > .mdbase-feedback-check { margin: 14px 0 8px; }
44
- .mdbase-feedback-help { font-size: 11px; line-height: 1.6; color: var(--color-text-muted); margin: 9px 0; }
45
- .mdbase-feedback-dialog details { font-size: 11px; color: var(--color-text-soft); margin: 12px 0; }
44
+ .mdbase-feedback-help { font-size: var(--font-size-caption); line-height: 1.6; color: var(--color-text-muted); margin: 9px 0; }
45
+ .mdbase-feedback-dialog details { font-size: var(--font-size-caption); color: var(--color-text-soft); margin: 12px 0; }
46
46
  .mdbase-feedback-dialog summary { cursor: pointer; }
47
- .mdbase-feedback-dialog pre { font: 11px/1.6 var(--mono); white-space: pre-wrap; overflow-wrap: anywhere; background: var(--color-surface-subtle); padding: 12px; border: 1px solid var(--color-border); border-radius: 4px; }
48
- .mdbase-feedback-error { color: var(--color-danger); font-size: 12px; }
47
+ .mdbase-feedback-dialog pre { font: var(--font-size-caption)/1.6 var(--mono); white-space: pre-wrap; overflow-wrap: anywhere; background: var(--color-surface-subtle); padding: 12px; border: 1px solid var(--color-border); border-radius: var(--radius-sm); }
48
+ .mdbase-feedback-error { color: var(--color-danger); font-size: var(--font-size-caption); }
49
49
  .mdbase-feedback-actions { display: flex; justify-content: space-between; align-items: center; gap: 12px; border-top: 1px solid var(--color-border); padding: 18px 27px; flex-shrink: 0; }
50
50
  .mdbase-feedback-dialog .mdbase-feedback-primary, .mdbase-feedback-markup .mdbase-feedback-actions .mdbase-feedback-primary { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-on-accent); }
51
51
  .mdbase-feedback-dialog .mdbase-feedback-primary:hover, .mdbase-feedback-markup .mdbase-feedback-actions .mdbase-feedback-primary:hover { background: var(--color-accent-strong); }
52
52
  .mdbase-feedback-success { padding: 40px 27px; }
53
- .mdbase-feedback-success h3 { font-size: 25px; line-height: 1.3; margin: 0 0 17px; }
53
+ .mdbase-feedback-success h3 { font-size: var(--font-size-heading); line-height: 1.3; margin: 0 0 17px; }
54
54
  .mdbase-feedback-success .mdbase-feedback-primary { margin-top: 20px; }
55
55
  .mdbase-feedback-markup { width: min(1100px,calc(100vw - 32px)); max-width: none; max-height: calc(100dvh - 32px); padding: 0; }
56
56
  .mdbase-feedback-markup .mdbase-dialog-body { max-height: calc(100dvh - 34px); display: flex; flex-direction: column; overflow: hidden; padding: 20px; }
@@ -59,4 +59,4 @@
59
59
  .mdbase-feedback-canvas { background: var(--color-surface-subtle); border: 1px solid var(--color-border); padding: 12px; overflow: auto; display: flex; justify-content: center; align-items: center; min-height: 0; }
60
60
  .mdbase-feedback-canvas canvas { display: block; max-width: 100%; max-height: calc(100dvh - 345px); width: auto; height: auto; background: white; touch-action: none; cursor: crosshair; }
61
61
  .mdbase-feedback-markup .mdbase-feedback-actions { padding: 15px 0 0; }
62
- @media (max-width: 600px) { .mdbase-feedback-header { padding: 23px; } .mdbase-feedback-body { padding: 20px 23px; } .mdbase-feedback-actions { padding: 16px 23px; } .mdbase-feedback-tools { gap: 4px; } .mdbase-feedback-tools button { font-size: 11px; padding: 6px 8px; } }
62
+ @media (max-width: 600px) { .mdbase-feedback-header { padding: 23px; } .mdbase-feedback-body { padding: 20px 23px; } .mdbase-feedback-actions { padding: 16px 23px; } .mdbase-feedback-tools { gap: 4px; } .mdbase-feedback-tools button { font-size: var(--font-size-caption); padding: 6px 8px; } }
package/css/fonts.css CHANGED
@@ -1,6 +1,5 @@
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";
1
+ /* Next provides real intermediate weights (200–800), served locally under font-src 'self'. */
2
+ @import "@fontsource-variable/atkinson-hyperlegible-next/wght.css";
4
3
  @import "@fontsource/azeret-mono/latin-400.css";
5
4
  @import "@fontsource/azeret-mono/latin-500.css";
6
5
  @import "@fontsource/azeret-mono/latin-600.css";
package/css/palette.css CHANGED
@@ -64,7 +64,7 @@
64
64
  color: var(--color-text);
65
65
  background: transparent;
66
66
  font: inherit;
67
- font-size: 15px;
67
+ font-size: var(--font-size-section);
68
68
  }
69
69
 
70
70
  .mdbase-command-search input::placeholder {
@@ -81,8 +81,8 @@
81
81
  .mdbase-command-group {
82
82
  padding: 10px 10px 4px;
83
83
  color: var(--color-text-muted);
84
- font-size: 11px;
85
- font-weight: 600;
84
+ font-size: var(--font-size-caption);
85
+ font-weight: var(--font-weight-semibold);
86
86
  }
87
87
 
88
88
  .mdbase-command-results > button {
@@ -124,14 +124,14 @@
124
124
 
125
125
  .mdbase-command-results strong {
126
126
  flex: 0 1 auto;
127
- font-size: 13px;
128
- font-weight: 500;
127
+ font-size: var(--font-size-ui);
128
+ font-weight: var(--font-weight-medium);
129
129
  }
130
130
 
131
131
  .mdbase-command-results small {
132
132
  flex: 1 1 auto;
133
133
  color: var(--color-text-muted);
134
- font-size: 12px;
134
+ font-size: var(--font-size-caption);
135
135
  }
136
136
 
137
137
  .mdbase-command-results kbd,
@@ -140,7 +140,7 @@
140
140
  border: 1px solid var(--color-border);
141
141
  border-radius: var(--control-radius);
142
142
  color: var(--color-text-muted);
143
- font: 10px/1.2 var(--mono);
143
+ font: var(--font-size-caption)/1.2 var(--sans);
144
144
  }
145
145
 
146
146
  .mdbase-command-footer kbd {
@@ -149,7 +149,7 @@
149
149
 
150
150
  .mdbase-command-alternate {
151
151
  color: var(--color-text-muted);
152
- font-size: 11px;
152
+ font-size: var(--font-size-caption);
153
153
  white-space: nowrap;
154
154
  }
155
155
 
@@ -160,13 +160,13 @@
160
160
  padding: 8px 14px;
161
161
  border-top: 1px solid var(--color-border);
162
162
  color: var(--color-text-muted);
163
- font-size: 11px;
163
+ font-size: var(--font-size-caption);
164
164
  }
165
165
 
166
166
  .mdbase-command-empty {
167
167
  padding: 28px;
168
168
  color: var(--color-text-muted);
169
- font-size: 13px;
169
+ font-size: var(--font-size-ui);
170
170
  text-align: center;
171
171
  }
172
172
 
package/css/screens.css CHANGED
@@ -25,13 +25,13 @@
25
25
 
26
26
  .mdbase-opening-message p {
27
27
  max-width: 44ch;
28
- font-size: 13px;
28
+ font-size: var(--font-size-ui);
29
29
  }
30
30
 
31
31
  .mdbase-opening-message small {
32
32
  margin-top: 4px;
33
33
  color: var(--color-text-muted);
34
- font-size: 11px;
34
+ font-size: var(--font-size-caption);
35
35
  }
36
36
 
37
37
  .mdbase-opening[data-loading-state="failed"] .mdbase-opening-message p {
@@ -55,8 +55,8 @@
55
55
  .mdbase-connect-screen h1 {
56
56
  margin: 44px 0 22px;
57
57
  color: var(--color-text);
58
- font-size: 48px;
59
- font-weight: 700;
58
+ font-size: var(--font-size-title);
59
+ font-weight: var(--font-weight-semibold);
60
60
  line-height: 0.98;
61
61
  letter-spacing: -0.045em;
62
62
  }
@@ -65,14 +65,14 @@
65
65
  max-width: 34ch;
66
66
  margin: 0;
67
67
  color: var(--color-text-soft);
68
- font-size: 17px;
68
+ font-size: var(--font-size-prose);
69
69
  line-height: 1.55;
70
70
  }
71
71
 
72
72
  .mdbase-connect-status {
73
73
  margin: 14px 0 0;
74
74
  color: var(--color-text-muted);
75
- font-size: 13px;
75
+ font-size: var(--font-size-ui);
76
76
  }
77
77
 
78
78
  /* The one strong action on the screen. */
@@ -88,10 +88,10 @@
88
88
  color: var(--color-text);
89
89
  background: var(--color-surface);
90
90
  font: inherit;
91
- font-size: 14px;
92
- font-weight: 700;
91
+ font-size: var(--font-size-ui);
92
+ font-weight: var(--font-weight-semibold);
93
93
  cursor: pointer;
94
- transition: border-color var(--motion-quick) ease, background var(--motion-quick) ease;
94
+ transition: border-color var(--motion-quick) var(--ease-standard), background var(--motion-quick) var(--ease-standard);
95
95
  }
96
96
 
97
97
  .mdbase-connect-action:hover:not(:disabled) {
@@ -113,28 +113,28 @@
113
113
  max-width: 47ch;
114
114
  margin: 18px 0 0;
115
115
  color: var(--color-text-muted);
116
- font-size: 12px;
116
+ font-size: var(--font-size-caption);
117
117
  line-height: 1.55;
118
118
  }
119
119
 
120
120
  .mdbase-connect-error {
121
121
  margin: 18px 0 0;
122
122
  color: var(--color-danger);
123
- font-size: 13px;
123
+ font-size: var(--font-size-ui);
124
124
  line-height: 1.45;
125
125
  }
126
126
 
127
127
  .mdbase-connect-detail {
128
128
  margin-top: 10px;
129
129
  color: var(--color-text-muted);
130
- font-size: 12px;
130
+ font-size: var(--font-size-caption);
131
131
  }
132
132
 
133
133
  .mdbase-connect-detail code {
134
134
  display: block;
135
135
  margin-top: 6px;
136
136
  font-family: var(--mono);
137
- font-size: 11px;
137
+ font-size: var(--font-size-caption);
138
138
  overflow-wrap: anywhere;
139
139
  }
140
140
 
@@ -150,6 +150,6 @@
150
150
 
151
151
  .mdbase-connect-screen h1 {
152
152
  margin-top: 48px;
153
- font-size: 43px;
153
+ font-size: var(--font-size-title);
154
154
  }
155
155
  }
package/css/tokens.css CHANGED
@@ -44,16 +44,56 @@
44
44
  --app-editor: #005c88;
45
45
  --app-reader: #a4467f;
46
46
  --app-writer: #d55e00;
47
- --sans: "Atkinson Hyperlegible", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
47
+ --sans: "Atkinson Hyperlegible Next Variable", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
48
48
  --mono: "Azeret Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
49
- /* Motion: popovers and small entrances are quick and ease out; a settled
50
- confirmation holds, then fades away (see controls.css). */
49
+ /* Six steps: captions, UI chrome, section titles, prose, headings, document titles. */
50
+ --font-size-caption: 12px;
51
+ --font-size-ui: 13px;
52
+ --font-size-section: 15px;
53
+ --font-size-prose: 17px;
54
+ --font-size-heading: 24px;
55
+ --font-size-title: 34px;
56
+ --font-weight-regular: 400;
57
+ --font-weight-medium: 500;
58
+ --font-weight-semibold: 600;
59
+ --font-weight-bold: 700;
60
+ --radius-sm: 4px;
61
+ --radius-md: 8px;
62
+ --radius-lg: 12px;
63
+ --elevation-low: 0 4px 12px var(--color-shadow);
64
+ --elevation-high: 0 12px 32px var(--color-shadow);
65
+ --space-0: 0;
66
+ --space-1: 4px;
67
+ --space-2: 8px;
68
+ --space-3: 12px;
69
+ --space-4: 16px;
70
+ --space-6: 24px;
71
+ --space-8: 32px;
72
+ --space-12: 48px;
73
+ --space-16: 64px;
74
+ /* Transitions share durations and easing; settled confirmations hold then fade. */
51
75
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
76
+ --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
77
+ --motion-fast: 120ms;
52
78
  --motion-quick: 150ms;
53
79
  --motion-base: 180ms;
80
+ --motion-slow: 240ms;
81
+ --motion-spin: 700ms;
82
+ --motion-pulse: 1.1s;
54
83
  --motion-settle: 2.4s;
55
84
  }
56
85
 
86
+ @media (prefers-reduced-motion: reduce) {
87
+ :root {
88
+ --motion-fast: 0ms;
89
+ --motion-quick: 0ms;
90
+ --motion-base: 0ms;
91
+ --motion-slow: 0ms;
92
+ --motion-spin: 0ms;
93
+ --motion-pulse: 0ms;
94
+ }
95
+ }
96
+
57
97
  :root[data-theme="dark"] {
58
98
  color-scheme: dark;
59
99
  --color-canvas: oklch(17.5% 0.012 255);
package/dist/Feedback.js CHANGED
@@ -242,6 +242,6 @@ export function FeedbackProvider({ endpoint: configuredEndpoint, application, tu
242
242
  }
243
243
  }
244
244
  const config = feedbackTopics[topic];
245
- return _jsxs(FeedbackContext.Provider, { value: controls, children: [_jsx("div", { className: "mdbase-feedback-app", ref: content, children: children }), _jsx("span", { className: "mdbase-feedback-sr-only", role: "status", children: wiggle ? "A problem occurred. You can use Send feedback to report it." : "" }), endpoint && _jsxs("dialog", { ref: dialog, className: "mdbase-feedback-dialog", "aria-labelledby": `${id}-title`, onKeyDown: (event) => event.stopPropagation(), onCancel: (event) => { event.preventDefault(); close(); }, children: [_jsxs("header", { className: "mdbase-feedback-header", children: [_jsx("h2", { id: `${id}-title`, children: "Send feedback" }), _jsx("button", { type: "button", disabled: submitting, onClick: close, "aria-label": "Close feedback", children: "\u00D7" })] }), submitted ? _jsxs("section", { className: "mdbase-feedback-success", children: [_jsx("h3", { ref: successHeading, tabIndex: -1, children: feedbackTopics[submitted].thanks }), _jsx("p", { children: "Sent privately to the mdbase team." }), _jsx("button", { type: "button", className: "mdbase-feedback-primary", onClick: close, children: "Back to your work" })] }) : _jsxs("form", { onSubmit: (event) => { event.preventDefault(); void submit(); }, children: [_jsxs("div", { className: "mdbase-feedback-body", ref: body, children: [_jsxs("fieldset", { className: "mdbase-feedback-topics", disabled: submitting, children: [_jsx("legend", { className: "mdbase-feedback-sr-only", children: "Feedback type" }), Object.keys(feedbackTopics).map((value) => _jsxs("label", { children: [_jsx("input", { type: "radio", name: `${id}-topic`, checked: topic === value, onChange: () => setTopic(value) }), feedbackTopics[value].label] }, value))] }), _jsxs("label", { className: "mdbase-feedback-field", children: [_jsx("span", { children: config.prompt }), _jsx("textarea", { ref: messageField, required: true, maxLength: FEEDBACK_MAX_MESSAGE_LENGTH, rows: 5, placeholder: config.placeholder, value: message, disabled: submitting, onChange: (event) => setMessage(event.target.value) })] }), _jsxs("label", { className: "mdbase-feedback-field", children: [_jsxs("span", { children: ["Reply email ", _jsx("small", { children: "Optional" })] }), _jsx("input", { type: "email", maxLength: 320, autoComplete: "email", placeholder: "you@example.com", value: replyEmail, disabled: submitting, onChange: (event) => setReplyEmail(event.target.value) })] }), _jsxs("div", { className: "mdbase-feedback-screenshot", role: "group", "aria-label": "Screenshot", children: [screenshot && _jsxs(_Fragment, { children: [_jsxs("button", { className: "mdbase-feedback-preview", type: "button", disabled: imageBusy || submitting, onClick: () => { setIncludeScreenshot(false); setMarkup(true); }, "aria-label": "Mark up screenshot", children: [_jsx("img", { src: screenshotUrl(screenshot), alt: "Screenshot attached to your feedback" }), _jsx("span", { children: "Mark up screenshot" })] }), _jsxs("div", { className: "mdbase-feedback-screenshot-actions", children: [_jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: includeScreenshot, disabled: submitting, onChange: (event) => setIncludeScreenshot(event.target.checked) }), "Include screenshot"] }), _jsx("button", { type: "button", disabled: imageBusy || submitting, onClick: () => { setScreenshot(null); setScreenshotError(""); setCaptureStatus(""); }, children: "Remove" })] })] }), _jsxs("div", { className: "mdbase-feedback-screenshot-actions", children: [_jsx("button", { type: "button", disabled: imageBusy || submitting, onClick: () => void capture(), children: capturing ? "Taking screenshot…" : screenshot ? "Retake screenshot" : "Attach screenshot" }), _jsxs("label", { className: "mdbase-feedback-upload", children: [_jsx("span", { children: "Choose an image" }), _jsx("input", { type: "file", accept: "image/png,image/jpeg", disabled: imageBusy || submitting, onChange: (event) => { const file = event.target.files?.[0]; event.target.value = ""; if (file)
246
- void upload(file); } })] })] }), screenshot && _jsx("p", { className: "mdbase-feedback-help", children: "Review for private information before sending." }), captureStatus && _jsx("p", { className: "mdbase-feedback-help", role: "status", children: captureStatus }), screenshotError && _jsx("p", { className: "mdbase-feedback-error", role: "alert", children: screenshotError })] }), _jsxs("details", { className: "mdbase-feedback-details", children: [_jsx("summary", { children: "What gets sent" }), _jsx("p", { className: "mdbase-feedback-help", children: "Your message, any reply email and included screenshot, plus these app and build details." }), _jsx("pre", { children: JSON.stringify(application, null, 2) }), collectionName && _jsxs("label", { className: "mdbase-feedback-check", children: [_jsx("input", { type: "checkbox", checked: includeCollection, disabled: submitting, onChange: (event) => setIncludeCollection(event.target.checked) }), "Include collection name: ", collectionName] }), topic === "problem" && _jsxs(_Fragment, { children: [_jsxs("label", { className: "mdbase-feedback-check", children: [_jsx("input", { type: "checkbox", checked: includeDiagnostics, disabled: submitting, onChange: (event) => setIncludeDiagnostics(event.target.checked) }), "Include technical diagnostics"] }), _jsx("p", { className: "mdbase-feedback-help", children: "Browser, OS, viewport and recent error codes. No note contents, paths or credentials." }), includeDiagnostics && _jsx("pre", { children: JSON.stringify(diagnostics, null, 2) })] }), _jsx("p", { className: "mdbase-feedback-help", children: "Images: PNG or JPEG, up to 3 MB. Blackout replaces pixels; only the final image is attached." }), _jsx("p", { className: "mdbase-feedback-help", children: "Replies aren\u2019t guaranteed. Not for security reports or urgent support." })] }), turnstileSiteKey && open && !capturing && _jsx(FeedbackVerification, { siteKey: turnstileSiteKey, onToken: setTurnstileToken }, verificationAttempt), submitError && _jsx("p", { className: "mdbase-feedback-error", role: "alert", children: submitError }), _jsx("p", { className: "mdbase-feedback-help", children: "Private feedback to the mdbase team." })] }), _jsxs("footer", { className: "mdbase-feedback-actions", children: [_jsx("button", { type: "button", disabled: submitting, onClick: close, children: "Cancel" }), _jsx("button", { type: "submit", className: "mdbase-feedback-primary", disabled: submitting || imageBusy || !message.trim() || Boolean(turnstileSiteKey && !turnstileToken), children: submitting ? "Sending…" : "Send feedback" })] })] })] }), markup && screenshot && _jsx(FeedbackMarkup, { screenshot: screenshot, onClose: () => { setMarkup(false); setCaptureStatus("Markup discarded. Screenshot excluded."); }, onApply: (next) => { setScreenshot(next); setIncludeScreenshot(true); setMarkup(false); setCaptureStatus("Markup applied."); } })] });
245
+ return _jsxs(FeedbackContext.Provider, { value: controls, children: [_jsx("div", { className: "mdbase-feedback-app", ref: content, children: children }), _jsx("span", { className: "mdbase-feedback-sr-only", role: "status", children: wiggle ? "A problem occurred. You can use Send feedback to report it." : "" }), endpoint && _jsxs("dialog", { ref: dialog, className: "mdbase-feedback-dialog", "aria-labelledby": `${id}-title`, onKeyDown: (event) => event.stopPropagation(), onCancel: (event) => { event.preventDefault(); close(); }, children: [_jsxs("header", { className: "mdbase-feedback-header", children: [_jsx("h2", { id: `${id}-title`, children: "Send feedback" }), _jsx("button", { type: "button", disabled: submitting, onClick: close, "aria-label": "Close feedback", children: "\u00D7" })] }), submitted ? _jsxs("section", { className: "mdbase-feedback-success", children: [_jsx("h3", { ref: successHeading, tabIndex: -1, children: feedbackTopics[submitted].thanks }), _jsx("p", { children: "Sent privately to the mdbase team." }), _jsx("button", { type: "button", className: "mdbase-feedback-primary", onClick: close, children: "Back to your work" })] }) : _jsxs("form", { onSubmit: (event) => { event.preventDefault(); void submit(); }, children: [_jsxs("div", { className: "mdbase-feedback-body", ref: body, children: [_jsxs("fieldset", { className: "mdbase-feedback-topics", disabled: submitting, children: [_jsx("legend", { className: "mdbase-feedback-sr-only", children: "Feedback type" }), Object.keys(feedbackTopics).map((value) => _jsxs("label", { children: [_jsx("input", { type: "radio", name: `${id}-topic`, checked: topic === value, onChange: () => setTopic(value) }), feedbackTopics[value].label] }, value))] }), _jsxs("label", { className: "mdbase-feedback-field", children: [_jsx("span", { children: config.prompt }), _jsx("textarea", { ref: messageField, required: true, maxLength: FEEDBACK_MAX_MESSAGE_LENGTH, rows: 5, placeholder: config.placeholder, value: message, disabled: submitting, onChange: (event) => setMessage(event.target.value) })] }), _jsxs("label", { className: "mdbase-feedback-field", children: [_jsxs("span", { children: ["Reply email ", _jsx("small", { children: "Optional" })] }), _jsx("input", { type: "email", maxLength: 320, autoComplete: "email", placeholder: "you@example.com", value: replyEmail, disabled: submitting, onChange: (event) => setReplyEmail(event.target.value) })] }), _jsxs("div", { className: "mdbase-feedback-screenshot", role: "group", "aria-label": "Screenshot", children: [screenshot && _jsxs(_Fragment, { children: [_jsxs("button", { className: "mdbase-feedback-preview", type: "button", disabled: imageBusy || submitting, onClick: () => { setIncludeScreenshot(false); setMarkup(true); }, "aria-label": "Mark up screenshot", children: [_jsx("img", { src: screenshotUrl(screenshot), alt: "Screenshot attached to your feedback" }), _jsx("span", { children: "Mark up screenshot" })] }), _jsxs("div", { className: "mdbase-feedback-screenshot-actions", children: [_jsxs("label", { children: [_jsx("input", { className: "mdbase-checkbox", type: "checkbox", checked: includeScreenshot, disabled: submitting, onChange: (event) => setIncludeScreenshot(event.target.checked) }), "Include screenshot"] }), _jsx("button", { type: "button", disabled: imageBusy || submitting, onClick: () => { setScreenshot(null); setScreenshotError(""); setCaptureStatus(""); }, children: "Remove" })] })] }), _jsxs("div", { className: "mdbase-feedback-screenshot-actions", children: [_jsx("button", { type: "button", disabled: imageBusy || submitting, onClick: () => void capture(), children: capturing ? "Taking screenshot…" : screenshot ? "Retake screenshot" : "Attach screenshot" }), _jsxs("label", { className: "mdbase-feedback-upload", children: [_jsx("span", { children: "Choose an image" }), _jsx("input", { type: "file", accept: "image/png,image/jpeg", disabled: imageBusy || submitting, onChange: (event) => { const file = event.target.files?.[0]; event.target.value = ""; if (file)
246
+ void upload(file); } })] })] }), screenshot && _jsx("p", { className: "mdbase-feedback-help", children: "Review for private information before sending." }), captureStatus && _jsx("p", { className: "mdbase-feedback-help", role: "status", children: captureStatus }), screenshotError && _jsx("p", { className: "mdbase-feedback-error", role: "alert", children: screenshotError })] }), _jsxs("details", { className: "mdbase-feedback-details", children: [_jsx("summary", { children: "What gets sent" }), _jsx("p", { className: "mdbase-feedback-help", children: "Your message, any reply email and included screenshot, plus these app and build details." }), _jsx("pre", { children: JSON.stringify(application, null, 2) }), collectionName && _jsxs("label", { className: "mdbase-feedback-check", children: [_jsx("input", { className: "mdbase-checkbox", type: "checkbox", checked: includeCollection, disabled: submitting, onChange: (event) => setIncludeCollection(event.target.checked) }), "Include collection name: ", collectionName] }), topic === "problem" && _jsxs(_Fragment, { children: [_jsxs("label", { className: "mdbase-feedback-check", children: [_jsx("input", { className: "mdbase-checkbox", type: "checkbox", checked: includeDiagnostics, disabled: submitting, onChange: (event) => setIncludeDiagnostics(event.target.checked) }), "Include technical diagnostics"] }), _jsx("p", { className: "mdbase-feedback-help", children: "Browser, OS, viewport and recent error codes. No note contents, paths or credentials." }), includeDiagnostics && _jsx("pre", { children: JSON.stringify(diagnostics, null, 2) })] }), _jsx("p", { className: "mdbase-feedback-help", children: "Images: PNG or JPEG, up to 3 MB. Blackout replaces pixels; only the final image is attached." }), _jsx("p", { className: "mdbase-feedback-help", children: "Replies aren\u2019t guaranteed. Not for security reports or urgent support." })] }), turnstileSiteKey && open && !capturing && _jsx(FeedbackVerification, { siteKey: turnstileSiteKey, onToken: setTurnstileToken }, verificationAttempt), submitError && _jsx("p", { className: "mdbase-feedback-error", role: "alert", children: submitError }), _jsx("p", { className: "mdbase-feedback-help", children: "Private feedback to the mdbase team." })] }), _jsxs("footer", { className: "mdbase-feedback-actions", children: [_jsx("button", { type: "button", disabled: submitting, onClick: close, children: "Cancel" }), _jsx("button", { type: "submit", className: "mdbase-feedback-primary", disabled: submitting || imageBusy || !message.trim() || Boolean(turnstileSiteKey && !turnstileToken), children: submitting ? "Sending…" : "Send feedback" })] })] })] }), markup && screenshot && _jsx(FeedbackMarkup, { screenshot: screenshot, onClose: () => { setMarkup(false); setCaptureStatus("Markup discarded. Screenshot excluded."); }, onApply: (next) => { setScreenshot(next); setIncludeScreenshot(true); setMarkup(false); setCaptureStatus("Markup applied."); } })] });
247
247
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mdbase-dev/ui",
3
- "version": "0.1.0-beta.125",
3
+ "version": "0.1.0-beta.126",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -93,7 +93,7 @@
93
93
  "README.md"
94
94
  ],
95
95
  "dependencies": {
96
- "@fontsource/atkinson-hyperlegible": "^5.3.0",
96
+ "@fontsource-variable/atkinson-hyperlegible-next": "^5.3.0",
97
97
  "@fontsource/azeret-mono": "^5.3.0"
98
98
  },
99
99
  "peerDependencies": {
@@ -107,7 +107,7 @@
107
107
  "react": "^19.3.0",
108
108
  "react-dom": "^19.3.0",
109
109
  "typescript": "^7.0.2",
110
- "vitest": "^4.1.10"
110
+ "vitest": "^4.1.11"
111
111
  },
112
112
  "peerDependenciesMeta": {
113
113
  "@codemirror/view": {