maverick-wave 4.28.0 → 5.0.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 (138) hide show
  1. package/.claude/settings.local.json +29 -1
  2. package/.claude/skills/mw-maverick-wave/SKILL.md +39 -24
  3. package/.claude/skills/mw-maverick-wave/examples/angular-services.md +16 -4
  4. package/.claude/skills/mw-maverick-wave/examples/static-landing-page.md +13 -4
  5. package/.claude/skills/mw-maverick-wave/references/components.md +42 -5
  6. package/.claude/skills/mw-maverick-wave/references/forms.md +14 -2
  7. package/.claude/skills/mw-maverick-wave/references/javascript.md +17 -4
  8. package/.claude/skills/mw-maverick-wave/references/layout.md +53 -19
  9. package/.claude/skills/mw-maverick-wave/references/theming.md +38 -1
  10. package/.impeccable/config.local.json +5 -0
  11. package/.impeccable/hook.cache.json +1 -0
  12. package/CHANGELOG.md +11 -0
  13. package/CLAUDE.md +5 -5
  14. package/README.md +48 -4
  15. package/index.html +19 -38
  16. package/maverick-wave.min.css +14 -12
  17. package/maverick-wave.min.js +1 -1
  18. package/package.json +2 -2
  19. package/scripts/verify.js +27 -2
  20. package/src/js/main.js +223 -83
  21. package/src/partials/accordions-container.html +6 -10
  22. package/src/partials/alerts-container.html +6 -6
  23. package/src/partials/announcement-container.html +5 -5
  24. package/src/partials/avatars-container.html +85 -17
  25. package/src/partials/badges-container.html +6 -6
  26. package/src/partials/blog-posts-container.html +11 -12
  27. package/src/partials/buttons-container.html +3 -3
  28. package/src/partials/cards-container.html +26 -9
  29. package/src/partials/code-container.html +5 -5
  30. package/src/partials/content-slider-container.html +5 -4
  31. package/src/partials/divider-container.html +7 -7
  32. package/src/partials/dropdown-container.html +2 -2
  33. package/src/partials/empty-state-container.html +5 -5
  34. package/src/partials/footer-container.html +21 -18
  35. package/src/partials/form-elements-container.html +14 -14
  36. package/src/partials/gallery-container.html +16 -8
  37. package/src/partials/get-started-container.html +4 -8
  38. package/src/partials/header-container.html +18 -7
  39. package/src/partials/home-container.html +10 -20
  40. package/src/partials/input-group-container.html +11 -2
  41. package/src/partials/kbd-container.html +1 -1
  42. package/src/partials/login-container.html +5 -1
  43. package/src/partials/media-container.html +2 -2
  44. package/src/partials/modals-container.html +22 -32
  45. package/src/partials/pricing-container.html +9 -9
  46. package/src/partials/skeleton-container.html +6 -6
  47. package/src/partials/stepper-container.html +2 -2
  48. package/src/partials/tables-container.html +12 -12
  49. package/src/partials/tabs-container.html +17 -27
  50. package/src/partials/tags-container.html +23 -17
  51. package/src/partials/techstack-bucket-container.html +13 -0
  52. package/src/partials/tiles-container.html +26 -9
  53. package/src/partials/typography-container.html +7 -6
  54. package/src/partials/utilities-container.html +35 -15
  55. package/src/scss/_layers.scss +10 -0
  56. package/src/scss/abstracts/_index.scss +6 -0
  57. package/src/scss/abstracts/_mixins.scss +61 -153
  58. package/src/scss/abstracts/_variables.scss +123 -319
  59. package/src/scss/base/_base.scss +170 -190
  60. package/src/scss/base/_reset.scss +106 -104
  61. package/src/scss/base/_typography.scss +153 -151
  62. package/src/scss/components/_accordions.scss +97 -97
  63. package/src/scss/components/_alerts.scss +71 -70
  64. package/src/scss/components/_announcement.scss +71 -70
  65. package/src/scss/components/_avatars.scss +130 -142
  66. package/src/scss/components/_badge.scss +131 -132
  67. package/src/scss/components/_blog-post.scss +223 -220
  68. package/src/scss/components/_breadcrumbs.scss +93 -92
  69. package/src/scss/components/_button-bar.scss +102 -110
  70. package/src/scss/components/_buttons.scss +319 -324
  71. package/src/scss/components/_calendar.scss +251 -252
  72. package/src/scss/components/_cards.scss +359 -386
  73. package/src/scss/components/_code.scss +190 -194
  74. package/src/scss/components/_coming-soon.scss +77 -75
  75. package/src/scss/components/_content-slider.scss +90 -89
  76. package/src/scss/components/_divider.scss +55 -53
  77. package/src/scss/components/_dropdown.scss +179 -185
  78. package/src/scss/components/_empty-state.scss +57 -57
  79. package/src/scss/components/_flag.scss +294 -278
  80. package/src/scss/components/_gallery.scss +131 -133
  81. package/src/scss/components/_info.scss +151 -167
  82. package/src/scss/components/_kanban.scss +332 -341
  83. package/src/scss/components/_kbd.scss +40 -38
  84. package/src/scss/components/_lang-switch.scss +89 -97
  85. package/src/scss/components/_links.scss +21 -28
  86. package/src/scss/components/_lists.scss +510 -514
  87. package/src/scss/components/_localhost-indicator.scss +26 -24
  88. package/src/scss/components/_media.scss +22 -20
  89. package/src/scss/components/_meta-info.scss +35 -33
  90. package/src/scss/components/_modals.scss +277 -186
  91. package/src/scss/components/_pagination.scss +81 -87
  92. package/src/scss/components/_panels.scss +99 -97
  93. package/src/scss/components/_pricing.scss +183 -241
  94. package/src/scss/components/_progress.scss +112 -89
  95. package/src/scss/components/_prose.scss +35 -33
  96. package/src/scss/components/_ratings.scss +40 -38
  97. package/src/scss/components/_segmented.scss +82 -84
  98. package/src/scss/components/_skeleton.scss +66 -72
  99. package/src/scss/components/_spinners.scss +155 -159
  100. package/src/scss/components/_stepper.scss +116 -116
  101. package/src/scss/components/_tables.scss +155 -148
  102. package/src/scss/components/_tabs.scss +232 -141
  103. package/src/scss/components/_tags.scss +109 -107
  104. package/src/scss/components/_techstack-bucket.scss +123 -122
  105. package/src/scss/components/_testimonial.scss +72 -70
  106. package/src/scss/components/_theme-toggle.scss +53 -51
  107. package/src/scss/components/_tiles.scss +174 -174
  108. package/src/scss/components/_timelines.scss +282 -280
  109. package/src/scss/components/_toasts.scss +66 -72
  110. package/src/scss/form-elements/_checkbox.scss +117 -132
  111. package/src/scss/form-elements/_form.scss +149 -115
  112. package/src/scss/form-elements/_input-group.scss +99 -103
  113. package/src/scss/form-elements/_input.scss +74 -74
  114. package/src/scss/form-elements/_login.scss +48 -48
  115. package/src/scss/form-elements/_prefilled.scss +53 -51
  116. package/src/scss/form-elements/_radio.scss +95 -107
  117. package/src/scss/form-elements/_select.scss +42 -42
  118. package/src/scss/form-elements/_slider.scss +117 -132
  119. package/src/scss/form-elements/_textarea.scss +68 -54
  120. package/src/scss/form-elements/_toggle.scss +127 -140
  121. package/src/scss/layout/_footer.scss +172 -171
  122. package/src/scss/layout/_grid.scss +268 -302
  123. package/src/scss/layout/_header.scss +426 -427
  124. package/src/scss/layout/_home.scss +28 -27
  125. package/src/scss/layout/_main.scss +275 -238
  126. package/src/scss/layout/_page-header.scss +38 -36
  127. package/src/scss/layout/_section.scss +150 -124
  128. package/src/scss/main.scss +1 -1
  129. package/src/scss/utilities/_accessibility.scss +46 -45
  130. package/src/scss/utilities/_aspect.scss +22 -20
  131. package/src/scss/utilities/_corner.scss +23 -5
  132. package/src/scss/utilities/_display.scss +76 -70
  133. package/src/scss/utilities/_elevation.scss +13 -11
  134. package/src/scss/utilities/_flex.scss +83 -85
  135. package/src/scss/utilities/_reveal.scss +40 -63
  136. package/src/scss/utilities/_spacing.scss +63 -64
  137. package/src/scss/utilities/_text.scss +139 -141
  138. package/src/scss/utilities/_touch-targets.scss +130 -147
@@ -267,39 +267,39 @@
267
267
  </p>
268
268
  <div class="mw-grid-2 mw-gap-6">
269
269
  <div class="mw-card mw-card-simple">
270
- <h5 class="mw-text-medium mw-mb-2"><code>mw-text-truncate</code></h5>
270
+ <h4 class="mw-text-medium mw-mb-2"><code>mw-text-truncate</code></h4>
271
271
  <p class="mw-text-truncate">
272
272
  One line only: this sentence is far longer than the box it has been given
273
273
  and ends in an ellipsis rather than wrapping.
274
274
  </p>
275
- <h5 class="mw-text-medium mw-mb-2 mw-mt-5">
275
+ <h4 class="mw-text-medium mw-mb-2 mw-mt-5">
276
276
  <code>mw-text-clamp-2</code>
277
- </h5>
277
+ </h4>
278
278
  <p class="mw-text-clamp-2">
279
279
  Two lines and then an ellipsis. This paragraph runs on for long enough
280
280
  that the third line is cut off, which is what a card preview or a list
281
281
  subtitle wants when the copy is not yours to control.
282
282
  </p>
283
- <h5 class="mw-text-medium mw-mb-2 mw-mt-5">
283
+ <h4 class="mw-text-medium mw-mb-2 mw-mt-5">
284
284
  <code>mw-text-break</code>
285
- </h5>
285
+ </h4>
286
286
  <p class="mw-text-break">
287
287
  https://example.com/a/very/long/url/that/has/no/spaces/in/it/at/all/and/would/otherwise/push/the/page/sideways
288
288
  </p>
289
289
  </div>
290
290
  <div class="mw-card mw-card-simple">
291
- <h5 class="mw-text-medium mw-mb-2">
291
+ <h4 class="mw-text-medium mw-mb-2">
292
292
  <code>mw-text-balance</code> and <code>mw-text-pretty</code>
293
- </h5>
293
+ </h4>
294
294
  <p class="mw-text-muted mw-text-sm">
295
295
  Headings get <code>balance</code> by default - it evens the lines out so
296
296
  no headline leaves one word alone on the last one. Paragraphs get
297
297
  <code>pretty</code>, which only prevents the orphan. Both are on already;
298
298
  the classes are for anything the framework does not style itself.
299
299
  </p>
300
- <h5 class="mw-text-medium mw-mb-2 mw-mt-5">
300
+ <h4 class="mw-text-medium mw-mb-2 mw-mt-5">
301
301
  <code>mw-text-eyebrow</code>
302
- </h5>
302
+ </h4>
303
303
  <p class="mw-text-eyebrow mw-mb-1">Release notes</p>
304
304
  <h4 class="mw-mb-0">The kicker above a heading</h4>
305
305
  <h5 class="mw-text-medium mw-mb-2 mw-mt-5">
@@ -323,29 +323,29 @@
323
323
  </p>
324
324
  <div class="mw-grid-3 mw-gap-6">
325
325
  <div class="mw-card mw-card-simple mw-elevation-1">
326
- <h5 class="mw-text-medium mw-mb-1">1 &middot; resting</h5>
326
+ <h4 class="mw-text-medium mw-mb-1">1 &middot; resting</h4>
327
327
  <p class="mw-text-muted mw-text-sm mw-mb-0">
328
328
  Inputs, tags, small controls.
329
329
  </p>
330
330
  </div>
331
331
  <div class="mw-card mw-card-simple mw-elevation-2">
332
- <h5 class="mw-text-medium mw-mb-1">2 &middot; raised</h5>
332
+ <h4 class="mw-text-medium mw-mb-1">2 &middot; raised</h4>
333
333
  <p class="mw-text-muted mw-text-sm mw-mb-0">Cards and panels at rest.</p>
334
334
  </div>
335
335
  <div class="mw-card mw-card-simple mw-elevation-3">
336
- <h5 class="mw-text-medium mw-mb-1">3 &middot; floating</h5>
336
+ <h4 class="mw-text-medium mw-mb-1">3 &middot; floating</h4>
337
337
  <p class="mw-text-muted mw-text-sm mw-mb-0">A card under the pointer.</p>
338
338
  </div>
339
339
  <div class="mw-card mw-card-simple mw-elevation-4">
340
- <h5 class="mw-text-medium mw-mb-1">4 &middot; overlay</h5>
340
+ <h4 class="mw-text-medium mw-mb-1">4 &middot; overlay</h4>
341
341
  <p class="mw-text-muted mw-text-sm mw-mb-0">Dropdowns, popovers, drawer.</p>
342
342
  </div>
343
343
  <div class="mw-card mw-card-simple mw-elevation-5">
344
- <h5 class="mw-text-medium mw-mb-1">5 &middot; modal</h5>
344
+ <h4 class="mw-text-medium mw-mb-1">5 &middot; modal</h4>
345
345
  <p class="mw-text-muted mw-text-sm mw-mb-0">The dialog over everything.</p>
346
346
  </div>
347
347
  <div class="mw-card mw-card-simple mw-elevation-0">
348
- <h5 class="mw-text-medium mw-mb-1">0 &middot; flat</h5>
348
+ <h4 class="mw-text-medium mw-mb-1">0 &middot; flat</h4>
349
349
  <p class="mw-text-muted mw-text-sm mw-mb-0">Explicitly on the page.</p>
350
350
  </div>
351
351
  </div>
@@ -403,3 +403,23 @@
403
403
  <code>photo</code>
404
404
  </div>
405
405
  </div>
406
+
407
+ <h3 class="mw-mb-4 mw-mt-6">Squircle Corners</h3>
408
+ <p class="mw-text-muted mw-text-measure">
409
+ <code>mw-squircle</code> draws the framework silhouette with a superellipse
410
+ instead of a circular arc - same four radii, fuller curve. Opt-in, because
411
+ <code>corner-shape</code> only lands in Chromium so far; everywhere else the
412
+ card below is simply the normal one.
413
+ </p>
414
+ <div class="mw-grid-2 mw-gap-6">
415
+ <div class="mw-card mw-card-simple">
416
+ <strong>Default</strong>
417
+ <p class="mw-text-muted mw-text-sm mw-mb-0">Circular corner radii.</p>
418
+ </div>
419
+ <div class="mw-card mw-card-simple mw-squircle">
420
+ <strong>mw-squircle</strong>
421
+ <p class="mw-text-muted mw-text-sm mw-mb-0">
422
+ Superellipse, corner accent follows.
423
+ </p>
424
+ </div>
425
+ </div>
@@ -0,0 +1,10 @@
1
+ // Declared here and before any rule: a layer's position is fixed by where its
2
+ // name first appears. What it buys a consumer is that any rule outside a layer
3
+ // beats every rule inside one, however specific.
4
+ //
5
+ // The order reads inside out - the smaller thing first, then whatever places it.
6
+ // Two steps are worth stating: forms before components, because a list restyles
7
+ // the checkbox inside it; components before layout, because the two meet on one
8
+ // element (`class="mw-card mw-grid-2"`) and there the layout class has to win, or
9
+ // `.mw-card` hands `display: block` back to a grid.
10
+ @layer mw.reset, mw.base, mw.forms, mw.components, mw.layout, mw.utilities;
@@ -1,3 +1,9 @@
1
+ // Here and not only in main.scss: every partial loads `abstracts`, so this is the
2
+ // one point every entry reaches. Importing components one by one otherwise
3
+ // leaves the layer order to the import order, which puts `layout` ahead of
4
+ // `components` and hands `display: block` back to a card that is also a grid.
5
+ @use '../layers';
6
+
1
7
  // 'variables' first: it is the only configurable module, and both 'functions'
2
8
  // and 'mixins' load it themselves. Forwarding it later would mean it is
3
9
  // already loaded by the time a `with (...)` configuration reaches it.
@@ -2,7 +2,9 @@
2
2
  @use 'variables' as v;
3
3
  @use 'functions' as *;
4
4
 
5
- // Media query mixins
5
+ // `media-down` stops 0.02px short, or both ranges match at exactly 768px - an
6
+ // iPad in portrait, where mw-hide-mobile and mw-hide-desktop would both hide.
7
+ // 0.02 and not 1, because a viewport can be fractional (zoom, scaled display).
6
8
  @mixin media-up($breakpoint) {
7
9
  @media (min-width: map.get(v.$breakpoints, $breakpoint)) {
8
10
  @content;
@@ -10,51 +12,15 @@
10
12
  }
11
13
 
12
14
  @mixin media-down($breakpoint) {
13
- @media (max-width: map.get(v.$breakpoints, $breakpoint)) {
15
+ @media (max-width: map.get(v.$breakpoints, $breakpoint) - 0.02px) {
14
16
  @content;
15
17
  }
16
18
  }
17
19
 
18
- // Flexbox mixins
19
- @mixin flex(
20
- $direction: row,
21
- $justify: flex-start,
22
- $align: stretch,
23
- $wrap: nowrap
24
- ) {
25
- display: flex;
26
- flex-direction: $direction;
27
- justify-content: $justify;
28
- align-items: $align;
29
- flex-wrap: $wrap;
30
- }
31
-
32
- // Grid mixins
33
- @mixin grid($columns: 1, $gap: 1rem) {
34
- display: grid;
35
- grid-template-columns: repeat($columns, 1fr);
36
- gap: $gap;
37
- }
38
-
39
- @mixin media-between($min, $max) {
40
- $min-width: map.get(v.$breakpoints, $min);
41
- $max-width: map.get(v.$breakpoints, $max) - 1px;
42
-
43
- @media (min-width: $min-width) and (max-width: $max-width) {
44
- @content;
45
- }
46
- }
47
-
48
- // Scroll hint for a horizontally scrollable strip - a tab bar, a wide table.
49
- //
50
- // Four gradient layers in two pairs. The cover pair is attached to the content
51
- // (`local`) and therefore scrolls out of the way once that edge is reached;
52
- // the shadow pair stays on the frame (`scroll`). Net effect: a shadow shows
53
- // only on the side that still has something hidden behind it. No JavaScript,
54
- // no scroll listener.
55
- //
56
- // The cover has to match whatever the strip sits on, which is why it reads a
57
- // token: preset to the page, re-pointed inside cards, panels and modals.
20
+ // Four gradients in two pairs: the cover pair is attached to the content and
21
+ // scrolls out of the way at the edge, the shadow pair stays on the frame - so a
22
+ // shadow shows only where something is still hidden. No scroll listener.
23
+ // The cover reads a token because it has to match whatever the strip sits on.
58
24
  @mixin scroll-hint {
59
25
  background-image:
60
26
  linear-gradient(
@@ -79,17 +45,8 @@
79
45
  background-attachment: local, local, scroll, scroll;
80
46
  }
81
47
 
82
- // ---------------------------------------------------------------------------
83
- // Focus
84
- //
85
- // The ring was written out by hand in nine places before this, in two different
86
- // shapes, and a form field had a third. One mixin per shape instead, so the
87
- // whole framework moves when the ring is retuned.
88
- //
89
- // `:focus-visible` and never `:focus`: a mouse click on a button should not
90
- // leave it ringed. The browser decides what counts - a keyboard tab does, a
91
- // pointer press on a button does not, a pointer press into a text field does.
92
- // ---------------------------------------------------------------------------
48
+ // `:focus-visible` and never `:focus`: a mouse click on a button should not leave
49
+ // it ringed, and the browser is the one that knows which press counts.
93
50
  @mixin focus-ring(
94
51
  $offset: var(--mw-focus-ring-offset),
95
52
  $color: var(--mw-focus-ring-color)
@@ -101,9 +58,7 @@
101
58
  }
102
59
  }
103
60
 
104
- // Same ring, drawn inside the element. For anything clipped by its own frame -
105
- // a segment inside a segmented control, a cell inside a table - where a ring on
106
- // the outside would be cut off by the parent.
61
+ // Same ring, inside the element - for anything its parent would clip
107
62
  @mixin focus-ring-inset($inset: -2px) {
108
63
  &:focus-visible {
109
64
  outline: var(--mw-focus-ring-width) solid var(--mw-focus-ring-color);
@@ -112,18 +67,10 @@
112
67
  }
113
68
  }
114
69
 
115
- // The form-field shape: recolour the border the field already has and lay a soft
116
- // halo around it, rather than adding a second hard line outside it.
117
- //
118
- // Two different tones, and the split is the framework's own fill-or-ink rule.
119
- // The border is drawn *on* the field, which is light in both themes, so it takes
120
- // the fill tone. The halo spreads *outside* it, onto the page or the card, so it
121
- // takes the ink tone - the theme-aware one. Using the fill tone for both is what
122
- // made the dark theme's halo a dark blue haze on a dark blue page.
123
- //
124
- // `outline: none` is only safe because the halo *is* the indicator - except in
125
- // forced-colors mode, where box-shadow is dropped and the field would be left
126
- // with nothing. The guard hands the outline back there.
70
+ // Two tones on purpose: the border is drawn *on* the field, which is light in
71
+ // both themes, so it takes the fill tone; the halo spreads onto the page and
72
+ // takes the theme-aware ink tone. `outline: none` is safe because the halo is the
73
+ // indicator - except under forced-colors, where the guard hands it back.
127
74
  @mixin field-focus(
128
75
  $trigger: 'focus',
129
76
  $border: 'primary-color',
@@ -144,8 +91,7 @@
144
91
  }
145
92
  }
146
93
 
147
- // The halo on its own, for the controls that draw their own box - a checkbox, a
148
- // radio, a switch track, a calendar day - and only want the ring around it.
94
+ // For controls that draw their own box and only want the ring around it
149
95
  @mixin focus-halo($color: 'primary-text-color') {
150
96
  box-shadow: 0 0 0 var(--mw-focus-halo-size)
151
97
  color-mix(
@@ -155,15 +101,26 @@
155
101
  );
156
102
  }
157
103
 
158
- // ---------------------------------------------------------------------------
159
- // Pointer
160
- // ---------------------------------------------------------------------------
104
+ // Both spellings: `mw-active` is documented, plain `active` is what older markup
105
+ // carries, and a state toggled off in one must not stay lit in the other.
106
+ // $suffix qualifies the state, e.g. `@include active(':not(:disabled)')`.
107
+ @mixin active($suffix: '') {
108
+ &.mw-active#{$suffix},
109
+ &.active#{$suffix} {
110
+ @content;
111
+ }
112
+ }
113
+
114
+ // Without the `min-width: 0` that mw-text-truncate carries: that fixes the layout
115
+ // around the text, and handing it to seven components would change how they size
116
+ @mixin truncate {
117
+ white-space: nowrap;
118
+ overflow: hidden;
119
+ text-overflow: ellipsis;
120
+ }
161
121
 
162
- // Hover effects that *move* something - a card lifting, a tile growing - only
163
- // on a device that can actually hover. On touch, `:hover` latches after a tap
164
- // and stays on until you tap somewhere else, so a lifted card stays lifted and
165
- // a grid of them ends up with one member permanently out of line. Colour
166
- // changes are fine unguarded; movement is not.
122
+ // For hover effects that *move* something. On touch `:hover` latches after a tap,
123
+ // so a lifted card stays lifted. Colour changes are fine unguarded, movement is not.
167
124
  @mixin hover {
168
125
  @media (hover: hover) and (pointer: fine) {
169
126
  &:hover {
@@ -172,25 +129,16 @@
172
129
  }
173
130
  }
174
131
 
175
- // Everything a control needs to feel native under a thumb: no 300ms wait for a
176
- // double-tap that is never coming, and no grey box flashing over it while the
177
- // press is held - the component draws its own :active state instead.
132
+ // No 300ms double-tap wait, no grey flash - the component draws its own :active
178
133
  @mixin tappable {
179
134
  touch-action: manipulation;
180
135
  -webkit-tap-highlight-color: transparent;
181
136
  }
182
137
 
183
- // ---------------------------------------------------------------------------
184
- // Scrollbar
185
- //
186
- // Was written per list variant, which is why three of them had a thumb the same
187
- // colour as their track. One shape, used by every scroll container and by the
188
- // page itself.
189
- // ---------------------------------------------------------------------------
138
+ // One shape for every scroll container and the page itself
190
139
  @mixin scrollbar($thumb: var(--mw-text-muted-color), $track: transparent) {
191
140
  scrollbar-width: thin;
192
- // Firefox takes both in one declaration - a single value is invalid and the
193
- // whole thing is dropped, which is how a thumb ends up invisible.
141
+ // Firefox needs both values - a single one is invalid and drops the declaration
194
142
  scrollbar-color: #{$thumb} #{$track};
195
143
 
196
144
  &::-webkit-scrollbar {
@@ -217,28 +165,16 @@
217
165
  }
218
166
  }
219
167
 
220
- // Everything that fills its own surface with a colour - every variant except
221
- // outline, link and plain.
222
- //
223
- // Two things, and both are what separates a button from a coloured rectangle: a
224
- // hairline of light along the top edge, as if there were a light source above
225
- // the page, and a shadow underneath that says the button sits on the surface
226
- // rather than in it. The pressed state inverts both - the highlight goes and an
227
- // inner shadow takes its place, which is the whole of what "pushed in" looks
228
- // like.
229
- //
230
- // White and black rather than tokens on purpose: this is lighting, not palette.
231
- // It has to behave the same on a primary, a danger and whatever a project
232
- // overrides them with.
168
+ // A hairline of light along the top edge and a shadow underneath, as if the light
169
+ // came from above the page; the pressed state inverts both. White and black and
170
+ // not tokens on purpose - this is lighting, not palette.
233
171
  @mixin btn-solid {
234
172
  box-shadow:
235
173
  inset 0 1px 0 color-mix(in srgb, #fff 15%, transparent),
236
174
  var(--mw-elevation-1);
237
175
 
238
- // A pixel up, and the shadow steps up with it. The two together are what read
239
- // as the button coming toward the pointer - the shadow alone says it, but not
240
- // as clearly, and the lift is small enough that a row of buttons does not
241
- // twitch. Guarded, so it never latches after a tap.
176
+ // A pixel up and the shadow steps with it - the pair reads as the button coming
177
+ // toward the pointer. Guarded, so it never latches after a tap.
242
178
  @include hover {
243
179
  box-shadow:
244
180
  inset 0 1px 0 color-mix(in srgb, #fff 20%, transparent),
@@ -255,11 +191,8 @@
255
191
  }
256
192
  }
257
193
 
258
- // The look a link has: no box of its own, the underline only on hover, and it
259
- // sits a little lower than the browser's default so a descender does not cross
260
- // it. Shared by `mw-link` in running text and `mw-btn-link` in a row of
261
- // buttons - the two differ in whether they carry a control height, not in how
262
- // they look.
194
+ // Shared by mw-link and mw-btn-link, which differ in whether they carry a control
195
+ // height, not in how they look. The underline sits low so descenders clear it.
263
196
  @mixin link-look {
264
197
  background: transparent;
265
198
  border: none;
@@ -272,27 +205,13 @@
272
205
  }
273
206
  }
274
207
 
275
- // ---------------------------------------------------------------------------
276
- // Surface
277
- //
278
- // The MaverickWave silhouette: sharp corners on the top-left/bottom-right
279
- // diagonal, round ones on the other. Nine components repeated those four radii
280
- // by hand - this is the only place the shape is defined now.
281
- //
282
- // On top of the radii, an accent arc on the two round corners. The asymmetry
283
- // alone reads as an accident; a coloured bow sitting exactly on it does not.
284
- // Point-symmetric like the radii themselves, so it holds up on a tall card, a
285
- // wide panel and a square tile alike.
286
- //
287
- // The ring is drawn inside the padding box rather than on the border, because
288
- // four of the callers clip with `overflow: hidden` and would swallow a ring
289
- // sitting on the border edge. Hence $border-width: `inset: 0` already lands on
290
- // the padding box, but the corner radius there is the outer one minus the
291
- // border, and a ring drawn at the outer radius would visibly miss the curve.
208
+ // The MaverickWave silhouette: sharp corners on one diagonal, round on the other,
209
+ // with an accent arc on the round pair - the asymmetry alone reads as an accident.
292
210
  //
293
- // $corners drops the top arc for a surface whose top-right corner is already
294
- // taken by a fill of its own - see the coloured panel headers.
295
- // ---------------------------------------------------------------------------
211
+ // The ring is drawn inside the padding box, because four callers clip with
212
+ // `overflow: hidden`. Hence $border-width: the radius there is the outer one minus
213
+ // the border, and a ring at the outer radius would miss the curve. $corners drops
214
+ // the top arc where a fill already owns that corner.
296
215
  @mixin surface($border-width: 1px, $corners: 'both') {
297
216
  position: relative;
298
217
  border-radius: radius('sm') radius('xl') radius('sm') radius('xl');
@@ -302,20 +221,14 @@
302
221
  }
303
222
  }
304
223
 
305
- // The arc on its own, for a variant that has to redraw it without restating the
306
- // shape - it is already on the element by then.
224
+ // The arc alone, for a variant that redraws it without restating the shape
307
225
  @mixin corner-accent($border-width: 1px, $corners: 'both') {
308
226
  $sharp: radius('sm') - $border-width;
309
227
  $round: radius('xl') - $border-width;
310
- // Opaque out to where the curve ends, fading from there - so the arc runs a
311
- // little way into the straight edge instead of being cut off at the tangent.
312
- //
313
- // The 1.8 is the one number worth retuning here, and it is bounded by the
314
- // shortest surface rather than by the nicest-looking card: the fade reaches
315
- // from a corner, so on a 50px-high box - `mw-card-simple` with one line in it
316
- // - anything past about 2 lets the two arcs meet in the middle and the pair
317
- // stops reading as two corners and starts reading as a frame. Higher is
318
- // softer on a tall card, lower is more defined on a short one.
228
+ // Opaque to where the curve ends, fading from there, so the arc runs into the
229
+ // straight edge instead of stopping at the tangent. 1.8 is bounded by the
230
+ // shortest surface: past about 2 the two arcs meet on a 50px box and read as
231
+ // a frame.
319
232
  $reach: $round * 1.8;
320
233
  $top: radial-gradient(circle $reach at 100% 0, #000 $round, transparent 100%);
321
234
  $bottom: radial-gradient(
@@ -328,9 +241,8 @@
328
241
  position: absolute;
329
242
  inset: 0;
330
243
  pointer-events: none;
331
- // 2px rather than 1px: in the dark theme --mw-border sits at OKLch lightness
332
- // 0.32 and the arc tone at 0.41, so a hairline is separated from the plain
333
- // border by little more than its saturation and vanishes at reading distance.
244
+ // 2px, or in the dark theme the arc is separated from the plain border by little
245
+ // more than its saturation and vanishes at reading distance
334
246
  border: 2px solid var(--mw-corner-accent);
335
247
  border-radius: $sharp $round $sharp $round;
336
248
 
@@ -341,13 +253,9 @@
341
253
  }
342
254
  }
343
255
 
344
- // The scroll-reveal zone (utilities/_reveal.scss), as the range of the n-th
345
- // column of a row. Viewport lengths rather than percentages of the block: a
346
- // two-line counter band and a tall card then rise through the same region of
347
- // the screen instead of the short one snapping in. The zone starts 8dvh above
348
- // the bottom edge - right at the edge nobody sees the motion - and ends at
349
- // 34dvh, still in the lower third, so nothing is moving while someone already
350
- // reads it. Each further column of a row starts 4dvh later than the one before.
256
+ // The reveal zone as the range of the n-th column. Viewport lengths and not
257
+ // percentages of the block, so a short band and a tall card rise through the same
258
+ // region of the screen. Each further column starts 4dvh after the one before.
351
259
  @mixin reveal-range($column: 1) {
352
260
  $shift: 4dvh * ($column - 1);
353
261