maverick-wave 4.27.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 (140) hide show
  1. package/.claude/settings.local.json +29 -1
  2. package/.claude/skills/mw-maverick-wave/SKILL.md +42 -25
  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 +90 -5
  6. package/.claude/skills/mw-maverick-wave/references/forms.md +14 -2
  7. package/.claude/skills/mw-maverick-wave/references/javascript.md +18 -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 +17 -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 +266 -75
  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/header-utilities-container.html +192 -5
  40. package/src/partials/home-container.html +10 -20
  41. package/src/partials/input-group-container.html +11 -2
  42. package/src/partials/kbd-container.html +1 -1
  43. package/src/partials/login-container.html +5 -1
  44. package/src/partials/media-container.html +2 -2
  45. package/src/partials/modals-container.html +22 -32
  46. package/src/partials/pricing-container.html +9 -9
  47. package/src/partials/skeleton-container.html +6 -6
  48. package/src/partials/stepper-container.html +2 -2
  49. package/src/partials/tables-container.html +12 -12
  50. package/src/partials/tabs-container.html +17 -27
  51. package/src/partials/tags-container.html +23 -17
  52. package/src/partials/techstack-bucket-container.html +13 -0
  53. package/src/partials/tiles-container.html +26 -9
  54. package/src/partials/typography-container.html +7 -6
  55. package/src/partials/utilities-container.html +35 -15
  56. package/src/scss/_layers.scss +10 -0
  57. package/src/scss/abstracts/_index.scss +6 -0
  58. package/src/scss/abstracts/_mixins.scss +61 -153
  59. package/src/scss/abstracts/_variables.scss +123 -319
  60. package/src/scss/base/_base.scss +170 -190
  61. package/src/scss/base/_reset.scss +106 -104
  62. package/src/scss/base/_typography.scss +153 -151
  63. package/src/scss/components/_accordions.scss +97 -97
  64. package/src/scss/components/_alerts.scss +71 -70
  65. package/src/scss/components/_announcement.scss +71 -70
  66. package/src/scss/components/_avatars.scss +130 -142
  67. package/src/scss/components/_badge.scss +131 -132
  68. package/src/scss/components/_blog-post.scss +223 -220
  69. package/src/scss/components/_breadcrumbs.scss +93 -92
  70. package/src/scss/components/_button-bar.scss +102 -110
  71. package/src/scss/components/_buttons.scss +319 -324
  72. package/src/scss/components/_calendar.scss +251 -252
  73. package/src/scss/components/_cards.scss +359 -386
  74. package/src/scss/components/_code.scss +190 -194
  75. package/src/scss/components/_coming-soon.scss +77 -75
  76. package/src/scss/components/_content-slider.scss +90 -89
  77. package/src/scss/components/_divider.scss +55 -53
  78. package/src/scss/components/_dropdown.scss +179 -185
  79. package/src/scss/components/_empty-state.scss +57 -57
  80. package/src/scss/components/_flag.scss +325 -0
  81. package/src/scss/components/_gallery.scss +131 -133
  82. package/src/scss/components/_index.scss +2 -0
  83. package/src/scss/components/_info.scss +151 -167
  84. package/src/scss/components/_kanban.scss +332 -341
  85. package/src/scss/components/_kbd.scss +40 -38
  86. package/src/scss/components/_lang-switch.scss +108 -0
  87. package/src/scss/components/_links.scss +21 -28
  88. package/src/scss/components/_lists.scss +510 -514
  89. package/src/scss/components/_localhost-indicator.scss +26 -24
  90. package/src/scss/components/_media.scss +22 -20
  91. package/src/scss/components/_meta-info.scss +35 -33
  92. package/src/scss/components/_modals.scss +277 -186
  93. package/src/scss/components/_pagination.scss +81 -87
  94. package/src/scss/components/_panels.scss +99 -97
  95. package/src/scss/components/_pricing.scss +183 -241
  96. package/src/scss/components/_progress.scss +112 -89
  97. package/src/scss/components/_prose.scss +35 -33
  98. package/src/scss/components/_ratings.scss +40 -38
  99. package/src/scss/components/_segmented.scss +82 -84
  100. package/src/scss/components/_skeleton.scss +66 -72
  101. package/src/scss/components/_spinners.scss +155 -159
  102. package/src/scss/components/_stepper.scss +116 -116
  103. package/src/scss/components/_tables.scss +155 -148
  104. package/src/scss/components/_tabs.scss +232 -141
  105. package/src/scss/components/_tags.scss +109 -107
  106. package/src/scss/components/_techstack-bucket.scss +123 -122
  107. package/src/scss/components/_testimonial.scss +72 -70
  108. package/src/scss/components/_theme-toggle.scss +53 -51
  109. package/src/scss/components/_tiles.scss +174 -174
  110. package/src/scss/components/_timelines.scss +282 -280
  111. package/src/scss/components/_toasts.scss +66 -72
  112. package/src/scss/form-elements/_checkbox.scss +117 -132
  113. package/src/scss/form-elements/_form.scss +149 -115
  114. package/src/scss/form-elements/_input-group.scss +99 -103
  115. package/src/scss/form-elements/_input.scss +74 -74
  116. package/src/scss/form-elements/_login.scss +48 -48
  117. package/src/scss/form-elements/_prefilled.scss +53 -51
  118. package/src/scss/form-elements/_radio.scss +95 -107
  119. package/src/scss/form-elements/_select.scss +42 -42
  120. package/src/scss/form-elements/_slider.scss +117 -132
  121. package/src/scss/form-elements/_textarea.scss +68 -54
  122. package/src/scss/form-elements/_toggle.scss +127 -140
  123. package/src/scss/layout/_footer.scss +172 -171
  124. package/src/scss/layout/_grid.scss +268 -302
  125. package/src/scss/layout/_header.scss +426 -427
  126. package/src/scss/layout/_home.scss +28 -27
  127. package/src/scss/layout/_main.scss +275 -238
  128. package/src/scss/layout/_page-header.scss +38 -36
  129. package/src/scss/layout/_section.scss +150 -124
  130. package/src/scss/main.scss +1 -1
  131. package/src/scss/utilities/_accessibility.scss +46 -45
  132. package/src/scss/utilities/_aspect.scss +22 -20
  133. package/src/scss/utilities/_corner.scss +23 -5
  134. package/src/scss/utilities/_display.scss +76 -70
  135. package/src/scss/utilities/_elevation.scss +13 -11
  136. package/src/scss/utilities/_flex.scss +83 -85
  137. package/src/scss/utilities/_reveal.scss +40 -63
  138. package/src/scss/utilities/_spacing.scss +63 -64
  139. package/src/scss/utilities/_text.scss +139 -141
  140. package/src/scss/utilities/_touch-targets.scss +130 -147
@@ -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