@tonycodes-ui/mortar 0.0.0-stage → 0.12.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.
Files changed (107) hide show
  1. package/AGENTS.md +10 -0
  2. package/CHANGELOG.md +268 -0
  3. package/CONTRIBUTING.md +60 -0
  4. package/DESIGN.md +51 -0
  5. package/LICENSE +21 -0
  6. package/README.md +155 -2
  7. package/TESTING.md +83 -0
  8. package/baseline-policy.json +16 -0
  9. package/bin/mortar.mjs +258 -0
  10. package/catalog.json +9900 -0
  11. package/cli/audit.mjs +943 -0
  12. package/cli/data.mjs +176 -0
  13. package/cli/mcp.mjs +216 -0
  14. package/cli/migrations.mjs +128 -0
  15. package/cli/packet.mjs +267 -0
  16. package/cli/search.mjs +246 -0
  17. package/cli/tools.mjs +229 -0
  18. package/design-principles.json +30 -0
  19. package/dist/agent-data.json +1 -0
  20. package/dist/application.d.ts +340 -0
  21. package/dist/application.js +240 -0
  22. package/dist/auth.d.ts +121 -0
  23. package/dist/auth.js +97 -0
  24. package/dist/brand.d.ts +69 -0
  25. package/dist/brand.js +266 -0
  26. package/dist/calendar.d.ts +75 -0
  27. package/dist/calendar.js +310 -0
  28. package/dist/choice.d.ts +36 -0
  29. package/dist/choice.js +36 -0
  30. package/dist/composer.d.ts +28 -0
  31. package/dist/composer.js +47 -0
  32. package/dist/display.d.ts +136 -0
  33. package/dist/display.js +75 -0
  34. package/dist/docs.d.ts +145 -0
  35. package/dist/docs.js +141 -0
  36. package/dist/ecommerce.d.ts +261 -0
  37. package/dist/ecommerce.js +177 -0
  38. package/dist/feedback.d.ts +80 -0
  39. package/dist/feedback.js +98 -0
  40. package/dist/foundation.d.ts +42 -0
  41. package/dist/foundation.js +121 -0
  42. package/dist/heading.d.ts +5 -0
  43. package/dist/heading.js +9 -0
  44. package/dist/icons.d.ts +50 -0
  45. package/dist/icons.js +70 -0
  46. package/dist/illustration.d.ts +47 -0
  47. package/dist/illustration.js +47 -0
  48. package/dist/index.d.ts +36 -0
  49. package/dist/index.js +18 -0
  50. package/dist/inspection.d.ts +24 -0
  51. package/dist/inspection.js +14 -0
  52. package/dist/internal.d.ts +514 -0
  53. package/dist/internal.js +332 -0
  54. package/dist/layout.d.ts +107 -0
  55. package/dist/layout.js +66 -0
  56. package/dist/marketing-sections.d.ts +161 -0
  57. package/dist/marketing-sections.js +58 -0
  58. package/dist/marketing.d.ts +277 -0
  59. package/dist/marketing.js +163 -0
  60. package/dist/navigation.d.ts +35 -0
  61. package/dist/navigation.js +44 -0
  62. package/dist/primitives.d.ts +258 -0
  63. package/dist/primitives.js +244 -0
  64. package/dist/showcase.d.ts +29 -0
  65. package/dist/showcase.js +10 -0
  66. package/dist/workspace.d.ts +80 -0
  67. package/dist/workspace.js +48 -0
  68. package/docs/agents.md +127 -0
  69. package/docs/auth-flows.md +71 -0
  70. package/docs/migrating-to-0.6.md +94 -0
  71. package/docs/theming.md +170 -0
  72. package/package.json +94 -4
  73. package/skills/mortar/SKILL.md +36 -0
  74. package/src/application.css +1487 -0
  75. package/src/application.tsx +1413 -0
  76. package/src/auth.tsx +452 -0
  77. package/src/brand.ts +335 -0
  78. package/src/calendar.css +468 -0
  79. package/src/calendar.tsx +868 -0
  80. package/src/choice.tsx +240 -0
  81. package/src/composer.tsx +158 -0
  82. package/src/data.css +690 -0
  83. package/src/display.tsx +452 -0
  84. package/src/docs.css +704 -0
  85. package/src/docs.tsx +737 -0
  86. package/src/ecommerce.css +810 -0
  87. package/src/ecommerce.tsx +1033 -0
  88. package/src/feedback.tsx +388 -0
  89. package/src/foundation.css +1867 -0
  90. package/src/foundation.tsx +227 -0
  91. package/src/heading.ts +13 -0
  92. package/src/icons.tsx +237 -0
  93. package/src/illustration.tsx +169 -0
  94. package/src/index.ts +233 -0
  95. package/src/inspection.tsx +91 -0
  96. package/src/internal.tsx +700 -0
  97. package/src/layout.tsx +342 -0
  98. package/src/marketing-sections.tsx +621 -0
  99. package/src/marketing.css +2579 -0
  100. package/src/marketing.tsx +1235 -0
  101. package/src/navigation.tsx +118 -0
  102. package/src/overlays.css +350 -0
  103. package/src/primitives.tsx +1135 -0
  104. package/src/showcase.tsx +120 -0
  105. package/src/tokens.css +449 -0
  106. package/src/workspace.tsx +333 -0
  107. package/styles.css +56 -0
@@ -0,0 +1,1867 @@
1
+ /* Foundation: document scope, shared patterns, tone and controls.
2
+ Every rule sits in @layer components, so equal-specificity rules resolve by source
3
+ order. styles.css loads tokens, foundation, overlays, application, data, calendar,
4
+ marketing, docs and ecommerce in that order; inside a file each component keeps
5
+ one section, ordered base, then variants and states, then media and container
6
+ queries, except where that would change which equal-specificity rule wins. */
7
+ @layer components {
8
+ /* == Document scope == */
9
+ /* Document scope paints the page. Container scope paints the wrapper. */
10
+ [data-mortar]:not(:root) {
11
+ background: var(--m-paper);
12
+ color: var(--m-ink);
13
+ }
14
+ [data-mortar] body {
15
+ margin: 0;
16
+ background: var(--m-paper);
17
+ color: var(--m-ink);
18
+ font-family: var(--m-brand-font, var(--m-font));
19
+ font-size: 15px;
20
+ line-height: 1.5;
21
+ -webkit-font-smoothing: antialiased;
22
+ }
23
+ [data-mortar] :where(button, input, select, textarea) {
24
+ font: inherit;
25
+ color: inherit;
26
+ }
27
+ [data-mortar]
28
+ :where(
29
+ button,
30
+ a,
31
+ input,
32
+ select,
33
+ textarea,
34
+ summary,
35
+ [tabindex]
36
+ ):focus-visible {
37
+ outline: 2px solid var(--m-focus);
38
+ outline-offset: var(--m-focus-offset);
39
+ }
40
+ /* Zero-specificity host so any component class can set a link's colour. */
41
+ :where([data-mortar]) a {
42
+ color: var(--m-link);
43
+ }
44
+ [data-mortar] :where(h1, h2, h3, p, figure, dl) {
45
+ margin: 0;
46
+ }
47
+ /* Brand fonts. Without a brand font each declaration is invalid, so the
48
+ element keeps the font it inherits: nothing changes until a brand sets
49
+ one. A container brand carries its body font; headings and component
50
+ titles take the brand's heading font, else --m-font-heading. Zero
51
+ specificity, so a component's own font (e.g. a mono title) still wins. */
52
+ :where([data-mortar-brand]:not(:root)) {
53
+ font-family: var(--m-brand-font);
54
+ }
55
+ :where(
56
+ [data-mortar] :is(h1, h2, h3, h4, h5, h6),
57
+ .m-page-title,
58
+ .m-section-title,
59
+ .m-card-title,
60
+ .m-settings-title,
61
+ .m-sign-in-title,
62
+ .m-auth-title,
63
+ .m-post-title,
64
+ .m-calendar-title,
65
+ .m-stacked-title
66
+ ) {
67
+ font-family: var(--m-brand-font-heading, var(--m-font-heading));
68
+ }
69
+ .m-required {
70
+ margin-inline-start: 2px;
71
+ color: var(--m-danger);
72
+ }
73
+ /* Carries a container-scoped theme into portalled overlays without a box. */
74
+ .m-theme-scope {
75
+ display: contents;
76
+ }
77
+
78
+ /* == Shared patterns == */
79
+ /* Shared patterns. Zero specificity, so every component rule can override them. */
80
+ /* List resets for every component list. */
81
+ :where(
82
+ .m-breadcrumbs ol,
83
+ .m-activity,
84
+ .m-logo-cloud ul,
85
+ .m-pricing-grid ul,
86
+ .m-avatar-group,
87
+ .m-toaster ol,
88
+ .m-stacked-list ul,
89
+ .m-bento,
90
+ .m-post-list,
91
+ .m-team-grid,
92
+ .m-steps,
93
+ .m-file-tree ul,
94
+ .m-link-cards,
95
+ .m-process-steps,
96
+ .m-check-list,
97
+ .m-chat-log,
98
+ .m-chat-suggestions,
99
+ .m-choice-options,
100
+ .m-grid-list,
101
+ .m-calendar-events,
102
+ .m-flyout-panel ul,
103
+ .m-product-list,
104
+ .m-cart,
105
+ .m-order-items,
106
+ .m-calendar-allday,
107
+ .m-calendar-slots,
108
+ .m-icon-links,
109
+ .m-heading-meta,
110
+ .m-attachments,
111
+ .m-progress-steps ol,
112
+ .m-tab-nav ul,
113
+ .m-product-swatches
114
+ ) {
115
+ margin: 0;
116
+ padding: 0;
117
+ list-style: none;
118
+ }
119
+ /* Icons: a host sets --m-icon-size (default --m-icon); every svg inside it follows.
120
+ Hosts reset the size so a nested host never inherits a larger parent size. */
121
+ :where(
122
+ .m-select-control,
123
+ .m-button-group-icon,
124
+ .m-choice-control,
125
+ .m-combobox-toggle,
126
+ .m-choice-leading,
127
+ .m-nav-icon,
128
+ .m-banner > button,
129
+ .m-input-group > .m-input-icon,
130
+ .m-calendar-nav,
131
+ .m-flyout-trigger,
132
+ .m-flyout-icon,
133
+ .m-rating,
134
+ .m-stars,
135
+ .m-button-icon,
136
+ .m-menu-icon,
137
+ .m-notice-dismiss,
138
+ .m-empty-icon,
139
+ .m-tab-icon,
140
+ .m-icon-links,
141
+ .m-metric-icon,
142
+ .m-heading-meta,
143
+ .m-palette-icon,
144
+ .m-attachment-icon,
145
+ .m-progress-steps[data-variant="circles"] .m-progress-step-marker,
146
+ .m-feature-icon[data-marker="icon"],
147
+ .m-check-list,
148
+ .m-sort-button,
149
+ .m-grid-list[data-actions="split"] .m-grid-list-actions,
150
+ .m-metrics span[data-trend]
151
+ ) {
152
+ --m-icon-size: var(--m-icon);
153
+ }
154
+ :where(
155
+ .m-select-control,
156
+ .m-button-group-icon,
157
+ .m-choice-control,
158
+ .m-combobox-toggle,
159
+ .m-choice-leading,
160
+ .m-nav-icon,
161
+ .m-banner > button,
162
+ .m-input-group > .m-input-icon,
163
+ .m-calendar-nav,
164
+ .m-flyout-trigger,
165
+ .m-flyout-icon,
166
+ .m-rating,
167
+ .m-stars,
168
+ .m-button-icon,
169
+ .m-menu-icon,
170
+ .m-notice-dismiss,
171
+ .m-empty-icon,
172
+ .m-tab-icon,
173
+ .m-icon-links,
174
+ .m-metric-icon,
175
+ .m-heading-meta,
176
+ .m-palette-icon,
177
+ .m-attachment-icon,
178
+ .m-progress-steps[data-variant="circles"] .m-progress-step-marker,
179
+ .m-feature-icon[data-marker="icon"],
180
+ .m-check-list,
181
+ .m-sort-button,
182
+ .m-grid-list[data-actions="split"] .m-grid-list-actions,
183
+ .m-metrics span[data-trend]
184
+ )
185
+ svg {
186
+ width: var(--m-icon-size);
187
+ height: var(--m-icon-size);
188
+ }
189
+ /* Borderless buttons: one reset; each component adds size, colour and type. */
190
+ :where(
191
+ .m-chip button,
192
+ .m-code-bar [role="tab"],
193
+ .m-code-copy,
194
+ .m-sort-button,
195
+ .m-combobox-toggle,
196
+ .m-banner > button,
197
+ .m-calendar-nav button,
198
+ .m-calendar td > button,
199
+ .m-flyout-trigger,
200
+ .m-cart-remove,
201
+ .m-filter-clear,
202
+ .m-menu :is(button, a),
203
+ .m-notice-dismiss,
204
+ .m-pagination-pages button
205
+ ) {
206
+ border: 0;
207
+ background: none;
208
+ cursor: pointer;
209
+ }
210
+ /* Ghost icon buttons (dismiss, previous/next, toggles): one size and hover. */
211
+ :where(
212
+ .m-banner > button,
213
+ .m-calendar-nav button,
214
+ .m-notice-dismiss,
215
+ .m-combobox-toggle
216
+ ) {
217
+ display: inline-grid;
218
+ place-items: center;
219
+ flex-shrink: 0;
220
+ width: var(--m-icon-button, 32px);
221
+ height: var(--m-icon-button, 32px);
222
+ min-width: var(--m-touch-target);
223
+ min-height: var(--m-touch-target);
224
+ border-radius: var(--m-radius-sm);
225
+ }
226
+ /* Class specificity: the host reset gives buttons color: inherit. */
227
+ .m-banner > button,
228
+ .m-calendar-nav button {
229
+ color: var(--m-secondary);
230
+ }
231
+ :where(
232
+ .m-banner > button,
233
+ .m-calendar-nav button,
234
+ .m-notice-dismiss,
235
+ .m-combobox-toggle
236
+ ):hover {
237
+ background: var(--m-sunken);
238
+ }
239
+ /* Card surface: bordered, large radius, surface background. */
240
+ :where(
241
+ .m-dialog-panel,
242
+ .m-metrics,
243
+ .m-data-table,
244
+ .m-toast,
245
+ .m-palette,
246
+ .m-stacked-list,
247
+ .m-card,
248
+ .m-action-panel,
249
+ .m-grid-list > li,
250
+ .m-flyout-panel,
251
+ .m-order,
252
+ .m-calendar-timegrid,
253
+ .m-product-list[data-variant="cards"] > li,
254
+ .m-mock-window
255
+ ) {
256
+ border: 1px solid var(--m-line);
257
+ border-radius: var(--m-radius-lg);
258
+ background: var(--m-surface);
259
+ }
260
+ /* Titles keep their style at any heading level; h4–h6 lose the UA margins. */
261
+ :where(
262
+ .m-section-title,
263
+ .m-post-title,
264
+ .m-release-version,
265
+ .m-release-type,
266
+ .m-api-title,
267
+ .m-api-params-title
268
+ ) {
269
+ margin: 0;
270
+ }
271
+
272
+ /* == Tabs and TabNav == */
273
+ /* Tabs, styled once for Tabs ([role=tab] buttons) and TabNav (links between pages).
274
+ A tab is selected by aria-selected (Tabs) or aria-current (TabNav). */
275
+ .m-tabs {
276
+ --m-tab-gap: 24px;
277
+ --m-tab-pad-x: 0px;
278
+ }
279
+ .m-tab-nav {
280
+ --m-tab-gap: 20px;
281
+ --m-tab-pad-x: 4px;
282
+ min-width: 0;
283
+ }
284
+ :is(.m-tabs, .m-tab-nav ul) {
285
+ display: flex;
286
+ gap: var(--m-tab-gap);
287
+ border-bottom: 1px solid var(--m-line);
288
+ overflow-x: auto;
289
+ }
290
+ :is(.m-tabs [role="tab"], .m-tab-nav a) {
291
+ display: inline-flex;
292
+ align-items: center;
293
+ gap: 8px;
294
+ min-height: var(--m-touch-target);
295
+ padding: 12px var(--m-tab-pad-x);
296
+ border: 0;
297
+ border-bottom: 2px solid transparent;
298
+ background: none;
299
+ color: var(--m-muted);
300
+ font-size: var(--m-font-size);
301
+ font-weight: var(--m-weight-semibold);
302
+ text-decoration: none;
303
+ white-space: nowrap;
304
+ cursor: pointer;
305
+ }
306
+ .m-tab-icon {
307
+ display: inline-flex;
308
+ }
309
+ /* Count pills: tab counts and the active-filter count share one shape. */
310
+ :is(.m-tab-count, .m-filter-active) {
311
+ display: inline-grid;
312
+ place-items: center;
313
+ min-width: 22px;
314
+ height: 20px;
315
+ padding: 0 7px;
316
+ border-radius: var(--m-radius-full);
317
+ font-size: 12px;
318
+ font-variant-numeric: tabular-nums;
319
+ }
320
+ .m-tab-count {
321
+ background: var(--m-sunken);
322
+ color: var(--m-secondary);
323
+ }
324
+ :is(.m-tabs [role="tab"], .m-tab-nav a):hover:not(
325
+ [aria-selected="true"],
326
+ [aria-current="page"]
327
+ ) {
328
+ color: var(--m-ink);
329
+ border-bottom-color: var(--m-line);
330
+ }
331
+ .m-tab-nav a:focus-visible {
332
+ outline-offset: -2px;
333
+ }
334
+ :is(.m-tabs [aria-selected="true"], .m-tab-nav a[aria-current="page"]) {
335
+ color: var(--m-link);
336
+ border-bottom-color: var(--m-link);
337
+ }
338
+ :is(.m-tabs[data-variant="pills"], .m-tab-nav[data-variant="pills"] ul) {
339
+ gap: 4px;
340
+ border-bottom: 0;
341
+ }
342
+ :is(
343
+ .m-tabs[data-variant="pills"] [role="tab"],
344
+ .m-tab-nav[data-variant="pills"] a
345
+ ) {
346
+ padding: 8px 12px;
347
+ border: 0;
348
+ border-radius: var(--m-radius);
349
+ }
350
+ :is(
351
+ .m-tabs[data-variant="pills"] [aria-selected="true"],
352
+ .m-tab-nav[data-variant="pills"] a[aria-current="page"]
353
+ ) {
354
+ background: var(--m-selected);
355
+ }
356
+ :is(
357
+ .m-tabs[data-full-width]:not([data-variant]),
358
+ .m-tab-nav[data-full-width]:not([data-variant]) ul
359
+ ) {
360
+ gap: 0;
361
+ }
362
+ .m-tab-nav[data-full-width] li {
363
+ display: flex;
364
+ flex: 1;
365
+ }
366
+ :is(.m-tabs[data-full-width] [role="tab"], .m-tab-nav[data-full-width] a) {
367
+ flex: 1;
368
+ justify-content: center;
369
+ }
370
+ :is(.m-tabs [aria-selected="true"], .m-tab-nav a[aria-current="page"])
371
+ .m-tab-count,
372
+ .m-filter-active {
373
+ background: var(--m-primary);
374
+ color: var(--m-on-primary);
375
+ }
376
+ :where(.m-tabs) ~ [role="tabpanel"]:not([hidden]) {
377
+ padding-top: 24px;
378
+ }
379
+
380
+ /* == Forced colours: selected and current states ==
381
+ Kept after Tabs and before Button and the other controls: equal-specificity rules
382
+ on both sides depend on this position. */
383
+ /* Forced colours drop backgrounds and shadows, so selected and current states
384
+ use system highlight colours (and today's date an outline) instead. */
385
+ @media (forced-colors: active) {
386
+ :where([data-mortar], [class^="m-"], [class*=" m-"])
387
+ :is(
388
+ button[aria-pressed="true"],
389
+ [role="tab"][aria-selected="true"],
390
+ [role="option"][aria-selected="true"],
391
+ .m-pagination-pages [aria-current],
392
+ .m-chip[data-selected],
393
+ .m-calendar td[aria-selected="true"] > button,
394
+ .m-calendar-column-heading[aria-current="date"] strong,
395
+ .m-data-table tr[data-selected] > *,
396
+ .m-progress-steps [data-status="current"] .m-progress-step-marker
397
+ ),
398
+ :where([data-mortar], [class^="m-"], [class*=" m-"])
399
+ :is(nav, [role="navigation"])
400
+ a[aria-current]:not([aria-current="false"]) {
401
+ forced-color-adjust: none;
402
+ background: Highlight;
403
+ color: HighlightText;
404
+ border-color: Highlight;
405
+ }
406
+ }
407
+
408
+ /* == Button and ActionLink == */
409
+ .m-button {
410
+ display: inline-flex;
411
+ align-items: center;
412
+ justify-content: center;
413
+ gap: 8px;
414
+ min-height: var(--m-control);
415
+ padding: 4px var(--m-pad);
416
+ border: 1px solid transparent;
417
+ border-radius: var(--m-radius);
418
+ font-size: var(--m-font-size);
419
+ font-weight: var(--m-weight-semibold);
420
+ line-height: 1.35;
421
+ text-decoration: none;
422
+ cursor: pointer;
423
+ }
424
+ .m-button-primary {
425
+ background: var(--m-primary);
426
+ color: var(--m-on-primary);
427
+ border-color: var(--m-primary);
428
+ }
429
+ .m-button-secondary {
430
+ background: var(--m-surface);
431
+ color: var(--m-ink);
432
+ border-color: var(--m-line-strong);
433
+ }
434
+ .m-button-ghost {
435
+ background: transparent;
436
+ color: var(--m-secondary);
437
+ }
438
+ .m-button-danger {
439
+ background: transparent;
440
+ color: var(--m-danger);
441
+ border-color: var(--m-danger);
442
+ }
443
+ /* Accent: the brand's accent fill (its gradient, or flat accent) under
444
+ on-accent text. Hover and active lay a wash over the fill that moves away
445
+ from the text: white under dark on-accent text, black under light. So
446
+ contrast only rises, on every stop, whatever the brand's colours. */
447
+ .m-button-accent {
448
+ background-color: var(--m-accent);
449
+ background-image: var(--m-accent-fill);
450
+ color: var(--m-on-accent);
451
+ border-color: transparent;
452
+ }
453
+ .m-spinner {
454
+ width: 13px;
455
+ height: 13px;
456
+ border: 2px solid currentColor;
457
+ border-right-color: transparent;
458
+ border-radius: 50%;
459
+ animation: m-spin 0.8s linear infinite;
460
+ }
461
+ .m-button-icon {
462
+ display: inline-flex;
463
+ flex-shrink: 0;
464
+ }
465
+ .m-button-primary:hover {
466
+ background: var(--m-primary-hover);
467
+ }
468
+ .m-button-primary:active {
469
+ background: var(--m-primary-active);
470
+ }
471
+ /* --m-accent-wash (tokens.css) moves the fill away from the on-accent text;
472
+ where it is unset the mix is transparent and the plain fill stays. */
473
+ .m-button-accent:hover {
474
+ background-image:
475
+ linear-gradient(
476
+ color-mix(in oklab, var(--m-accent-wash, transparent) 15%, transparent),
477
+ color-mix(in oklab, var(--m-accent-wash, transparent) 15%, transparent)
478
+ ),
479
+ var(--m-accent-fill);
480
+ }
481
+ .m-button-accent:active {
482
+ background-image:
483
+ linear-gradient(
484
+ color-mix(in oklab, var(--m-accent-wash, transparent) 28%, transparent),
485
+ color-mix(in oklab, var(--m-accent-wash, transparent) 28%, transparent)
486
+ ),
487
+ var(--m-accent-fill);
488
+ }
489
+ .m-button-secondary:hover,
490
+ .m-button-ghost:hover {
491
+ background: var(--m-sunken);
492
+ }
493
+ .m-button-danger:hover {
494
+ background: var(--m-sunken);
495
+ }
496
+ .m-button:active:not(:disabled, [aria-disabled="true"]) {
497
+ transform: translateY(1px);
498
+ }
499
+ .m-button:is(:disabled, [aria-disabled="true"]) {
500
+ background: var(--m-sunken);
501
+ color: var(--m-muted);
502
+ border: 1px dashed var(--m-line-strong);
503
+ cursor: not-allowed;
504
+ }
505
+ .m-button-danger[data-solid]:not(:disabled, [aria-disabled="true"]) {
506
+ background: var(--m-danger);
507
+ color: var(--m-on-feedback);
508
+ }
509
+ /* Sizes and icon-only buttons carry :is(button, a) (a .m-button is always one) so they outrank equal-specificity context rules that load later: Toast, InputGroup action, ProductOverview form and OrderSummary action. */
510
+ :is(button, a).m-button[data-size="sm"] {
511
+ min-height: 30px;
512
+ padding: 2px 10px;
513
+ font-size: 13px;
514
+ }
515
+ :is(button, a).m-button[data-size="lg"] {
516
+ min-height: 44px;
517
+ padding: 6px 20px;
518
+ font-size: 15px;
519
+ }
520
+ :is(button, a).m-button[data-icon-only] {
521
+ min-width: var(--m-control);
522
+ padding-inline: 0;
523
+ }
524
+ .m-button[data-icon-only][data-size="sm"] {
525
+ min-width: 30px;
526
+ }
527
+ .m-button[data-icon-only][data-size="lg"] {
528
+ min-width: 44px;
529
+ }
530
+
531
+ /* == Fields: Input, TextArea and Select == */
532
+ .m-field {
533
+ display: flex;
534
+ flex-direction: column;
535
+ gap: 6px;
536
+ min-width: 0;
537
+ }
538
+ .m-field > label,
539
+ .m-radio-group > legend {
540
+ font-size: 13px;
541
+ font-weight: var(--m-weight-semibold);
542
+ }
543
+ .m-field p {
544
+ font-size: 12px;
545
+ color: var(--m-muted);
546
+ }
547
+ .m-input-group {
548
+ display: flex;
549
+ align-items: stretch;
550
+ border: 1px solid var(--m-line-strong);
551
+ border-radius: var(--m-radius);
552
+ background: var(--m-surface);
553
+ overflow: hidden;
554
+ }
555
+ .m-input-group input {
556
+ min-width: 0;
557
+ width: 100%;
558
+ min-height: var(--m-control);
559
+ padding: 4px var(--m-pad);
560
+ border: 0;
561
+ background: transparent;
562
+ font-size: var(--m-font-size);
563
+ outline: none;
564
+ }
565
+ .m-input-group > span {
566
+ display: flex;
567
+ align-items: center;
568
+ background: var(--m-sunken);
569
+ padding: 0 10px;
570
+ color: var(--m-muted);
571
+ font: 12px var(--m-mono);
572
+ }
573
+ .m-field select {
574
+ min-height: var(--m-control);
575
+ }
576
+ .m-field select,
577
+ .m-field textarea {
578
+ padding: 6px var(--m-pad);
579
+ border: 1px solid var(--m-line-strong);
580
+ border-radius: var(--m-radius);
581
+ background: var(--m-surface);
582
+ font-size: var(--m-font-size);
583
+ }
584
+ .m-select-control {
585
+ position: relative;
586
+ display: grid;
587
+ min-width: 0;
588
+ }
589
+ .m-select-control select {
590
+ appearance: none;
591
+ width: 100%;
592
+ min-width: 0;
593
+ line-height: 1.4;
594
+ }
595
+ .m-select-control svg {
596
+ position: absolute;
597
+ inset-inline-end: var(--m-pad);
598
+ top: 50%;
599
+ transform: translateY(-50%);
600
+ color: var(--m-secondary);
601
+ pointer-events: none;
602
+ }
603
+ @media (forced-colors: active) {
604
+ .m-select-control select {
605
+ appearance: auto;
606
+ }
607
+ .m-select-control svg {
608
+ display: none;
609
+ }
610
+ }
611
+ .m-field textarea {
612
+ min-height: 110px;
613
+ resize: vertical;
614
+ }
615
+ .m-input-group > .m-input-icon:first-child {
616
+ padding-inline-start: var(--m-pad);
617
+ }
618
+ .m-input-group > input + .m-input-icon {
619
+ padding-inline-end: var(--m-pad);
620
+ }
621
+ .m-input-action {
622
+ display: flex;
623
+ flex-shrink: 0;
624
+ border-inline-start: 1px solid var(--m-line-strong);
625
+ }
626
+ .m-input-group:focus-within {
627
+ outline: 2px solid var(--m-focus);
628
+ outline-offset: 2px;
629
+ }
630
+ .m-input-group input:focus-visible {
631
+ outline: none;
632
+ }
633
+ .m-input-group[data-invalid],
634
+ .m-field [aria-invalid="true"] {
635
+ border-color: var(--m-danger);
636
+ }
637
+ .m-input-action > :is(button, a, .m-button) {
638
+ min-height: 100%;
639
+ border: 0;
640
+ border-radius: 0;
641
+ }
642
+ .m-select-control:not([data-listbox]) select {
643
+ height: var(--m-control);
644
+ padding-block: 0;
645
+ padding-inline-end: calc(var(--m-pad) + 24px);
646
+ cursor: pointer;
647
+ }
648
+ .m-select-control select:hover:not(:disabled):not([aria-invalid="true"]) {
649
+ border-color: var(--m-secondary);
650
+ }
651
+ .m-select-control:has(select:disabled) svg {
652
+ color: var(--m-muted);
653
+ }
654
+ .m-field :disabled {
655
+ color: var(--m-muted);
656
+ background: var(--m-sunken);
657
+ cursor: not-allowed;
658
+ }
659
+ .m-input-group > .m-input-icon {
660
+ padding: 0;
661
+ background: transparent;
662
+ color: var(--m-muted);
663
+ font: inherit;
664
+ }
665
+ .m-field[data-disabled] .m-choice-control {
666
+ color: var(--m-muted);
667
+ background: var(--m-sunken);
668
+ cursor: not-allowed;
669
+ }
670
+ .m-field[data-disabled] .m-choice-control :is(input, button) {
671
+ cursor: not-allowed;
672
+ }
673
+ .m-input-group[data-leading] > .m-input-icon + input {
674
+ padding-inline-start: 8px;
675
+ }
676
+ .m-input-group[data-trailing] > input:has(+ .m-input-icon) {
677
+ padding-inline-end: 8px;
678
+ }
679
+ .m-input-action > :is(button, a, .m-button):focus-visible {
680
+ outline-offset: -2px;
681
+ }
682
+ @media (max-width: 560px) {
683
+ .m-input-group input,
684
+ .m-field select,
685
+ .m-field textarea {
686
+ font-size: 16px;
687
+ }
688
+ }
689
+
690
+ /* == Badge == */
691
+ /* Tone: each status value sets --m-tone once; components read it. */
692
+ :where(.m-badge, .m-notice) {
693
+ --m-tone: var(--m-secondary);
694
+ }
695
+ .m-badge {
696
+ display: inline-flex;
697
+ align-items: center;
698
+ padding: 2px 8px;
699
+ gap: 6px;
700
+ font-size: 12px;
701
+ font-weight: var(--m-weight-semibold);
702
+ border: 1px solid var(--m-line);
703
+ border-radius: var(--m-radius-full);
704
+ }
705
+ .m-badge::before {
706
+ content: "";
707
+ width: 6px;
708
+ height: 6px;
709
+ border-radius: 50%;
710
+ background: currentColor;
711
+ }
712
+
713
+ /* == Tone == */
714
+ :where([data-tone="success"]) {
715
+ --m-tone: var(--m-success);
716
+ }
717
+ :where([data-tone="warning"]) {
718
+ --m-tone: var(--m-warning);
719
+ }
720
+ :where([data-tone="danger"], .m-error) {
721
+ --m-tone: var(--m-danger);
722
+ }
723
+ :where([data-tone="info"]) {
724
+ --m-tone: var(--m-info);
725
+ }
726
+ :is(.m-error, .m-badge, .m-notice),
727
+ .m-field .m-error {
728
+ color: var(--m-tone);
729
+ }
730
+ /* The inverse token remap lives in tokens.css; text follows the remapped ink. */
731
+ :where([data-tone="inverse"]) {
732
+ color: var(--m-ink);
733
+ }
734
+
735
+ /* == ButtonGroup == */
736
+ .m-button-group {
737
+ display: inline-flex;
738
+ max-width: 100%;
739
+ border-radius: var(--m-radius);
740
+ }
741
+ .m-button-group > :is(button, a) {
742
+ display: inline-flex;
743
+ align-items: center;
744
+ justify-content: center;
745
+ gap: 6px;
746
+ min-height: var(--m-control);
747
+ min-width: var(--m-control);
748
+ margin-left: -1px;
749
+ padding: 4px 12px;
750
+ border: 1px solid var(--m-line-strong);
751
+ background: var(--m-surface);
752
+ color: var(--m-ink);
753
+ font-size: var(--m-font-size);
754
+ font-weight: var(--m-weight-semibold);
755
+ text-decoration: none;
756
+ cursor: pointer;
757
+ }
758
+ .m-button-group > :first-child {
759
+ margin-left: 0;
760
+ border-start-start-radius: var(--m-radius);
761
+ border-end-start-radius: var(--m-radius);
762
+ }
763
+ .m-button-group > :last-child {
764
+ border-start-end-radius: var(--m-radius);
765
+ border-end-end-radius: var(--m-radius);
766
+ }
767
+ .m-button-group-icon {
768
+ display: inline-flex;
769
+ }
770
+ .m-button-group > :is(button, a):hover:not(:disabled) {
771
+ background: var(--m-sunken);
772
+ }
773
+ .m-button-group > :is(button, a):focus-visible {
774
+ position: relative;
775
+ z-index: 1;
776
+ outline-offset: 2px;
777
+ }
778
+ .m-button-group > button:disabled {
779
+ color: var(--m-muted);
780
+ cursor: not-allowed;
781
+ }
782
+
783
+ /* == Fieldset == */
784
+ .m-fieldset {
785
+ /* Block layout: a rendered legend is not a flex item, so gap would not apply to it. */
786
+ display: block;
787
+ min-width: 0;
788
+ margin: 0;
789
+ padding: 0;
790
+ border: 0;
791
+ }
792
+ .m-fieldset > legend {
793
+ margin-bottom: 16px;
794
+ padding: 0;
795
+ color: var(--m-ink);
796
+ font-size: 15px;
797
+ font-weight: var(--m-weight-semibold);
798
+ }
799
+ .m-fieldset > legend:has(+ p) {
800
+ margin-bottom: 4px;
801
+ }
802
+ .m-fieldset > p {
803
+ margin: 0 0 16px;
804
+ color: var(--m-muted);
805
+ font-size: 13px;
806
+ }
807
+ .m-fieldset-fields {
808
+ display: grid;
809
+ gap: 16px;
810
+ min-width: 0;
811
+ }
812
+
813
+ /* == Choices: Checkbox, RadioGroup and Switch == */
814
+ .m-choice {
815
+ display: flex;
816
+ align-items: center;
817
+ gap: 10px;
818
+ min-height: var(--m-control);
819
+ font-size: var(--m-font-size);
820
+ cursor: pointer;
821
+ }
822
+ [data-mortar] input:is([type="checkbox"], [type="radio"]) {
823
+ width: 18px;
824
+ height: 18px;
825
+ flex-shrink: 0;
826
+ accent-color: var(--m-primary);
827
+ cursor: pointer;
828
+ }
829
+ [data-mortar] input.m-switch {
830
+ appearance: none;
831
+ width: 34px;
832
+ height: 20px;
833
+ border: 1px solid var(--m-line-strong);
834
+ border-radius: var(--m-radius-full);
835
+ background: var(--m-sunken);
836
+ position: relative;
837
+ }
838
+ .m-switch::after {
839
+ content: "";
840
+ position: absolute;
841
+ width: 14px;
842
+ height: 14px;
843
+ top: 2px;
844
+ left: 2px;
845
+ border-radius: 50%;
846
+ background: var(--m-muted);
847
+ transition: transform 0.15s;
848
+ }
849
+ .m-radio-group {
850
+ border: 0;
851
+ padding: 0;
852
+ margin: 0;
853
+ display: grid;
854
+ gap: 10px;
855
+ }
856
+ .m-radio-group legend {
857
+ margin-bottom: 10px;
858
+ }
859
+ .m-radio-card {
860
+ display: flex;
861
+ align-items: start;
862
+ gap: 12px;
863
+ border: 1px solid var(--m-line);
864
+ padding: 16px;
865
+ border-radius: var(--m-radius);
866
+ cursor: pointer;
867
+ }
868
+ .m-radio-card strong,
869
+ .m-radio-card span > span {
870
+ display: block;
871
+ font-size: 13px;
872
+ }
873
+ .m-radio-card span > span {
874
+ color: var(--m-muted);
875
+ margin-top: 4px;
876
+ }
877
+ .m-choice-described {
878
+ align-items: flex-start;
879
+ cursor: default;
880
+ }
881
+ .m-choice-described > input {
882
+ margin-top: 2px;
883
+ }
884
+ /* Error under a checkbox label, and the hint or error under a group's legend. */
885
+ .m-choice-text p.m-error {
886
+ color: var(--m-tone);
887
+ font-size: 12px;
888
+ }
889
+ .m-radio-group > p {
890
+ grid-column: 1 / -1;
891
+ flex-basis: 100%;
892
+ margin: -4px 0 0;
893
+ color: var(--m-muted);
894
+ font-size: 12px;
895
+ }
896
+ .m-radio-group > p.m-error {
897
+ color: var(--m-tone);
898
+ }
899
+ .m-radio-group[data-invalid] .m-radio-card:not(:has(:checked)) {
900
+ border-color: var(--m-danger);
901
+ }
902
+ [data-mortar] input.m-switch:checked {
903
+ background: var(--m-primary);
904
+ border-color: var(--m-primary);
905
+ }
906
+ .m-switch:checked::after {
907
+ transform: translateX(14px);
908
+ background: var(--m-on-primary);
909
+ }
910
+ .m-radio-card:has(:checked) {
911
+ background: var(--m-selected);
912
+ border-color: var(--m-primary);
913
+ }
914
+ .m-radio-card:has(:disabled) {
915
+ background: var(--m-sunken);
916
+ color: var(--m-muted);
917
+ cursor: not-allowed;
918
+ }
919
+ .m-radio-card:has(:disabled) :is(strong, input) {
920
+ color: var(--m-muted);
921
+ cursor: not-allowed;
922
+ }
923
+ .m-radio-group[data-layout="grid"] {
924
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
925
+ }
926
+ .m-radio-group[data-layout="swatches"] {
927
+ display: flex;
928
+ flex-wrap: wrap;
929
+ gap: 12px;
930
+ }
931
+ .m-radio-group[data-layout="swatches"] legend {
932
+ width: 100%;
933
+ }
934
+ .m-radio-group[data-layout="swatches"] .m-radio-card {
935
+ position: relative;
936
+ padding: 0;
937
+ border: 0;
938
+ border-radius: 50%;
939
+ }
940
+ .m-radio-group[data-layout="swatches"] .m-radio-card:has(:checked) {
941
+ background: none;
942
+ }
943
+ .m-radio-group[data-layout="swatches"] input {
944
+ position: absolute;
945
+ inset: 0;
946
+ width: 100%;
947
+ height: 100%;
948
+ margin: 0;
949
+ opacity: 0;
950
+ }
951
+ .m-radio-group[data-layout="swatches"] .m-swatch {
952
+ display: block;
953
+ width: 32px;
954
+ height: 32px;
955
+ border-radius: 50%;
956
+ box-shadow: inset 0 0 0 1px var(--m-swatch-ring);
957
+ }
958
+ .m-radio-group[data-layout="swatches"] input:checked + .m-swatch {
959
+ outline: 2px solid var(--m-ink);
960
+ outline-offset: 2px;
961
+ }
962
+ .m-radio-group[data-layout="swatches"] input:focus-visible + .m-swatch {
963
+ outline: 2px solid var(--m-focus);
964
+ outline-offset: 4px;
965
+ }
966
+ .m-radio-group[data-layout="swatches"] input:disabled + .m-swatch {
967
+ opacity: 0.35;
968
+ }
969
+
970
+ /* == Listbox and Combobox == */
971
+ .m-choice-control {
972
+ position: relative;
973
+ display: flex;
974
+ align-items: center;
975
+ gap: 8px;
976
+ width: 100%;
977
+ min-width: 0;
978
+ min-height: var(--m-control);
979
+ padding: 4px var(--m-pad);
980
+ border: 1px solid var(--m-line-strong);
981
+ border-radius: var(--m-radius);
982
+ background: var(--m-surface);
983
+ color: var(--m-ink);
984
+ font-size: var(--m-font-size);
985
+ text-align: start;
986
+ }
987
+ button.m-choice-control {
988
+ padding-inline-end: calc(var(--m-pad) + 24px);
989
+ cursor: pointer;
990
+ }
991
+ .m-choice-control > svg,
992
+ .m-combobox-toggle svg {
993
+ color: var(--m-secondary);
994
+ }
995
+ button.m-choice-control > svg {
996
+ position: absolute;
997
+ inset-inline-end: var(--m-pad);
998
+ top: 50%;
999
+ transform: translateY(-50%);
1000
+ }
1001
+ .m-choice-value {
1002
+ display: flex;
1003
+ align-items: center;
1004
+ gap: 8px;
1005
+ min-width: 0;
1006
+ }
1007
+ .m-choice-value > span:last-child {
1008
+ overflow: hidden;
1009
+ text-overflow: ellipsis;
1010
+ white-space: nowrap;
1011
+ }
1012
+ .m-placeholder {
1013
+ color: var(--m-muted);
1014
+ }
1015
+ .m-combobox {
1016
+ position: relative;
1017
+ min-width: 0;
1018
+ }
1019
+ .m-combobox > input.m-choice-control {
1020
+ padding-inline-end: calc(var(--m-pad) + 28px);
1021
+ }
1022
+ .m-combobox > .m-choice-leading {
1023
+ position: absolute;
1024
+ inset-inline-start: var(--m-pad);
1025
+ top: 50%;
1026
+ z-index: 1;
1027
+ transform: translateY(-50%);
1028
+ pointer-events: none;
1029
+ }
1030
+ .m-combobox input::placeholder {
1031
+ color: var(--m-muted);
1032
+ }
1033
+ .m-combobox-toggle {
1034
+ position: absolute;
1035
+ inset-inline-end: 4px;
1036
+ top: 50%;
1037
+ transform: translateY(-50%);
1038
+ --m-icon-button: 28px;
1039
+ }
1040
+ .m-choice-leading {
1041
+ display: inline-flex;
1042
+ flex-shrink: 0;
1043
+ }
1044
+ .m-choice-options {
1045
+ z-index: var(--m-z-popover);
1046
+ width: var(--button-width);
1047
+ min-width: 200px;
1048
+ max-height: 280px;
1049
+ padding: 6px;
1050
+ overflow-y: auto;
1051
+ border: 1px solid var(--m-line);
1052
+ border-radius: var(--m-radius);
1053
+ background: var(--m-surface);
1054
+ box-shadow: var(--m-float);
1055
+ --anchor-gap: 6px;
1056
+ outline: none;
1057
+ }
1058
+ .m-combobox-options {
1059
+ width: var(--input-width);
1060
+ }
1061
+ .m-choice-option {
1062
+ display: flex;
1063
+ align-items: center;
1064
+ gap: 10px;
1065
+ padding: 8px 10px;
1066
+ border-radius: var(--m-radius-sm);
1067
+ color: var(--m-ink);
1068
+ font-size: var(--m-font-size);
1069
+ cursor: pointer;
1070
+ min-height: var(--m-touch-target);
1071
+ }
1072
+ .m-choice-text {
1073
+ display: flex;
1074
+ flex: 1;
1075
+ flex-direction: column;
1076
+ min-width: 0;
1077
+ }
1078
+ .m-choice-text small {
1079
+ color: var(--m-muted);
1080
+ font-size: 12px;
1081
+ }
1082
+ .m-choice-check {
1083
+ width: 16px;
1084
+ height: 16px;
1085
+ flex-shrink: 0;
1086
+ visibility: hidden;
1087
+ }
1088
+ .m-choice-text label {
1089
+ color: var(--m-ink);
1090
+ font-weight: var(--m-weight-medium);
1091
+ cursor: pointer;
1092
+ }
1093
+ .m-choice-text p {
1094
+ margin: 2px 0 0;
1095
+ color: var(--m-muted);
1096
+ font-size: 13px;
1097
+ }
1098
+ .m-choice-control:hover:not(
1099
+ :disabled,
1100
+ [data-disabled],
1101
+ [aria-invalid="true"],
1102
+ [data-invalid]
1103
+ ) {
1104
+ border-color: var(--m-secondary);
1105
+ }
1106
+ .m-choice-control:is([aria-invalid="true"], [data-invalid]) {
1107
+ border-color: var(--m-danger);
1108
+ }
1109
+ .m-combobox[data-leading] > input.m-choice-control {
1110
+ padding-inline-start: calc(var(--m-pad) + 30px);
1111
+ }
1112
+ .m-choice-options:focus-visible {
1113
+ /* Focus stays visible on the active option via aria-activedescendant. */
1114
+ outline: none;
1115
+ }
1116
+ .m-choice-option[data-focus] {
1117
+ background: var(--m-selected);
1118
+ }
1119
+ .m-choice-option[data-disabled] {
1120
+ color: var(--m-muted);
1121
+ cursor: not-allowed;
1122
+ }
1123
+ .m-choice-option[data-selected] {
1124
+ font-weight: var(--m-weight-semibold);
1125
+ }
1126
+ .m-choice-option[data-selected] .m-choice-check {
1127
+ visibility: visible;
1128
+ }
1129
+
1130
+ /* == SegmentedControl == */
1131
+ .m-segmented {
1132
+ display: inline-flex;
1133
+ width: fit-content;
1134
+ background: var(--m-sunken);
1135
+ padding: 3px;
1136
+ gap: 2px;
1137
+ border-radius: var(--m-radius);
1138
+ }
1139
+ .m-segmented button {
1140
+ border: 1px solid transparent;
1141
+ background: transparent;
1142
+ color: var(--m-muted);
1143
+ font-size: 12px;
1144
+ font-weight: var(--m-weight-medium);
1145
+ border-radius: 6px;
1146
+ padding: 5px 10px;
1147
+ cursor: pointer;
1148
+ min-height: max(28px, var(--m-touch-target));
1149
+ }
1150
+ .m-segmented [aria-pressed="true"] {
1151
+ background: var(--m-surface);
1152
+ color: var(--m-ink);
1153
+ border-color: var(--m-line);
1154
+ }
1155
+
1156
+ /* == FilterChip == */
1157
+ .m-chip {
1158
+ display: inline-flex;
1159
+ align-items: center;
1160
+ gap: 6px;
1161
+ min-height: max(calc(var(--m-control) - 8px), var(--m-touch-target));
1162
+ padding: 0 10px;
1163
+ border: 1px dashed var(--m-line-strong);
1164
+ border-radius: var(--m-radius-full);
1165
+ background: transparent;
1166
+ color: var(--m-secondary);
1167
+ font-size: 12px;
1168
+ font-weight: var(--m-weight-semibold);
1169
+ cursor: pointer;
1170
+ }
1171
+ .m-chip button {
1172
+ display: grid;
1173
+ place-items: center;
1174
+ width: 18px;
1175
+ height: 18px;
1176
+ padding: 0;
1177
+ border-radius: 50%;
1178
+ font-size: 10px;
1179
+ }
1180
+ .m-chip[data-selected] {
1181
+ border: 1px solid transparent;
1182
+ background: var(--m-selected);
1183
+ color: var(--m-ink);
1184
+ padding-right: 4px;
1185
+ }
1186
+ .m-chip button:hover {
1187
+ background: var(--m-surface);
1188
+ }
1189
+
1190
+ /* == Kbd == */
1191
+ .m-kbd-group {
1192
+ display: inline-flex;
1193
+ gap: 4px;
1194
+ }
1195
+ .m-kbd {
1196
+ font: var(--m-weight-medium) 11px var(--m-mono);
1197
+ padding: 1px 6px;
1198
+ border: 1px solid var(--m-line-strong);
1199
+ border-bottom-width: 2px;
1200
+ border-radius: var(--m-radius-sm);
1201
+ color: var(--m-ink);
1202
+ background: var(--m-surface);
1203
+ }
1204
+
1205
+ /* == Avatar and AvatarGroup == */
1206
+ /* Display */
1207
+ .m-avatar {
1208
+ position: relative;
1209
+ display: inline-grid;
1210
+ place-items: center;
1211
+ flex-shrink: 0;
1212
+ width: 34px;
1213
+ height: 34px;
1214
+ border-radius: 50%;
1215
+ background: var(--m-sunken);
1216
+ color: var(--m-ink);
1217
+ font-size: 12px;
1218
+ font-weight: var(--m-weight-bold);
1219
+ }
1220
+ .m-avatar img {
1221
+ width: 100%;
1222
+ height: 100%;
1223
+ border-radius: inherit;
1224
+ object-fit: cover;
1225
+ }
1226
+ .m-avatar-sm {
1227
+ width: 26px;
1228
+ height: 26px;
1229
+ font-size: 10px;
1230
+ }
1231
+ .m-avatar-lg {
1232
+ width: 52px;
1233
+ height: 52px;
1234
+ font-size: 18px;
1235
+ }
1236
+ .m-avatar-status {
1237
+ position: absolute;
1238
+ right: -1px;
1239
+ bottom: -1px;
1240
+ width: 10px;
1241
+ height: 10px;
1242
+ border-radius: 50%;
1243
+ border: 2px solid var(--m-surface);
1244
+ background: var(--m-line-strong);
1245
+ }
1246
+ .m-avatar-group {
1247
+ display: flex;
1248
+ }
1249
+ .m-avatar-group li + li {
1250
+ margin-left: -8px;
1251
+ }
1252
+ .m-avatar-group .m-avatar {
1253
+ border: 2px solid var(--m-surface);
1254
+ }
1255
+ .m-avatar-more {
1256
+ font: var(--m-weight-medium) 10px var(--m-mono);
1257
+ color: var(--m-secondary);
1258
+ }
1259
+ .m-avatar-xs {
1260
+ width: 20px;
1261
+ height: 20px;
1262
+ font-size: 9px;
1263
+ }
1264
+ .m-avatar-xl {
1265
+ width: 72px;
1266
+ height: 72px;
1267
+ font-size: 24px;
1268
+ }
1269
+ .m-avatar:has(> .m-avatar-placeholder) {
1270
+ color: var(--m-muted);
1271
+ }
1272
+ .m-avatar-placeholder {
1273
+ width: 100%;
1274
+ height: 100%;
1275
+ border-radius: inherit;
1276
+ overflow: hidden;
1277
+ }
1278
+ .m-avatar[data-shape="square"] {
1279
+ border-radius: var(--m-radius);
1280
+ }
1281
+ .m-avatar-status[data-status="online"] {
1282
+ background: var(--m-success);
1283
+ }
1284
+ .m-avatar-status[data-position="top"] {
1285
+ top: -1px;
1286
+ bottom: auto;
1287
+ }
1288
+
1289
+ /* == ProgressBar == */
1290
+ .m-progress {
1291
+ display: grid;
1292
+ gap: 8px;
1293
+ font-size: 12px;
1294
+ }
1295
+ .m-progress-label {
1296
+ display: flex;
1297
+ justify-content: space-between;
1298
+ gap: 12px;
1299
+ }
1300
+ .m-progress-label span:first-child {
1301
+ font-weight: var(--m-weight-semibold);
1302
+ }
1303
+ .m-progress-label span + span {
1304
+ font-family: var(--m-mono);
1305
+ color: var(--m-muted);
1306
+ }
1307
+ .m-progress-track {
1308
+ height: 6px;
1309
+ border-radius: 6px;
1310
+ background: var(--m-sunken);
1311
+ overflow: hidden;
1312
+ }
1313
+ .m-progress-track span {
1314
+ display: block;
1315
+ height: 100%;
1316
+ border-radius: inherit;
1317
+ background: var(--m-primary);
1318
+ }
1319
+ .m-progress[data-tone="neutral"] {
1320
+ --m-tone: var(--m-secondary);
1321
+ }
1322
+ .m-progress[data-tone] .m-progress-track span {
1323
+ background: var(--m-tone);
1324
+ }
1325
+
1326
+ /* == CheckList == */
1327
+ /* Check list. */
1328
+ .m-check-list {
1329
+ display: grid;
1330
+ gap: 12px 24px;
1331
+ --m-icon-size: 18px;
1332
+ }
1333
+ .m-check-list li {
1334
+ display: grid;
1335
+ grid-template-columns: 18px minmax(0, 1fr);
1336
+ gap: 10px;
1337
+ align-items: start;
1338
+ font-size: 15px;
1339
+ color: var(--m-ink);
1340
+ }
1341
+ .m-check-list svg {
1342
+ margin-top: 2px;
1343
+ padding: 2px;
1344
+ border-radius: 50%;
1345
+ background: var(--m-selected);
1346
+ color: var(--m-primary);
1347
+ }
1348
+ .m-check-list[data-columns="2"] {
1349
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1350
+ }
1351
+ .m-check-list[data-size="sm"] li {
1352
+ font-size: 13px;
1353
+ }
1354
+
1355
+ /* == Notice == */
1356
+ .m-notice {
1357
+ border: 1px solid var(--m-line);
1358
+ border-radius: var(--m-radius);
1359
+ padding: 16px;
1360
+ font-size: var(--m-font-size);
1361
+ }
1362
+ .m-notice > div {
1363
+ margin-top: 6px;
1364
+ }
1365
+ .m-notice-actions {
1366
+ display: flex;
1367
+ flex-wrap: wrap;
1368
+ gap: 8px 16px;
1369
+ font-weight: var(--m-weight-semibold);
1370
+ }
1371
+ .m-notice-actions a {
1372
+ color: inherit;
1373
+ text-decoration: underline;
1374
+ text-underline-offset: 3px;
1375
+ }
1376
+ .m-notice-dismiss {
1377
+ position: absolute;
1378
+ top: 10px;
1379
+ inset-inline-end: 10px;
1380
+ color: inherit;
1381
+ --m-icon-button: 28px;
1382
+ }
1383
+ .m-notice[data-dismissible] {
1384
+ position: relative;
1385
+ padding-inline-end: 48px;
1386
+ }
1387
+
1388
+ /* == ErrorSummary == */
1389
+ .m-error-summary {
1390
+ --m-tone: var(--m-danger);
1391
+ padding: 16px;
1392
+ border: 2px solid var(--m-tone);
1393
+ border-radius: var(--m-radius);
1394
+ background: var(--m-surface);
1395
+ color: var(--m-ink);
1396
+ font-size: var(--m-font-size);
1397
+ }
1398
+ /* Focus moves here after a failed submit, by mouse too: always show it. */
1399
+ .m-error-summary:focus {
1400
+ outline: 2px solid var(--m-focus);
1401
+ outline-offset: 2px;
1402
+ }
1403
+ .m-error-summary-title {
1404
+ font-size: 15px;
1405
+ font-weight: var(--m-weight-semibold);
1406
+ }
1407
+ .m-error-summary ul {
1408
+ display: grid;
1409
+ gap: 6px;
1410
+ margin: 10px 0 0;
1411
+ padding: 0;
1412
+ list-style: none;
1413
+ }
1414
+ .m-error-summary li {
1415
+ color: var(--m-tone);
1416
+ font-weight: var(--m-weight-semibold);
1417
+ }
1418
+ .m-error-summary a {
1419
+ color: var(--m-tone);
1420
+ text-decoration: underline;
1421
+ text-underline-offset: 3px;
1422
+ }
1423
+
1424
+ /* == EmptyState == */
1425
+ .m-empty {
1426
+ text-align: center;
1427
+ padding: 64px 24px;
1428
+ }
1429
+ .m-empty h3 {
1430
+ font-size: 18px;
1431
+ font-weight: var(--m-weight-semibold);
1432
+ }
1433
+ .m-empty p {
1434
+ color: var(--m-muted);
1435
+ margin: 10px 0 20px;
1436
+ }
1437
+ .m-empty-icon {
1438
+ display: inline-grid;
1439
+ place-items: center;
1440
+ width: 48px;
1441
+ height: 48px;
1442
+ margin-bottom: 16px;
1443
+ border-radius: var(--m-radius-lg);
1444
+ background: var(--m-sunken);
1445
+ color: var(--m-secondary);
1446
+ --m-icon-size: 24px;
1447
+ }
1448
+ .m-empty > :is(h2, h4, h5, h6) {
1449
+ font-size: 18px;
1450
+ font-weight: var(--m-weight-semibold);
1451
+ }
1452
+ .m-empty-extra {
1453
+ max-width: 32rem;
1454
+ margin: 32px auto 0;
1455
+ text-align: start;
1456
+ }
1457
+ .m-empty-extra .m-empty-icon {
1458
+ flex-shrink: 0;
1459
+ width: 40px;
1460
+ height: 40px;
1461
+ margin: 0;
1462
+ --m-icon-size: 20px;
1463
+ }
1464
+ .m-empty[data-dashed] {
1465
+ padding: 48px 24px;
1466
+ border: 2px dashed var(--m-line-strong);
1467
+ border-radius: var(--m-radius-lg);
1468
+ }
1469
+ .m-empty[data-align="start"] {
1470
+ text-align: start;
1471
+ padding-inline: 0;
1472
+ }
1473
+ .m-empty[data-align="start"][data-dashed] {
1474
+ padding-inline: 24px;
1475
+ }
1476
+ .m-empty[data-align="start"] .m-empty-extra {
1477
+ max-width: none;
1478
+ }
1479
+
1480
+ /* == Dropdown == */
1481
+ .m-menu {
1482
+ z-index: var(--m-z-popover);
1483
+ min-width: 200px;
1484
+ border: 1px solid var(--m-line);
1485
+ padding: 6px;
1486
+ border-radius: var(--m-radius);
1487
+ background: var(--m-surface);
1488
+ box-shadow: var(--m-float);
1489
+ --anchor-gap: 6px;
1490
+ }
1491
+ .m-menu :is(button, a) {
1492
+ display: flex;
1493
+ align-items: center;
1494
+ gap: 10px;
1495
+ width: 100%;
1496
+ padding: 8px 10px;
1497
+ border-radius: var(--m-radius-sm);
1498
+ color: var(--m-ink);
1499
+ font-size: var(--m-font-size);
1500
+ text-align: left;
1501
+ text-decoration: none;
1502
+ }
1503
+ .m-menu .m-error {
1504
+ color: var(--m-danger);
1505
+ }
1506
+ .m-menu-icon {
1507
+ display: inline-flex;
1508
+ flex-shrink: 0;
1509
+ color: var(--m-muted);
1510
+ }
1511
+ .m-menu-text {
1512
+ display: flex;
1513
+ flex-direction: column;
1514
+ min-width: 0;
1515
+ }
1516
+ .m-menu-text small {
1517
+ color: var(--m-muted);
1518
+ font-size: 12px;
1519
+ }
1520
+ .m-menu-heading {
1521
+ padding: 8px 10px 4px;
1522
+ color: var(--m-muted);
1523
+ font-size: 11px;
1524
+ font-weight: var(--m-weight-semibold);
1525
+ letter-spacing: var(--m-tracking-caps);
1526
+ text-transform: uppercase;
1527
+ }
1528
+ .m-menu-separator {
1529
+ height: 1px;
1530
+ margin: 6px -6px;
1531
+ background: var(--m-line);
1532
+ }
1533
+ .m-menu-header {
1534
+ margin: -6px -6px 6px;
1535
+ padding: 10px 16px;
1536
+ border-bottom: 1px solid var(--m-line);
1537
+ color: var(--m-secondary);
1538
+ font-size: 13px;
1539
+ }
1540
+ .m-menu:focus-visible {
1541
+ /* The active item shows focus; the panel itself needs no ring. */
1542
+ outline: none;
1543
+ }
1544
+ .m-menu :is(button, a)[data-focus] {
1545
+ background: var(--m-selected);
1546
+ }
1547
+ .m-menu :is(button, a)[data-disabled] {
1548
+ color: var(--m-muted);
1549
+ cursor: not-allowed;
1550
+ }
1551
+
1552
+ /* == Layout: Stack, Container, Text and Divider == */
1553
+ .m-actions {
1554
+ display: flex;
1555
+ align-items: center;
1556
+ flex-wrap: wrap;
1557
+ gap: 10px;
1558
+ }
1559
+ .m-stack {
1560
+ display: flex;
1561
+ flex-direction: column;
1562
+ gap: 20px;
1563
+ }
1564
+ .m-layout-stack {
1565
+ display: flex;
1566
+ flex-direction: column;
1567
+ gap: 16px;
1568
+ min-width: 0;
1569
+ }
1570
+ .m-container {
1571
+ width: 100%;
1572
+ max-width: 1280px;
1573
+ margin-inline: auto;
1574
+ min-width: 0;
1575
+ }
1576
+ .m-text {
1577
+ margin: 0;
1578
+ color: var(--m-ink);
1579
+ font-size: 15px;
1580
+ text-wrap: pretty;
1581
+ }
1582
+ .m-divider {
1583
+ width: 100%;
1584
+ margin: 24px 0;
1585
+ border: 0;
1586
+ border-top: 1px solid var(--m-line-strong);
1587
+ }
1588
+ div.m-divider {
1589
+ display: flex;
1590
+ align-items: center;
1591
+ gap: 12px;
1592
+ border-top: 0;
1593
+ color: var(--m-muted);
1594
+ font-size: 13px;
1595
+ }
1596
+ div.m-divider::before,
1597
+ div.m-divider::after {
1598
+ content: "";
1599
+ flex: 1;
1600
+ border-top: 1px solid var(--m-line-strong);
1601
+ }
1602
+ .m-layout-stack[data-direction="row"] {
1603
+ flex-direction: row;
1604
+ align-items: center;
1605
+ }
1606
+ .m-layout-stack[data-wrap] {
1607
+ flex-wrap: wrap;
1608
+ }
1609
+ .m-layout-stack[data-gap="none"] {
1610
+ gap: 0;
1611
+ }
1612
+ .m-layout-stack[data-gap="xs"] {
1613
+ gap: 4px;
1614
+ }
1615
+ .m-layout-stack[data-gap="sm"] {
1616
+ gap: 8px;
1617
+ }
1618
+ .m-layout-stack[data-gap="lg"] {
1619
+ gap: 24px;
1620
+ }
1621
+ .m-layout-stack[data-gap="xl"] {
1622
+ gap: 40px;
1623
+ }
1624
+ .m-layout-stack[data-align="start"] {
1625
+ align-items: flex-start;
1626
+ }
1627
+ .m-layout-stack[data-align="center"] {
1628
+ align-items: center;
1629
+ }
1630
+ .m-layout-stack[data-align="end"] {
1631
+ align-items: flex-end;
1632
+ }
1633
+ .m-layout-stack[data-align="stretch"] {
1634
+ align-items: stretch;
1635
+ }
1636
+ .m-layout-stack[data-justify="center"] {
1637
+ justify-content: center;
1638
+ }
1639
+ .m-layout-stack[data-justify="end"] {
1640
+ justify-content: flex-end;
1641
+ }
1642
+ .m-layout-stack[data-justify="between"] {
1643
+ justify-content: space-between;
1644
+ }
1645
+ .m-container[data-size="sm"] {
1646
+ max-width: 480px;
1647
+ }
1648
+ .m-container[data-size="md"] {
1649
+ max-width: 768px;
1650
+ }
1651
+ .m-container[data-size="xl"] {
1652
+ max-width: 1440px;
1653
+ }
1654
+ .m-container[data-padded="true"] {
1655
+ padding: 48px 32px;
1656
+ }
1657
+ .m-text[data-color="secondary"] {
1658
+ color: var(--m-secondary);
1659
+ }
1660
+ .m-text[data-color="muted"] {
1661
+ color: var(--m-muted);
1662
+ }
1663
+ .m-text[data-size="sm"] {
1664
+ font-size: 13px;
1665
+ }
1666
+ .m-text[data-size="lg"] {
1667
+ font-size: 17px;
1668
+ }
1669
+ .m-text[data-weight="medium"] {
1670
+ font-weight: var(--m-weight-medium);
1671
+ }
1672
+ .m-text[data-weight="semibold"] {
1673
+ font-weight: var(--m-weight-semibold);
1674
+ }
1675
+ .m-divider[data-soft] {
1676
+ border-top-color: var(--m-line);
1677
+ }
1678
+ div.m-divider[data-soft]::before,
1679
+ div.m-divider[data-soft]::after {
1680
+ border-top-color: var(--m-line);
1681
+ }
1682
+ .m-divider[data-spacing="none"] {
1683
+ margin: 0;
1684
+ }
1685
+ .m-divider[data-spacing="sm"] {
1686
+ margin: 12px 0;
1687
+ }
1688
+ .m-divider[data-spacing="lg"] {
1689
+ margin: 40px 0;
1690
+ }
1691
+ @media (max-width: 560px) {
1692
+ .m-container[data-padded="true"] {
1693
+ padding: 32px 20px;
1694
+ }
1695
+ }
1696
+
1697
+ /* == Card == */
1698
+ .m-card {
1699
+ display: flex;
1700
+ flex-direction: column;
1701
+ min-width: 0;
1702
+ color: var(--m-ink);
1703
+ overflow: hidden;
1704
+ --m-card-pad: 20px;
1705
+ }
1706
+ .m-card-header {
1707
+ display: flex;
1708
+ flex-wrap: wrap;
1709
+ align-items: center;
1710
+ justify-content: space-between;
1711
+ gap: 12px;
1712
+ padding: 14px 20px;
1713
+ border-bottom: 1px solid var(--m-line);
1714
+ }
1715
+ .m-card-header:last-child {
1716
+ border-bottom: 0;
1717
+ }
1718
+ .m-card-heading {
1719
+ min-width: 0;
1720
+ }
1721
+ .m-card-heading .m-card-title {
1722
+ margin: 0;
1723
+ font-size: 15px;
1724
+ font-weight: var(--m-weight-semibold);
1725
+ }
1726
+ .m-card-heading p {
1727
+ margin: 2px 0 0;
1728
+ color: var(--m-muted);
1729
+ font-size: 13px;
1730
+ }
1731
+ .m-card-actions {
1732
+ display: flex;
1733
+ flex-wrap: wrap;
1734
+ gap: 8px;
1735
+ }
1736
+ .m-card-body {
1737
+ flex: 1;
1738
+ min-width: 0;
1739
+ padding: var(--m-card-pad);
1740
+ }
1741
+ .m-card-footer {
1742
+ padding: 12px 20px;
1743
+ border-top: 1px solid var(--m-line);
1744
+ background: var(--m-sunken);
1745
+ color: var(--m-secondary);
1746
+ font-size: 13px;
1747
+ }
1748
+ .m-card-body > .m-description-list dl {
1749
+ border-top: 0;
1750
+ }
1751
+ .m-card-body > .m-description-list dl > div:first-child {
1752
+ padding-top: 0;
1753
+ }
1754
+ .m-card-body > .m-description-list dl > div:last-child {
1755
+ padding-bottom: 0;
1756
+ border-bottom: 0;
1757
+ }
1758
+ .m-card[data-padding="compact"] {
1759
+ --m-card-pad: 14px;
1760
+ }
1761
+ .m-card[data-padding="none"] {
1762
+ --m-card-pad: 0;
1763
+ }
1764
+ .m-card[data-tone="muted"] {
1765
+ background: var(--m-sunken);
1766
+ }
1767
+
1768
+ /* == MediaObject == */
1769
+ .m-media-object {
1770
+ display: flex;
1771
+ align-items: flex-start;
1772
+ gap: 14px;
1773
+ min-width: 0;
1774
+ }
1775
+ .m-media-object-media {
1776
+ display: inline-flex;
1777
+ flex-shrink: 0;
1778
+ }
1779
+ .m-media-object-media img {
1780
+ display: block;
1781
+ max-width: 96px;
1782
+ height: auto;
1783
+ border-radius: var(--m-radius);
1784
+ }
1785
+ .m-media-object-body {
1786
+ flex: 1;
1787
+ min-width: 0;
1788
+ color: var(--m-secondary);
1789
+ font-size: var(--m-font-size);
1790
+ }
1791
+ .m-media-object-title {
1792
+ margin: 0 0 2px;
1793
+ color: var(--m-ink);
1794
+ font-weight: var(--m-weight-semibold);
1795
+ }
1796
+ .m-media-object[data-align="center"] {
1797
+ align-items: center;
1798
+ }
1799
+ .m-media-object[data-reverse] {
1800
+ flex-direction: row-reverse;
1801
+ }
1802
+ .m-media-object[data-reverse] > .m-media-object-body {
1803
+ text-align: end;
1804
+ }
1805
+
1806
+ /* == BrandMark == */
1807
+ /* Brand logo slot. */
1808
+ .m-logo {
1809
+ display: inline-grid;
1810
+ place-items: center;
1811
+ width: 22px;
1812
+ height: 22px;
1813
+ flex-shrink: 0;
1814
+ }
1815
+ .m-logo :is(img, svg) {
1816
+ width: 100%;
1817
+ height: 100%;
1818
+ object-fit: contain;
1819
+ }
1820
+
1821
+ /* == Visually hidden text and skip link == */
1822
+ .m-sr-only,
1823
+ .m-skip {
1824
+ position: absolute;
1825
+ width: 1px;
1826
+ height: 1px;
1827
+ overflow: hidden;
1828
+ clip-path: inset(50%);
1829
+ white-space: nowrap;
1830
+ }
1831
+ .m-skip:focus {
1832
+ width: auto;
1833
+ height: auto;
1834
+ padding: 12px;
1835
+ top: 8px;
1836
+ left: 8px;
1837
+ clip-path: none;
1838
+ z-index: var(--m-z-skip);
1839
+ background: var(--m-surface);
1840
+ }
1841
+
1842
+ /* == Accessibility: reduced motion and forced colours == */
1843
+ /* One reduced-motion rule covers every Mortar element, its content and pseudo-elements. */
1844
+ @media (prefers-reduced-motion: reduce) {
1845
+ :where([data-mortar], [class^="m-"], [class*=" m-"]),
1846
+ :where([data-mortar], [class^="m-"], [class*=" m-"]) *,
1847
+ :where([data-mortar], [class^="m-"], [class*=" m-"]) ::before,
1848
+ :where([data-mortar], [class^="m-"], [class*=" m-"]) ::after,
1849
+ :where([class^="m-"], [class*=" m-"])::before,
1850
+ :where([class^="m-"], [class*=" m-"])::after {
1851
+ animation: none !important;
1852
+ transition: none !important;
1853
+ }
1854
+ }
1855
+ @media (forced-colors: active) {
1856
+ :where([data-mortar], [class^="m-"], [class*=" m-"])
1857
+ :is(.m-calendar [aria-current="date"], .m-radio-card:has(:checked)) {
1858
+ outline: 2px solid CanvasText;
1859
+ outline-offset: -2px;
1860
+ }
1861
+ }
1862
+ }
1863
+ @keyframes m-spin {
1864
+ to {
1865
+ transform: rotate(360deg);
1866
+ }
1867
+ }