maverick-wave 5.8.0 → 5.10.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.
- package/.claude/skills/mw-maverick-wave/SKILL.md +22 -12
- package/.claude/skills/mw-maverick-wave/examples/angular-services.md +10 -1
- package/.claude/skills/mw-maverick-wave/examples/static-landing-page.md +4 -3
- package/.claude/skills/mw-maverick-wave/references/components.md +23 -9
- package/.claude/skills/mw-maverick-wave/references/javascript.md +1 -1
- package/.claude/skills/mw-maverick-wave/references/layout.md +8 -3
- package/.claude/skills/mw-maverick-wave/references/theming.md +48 -25
- package/CHANGELOG.md +13 -0
- package/README.md +41 -24
- package/index.html +325 -20
- package/maverick-wave.min.css +61 -5
- package/maverick-wave.min.js +1 -1
- package/package.json +2 -2
- package/scripts/verify.js +7 -1
- package/src/js/main.js +24 -5
- package/src/partials/palette-container.html +105 -65
- package/src/partials/preview-container.html +136 -0
- package/src/partials/utilities-container.html +38 -0
- package/src/scss/abstracts/_functions.scss +31 -3
- package/src/scss/abstracts/_mixins.scss +62 -12
- package/src/scss/abstracts/_variables.scss +47 -21
- package/src/scss/base/_base.scss +97 -8
- package/src/scss/components/_badge.scss +1 -1
- package/src/scss/components/_buttons.scss +1 -1
- package/src/scss/components/_cards.scss +23 -14
- package/src/scss/components/_dropdown.scss +58 -1
- package/src/scss/components/_kanban.scss +10 -9
- package/src/scss/components/_lang-switch.scss +1 -1
- package/src/scss/components/_localhost-indicator.scss +1 -1
- package/src/scss/components/_modals.scss +13 -1
- package/src/scss/components/_pricing.scss +1 -1
- package/src/scss/components/_progress.scss +9 -5
- package/src/scss/components/_skeleton.scss +1 -1
- package/src/scss/components/_spinners.scss +5 -5
- package/src/scss/components/_stepper.scss +4 -1
- package/src/scss/components/_tabs.scss +28 -8
- package/src/scss/components/_tiles.scss +41 -16
- package/src/scss/components/_timelines.scss +12 -5
- package/src/scss/layout/_main.scss +1 -1
- package/src/scss/layout/_parallax.scss +10 -5
- package/src/scss/utilities/_elevation.scss +23 -0
- package/src/scss/utilities/_touch-targets.scss +1 -1
- package/src/scss/utilities/_variants.scss +74 -7
|
@@ -44,14 +44,14 @@ Load the one you need - do not read them all up front.
|
|
|
44
44
|
```html
|
|
45
45
|
<link
|
|
46
46
|
rel="stylesheet"
|
|
47
|
-
href="https://cdn.jsdelivr.net/npm/maverick-wave@5.
|
|
47
|
+
href="https://cdn.jsdelivr.net/npm/maverick-wave@5.9.0/maverick-wave.min.css"
|
|
48
48
|
/>
|
|
49
49
|
<link
|
|
50
50
|
rel="stylesheet"
|
|
51
51
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"
|
|
52
52
|
/>
|
|
53
53
|
...
|
|
54
|
-
<script src="https://cdn.jsdelivr.net/npm/maverick-wave@5.
|
|
54
|
+
<script src="https://cdn.jsdelivr.net/npm/maverick-wave@5.9.0/maverick-wave.min.js"></script>
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
Pin the version. The JS file is optional and only for server-rendered/static pages -
|
|
@@ -151,9 +151,9 @@ keys at all (negative gap is invalid CSS and is not generated).
|
|
|
151
151
|
`5xl` 3, `6xl` 4.3 rem.
|
|
152
152
|
|
|
153
153
|
**Breakpoints**: `xs` 375, `sm` 576, `md` 768, `lg` 992, `xl` 1200, `2xl` 1400 px.
|
|
154
|
-
Column grids and most components are mobile-first
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
Column grids and most components are mobile-first. The mixins emit range syntax
|
|
155
|
+
(`media-up` is `width >= bp`, `media-down` is `width < bp`), so the two never
|
|
156
|
+
overlap at the breakpoint itself and neither has to stop a fraction short of it.
|
|
157
157
|
|
|
158
158
|
**Overriding.** Everything the framework emits sits in
|
|
159
159
|
`@layer mw.reset, mw.base, mw.forms, mw.components, mw.layout, mw.utilities`.
|
|
@@ -187,16 +187,23 @@ Two shadows per level - a tight contact layer plus a wide ambient one:
|
|
|
187
187
|
`0` is explicitly flat. Never write a `box-shadow` by hand - the twelve one-off
|
|
188
188
|
values that used to exist are exactly what this replaced.
|
|
189
189
|
|
|
190
|
+
**Glow** (`mw-glow`, `mw-glow-{primary,secondary,info,success,warning,danger}`).
|
|
191
|
+
The same two-layer idea without the offset, so the surface reads as giving off
|
|
192
|
+
the light rather than casting a shadow. For the one element on a screen that has
|
|
193
|
+
to be seen first, and for nothing else.
|
|
194
|
+
|
|
190
195
|
**Motion** `--mw-duration-instant|fast|base|slow|slower` = 110/180/300/520/900ms
|
|
191
196
|
plus `--mw-duration-zoom` (650ms, for a large surface actually travelling),
|
|
192
197
|
`--mw-ease-out` (things arriving - the default), `--mw-ease-in-out` (A to B and
|
|
193
198
|
back), `--mw-ease-spring` (a pop). Two ready-made transitions:
|
|
194
199
|
`var(--mw-transition)` for hover and focus states, `var(--mw-transition-fast)`
|
|
195
200
|
for anything that should feel instant under the pointer. Both list their
|
|
196
|
-
properties explicitly rather than saying `all`.
|
|
201
|
+
properties explicitly rather than saying `all`. All six durations are multiplied
|
|
202
|
+
by `--mw-motion-scale` (default 1), the tempo counterpart to
|
|
203
|
+
`--mw-radius-scale`; `prefers-reduced-motion` overrides the result.
|
|
197
204
|
|
|
198
205
|
**Control sizes** `--mw-control-height-sm|base|lg` = 1.875 / 2.125 / 2.375rem (an
|
|
199
|
-
even 30 / 34 / 38px step) and
|
|
206
|
+
even 30 / 34 / 38px step, moved by `mw-density-compact` and `-roomy`) and
|
|
200
207
|
`--mw-control-font-sm|base|lg` = 0.8 / 0.9 / 1rem, shared by `mw-input`,
|
|
201
208
|
`mw-select`, `mw-textarea` and `mw-btn`. A field and the button beside it are
|
|
202
209
|
the same height by construction. Buttons run one font step above the fields.
|
|
@@ -264,7 +271,8 @@ feature frame) ·
|
|
|
264
271
|
`mw-skip-link` · `mw-row-split` (+ `center`) · `mw-text-numeric` /
|
|
265
272
|
`mw-text-currency` · `mw-text-truncate` / `mw-text-clamp-2..5` /
|
|
266
273
|
`mw-text-break` / `mw-text-nowrap` · `mw-text-balance` / `mw-text-pretty` /
|
|
267
|
-
`mw-text-eyebrow` / `mw-text-measure` · `mw-elevation-0..5` ·
|
|
274
|
+
`mw-text-eyebrow` / `mw-text-measure` · `mw-elevation-0..5` · `mw-glow` /
|
|
275
|
+
`mw-glow-{primary,secondary,info,success,warning,danger}` ·
|
|
268
276
|
`mw-corner-plain` ·
|
|
269
277
|
`mw-aspect-square|video|wide|portrait|photo` · `mw-d-{sm,md,lg,xl}-*` /
|
|
270
278
|
`mw-hide-mobile` / `mw-hide-desktop` · `mw-overflow-*` / `mw-snap-x` ·
|
|
@@ -342,10 +350,12 @@ feature frame) ·
|
|
|
342
350
|
and the open state come from, and it works without script. The framework JS
|
|
343
351
|
only adds Escape and click-outside. Writing your own div-plus-click loses all
|
|
344
352
|
of it.
|
|
345
|
-
17. **An open dropdown
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
353
|
+
17. **An open dropdown can be clipped by anything that hides its overflow.**
|
|
354
|
+
Where the browser supports anchor positioning the menu is `position: fixed`
|
|
355
|
+
and anchored to its trigger, so it escapes the clip on its own. Everywhere
|
|
356
|
+
else it is absolutely positioned and cut off. Either way the framework's own
|
|
357
|
+
containers (panel, card, tile, modal body, responsive table) lift the clip
|
|
358
|
+
while a menu is open. On your own container it is one line:
|
|
349
359
|
`:has(.mw-dropdown[open]) { overflow: visible }`.
|
|
350
360
|
18. **Never write `box-shadow` by hand.** Use `var(--mw-elevation-1..5)` or the
|
|
351
361
|
`mw-elevation-*` class. A hand-rolled shadow is the wrong colour in one of
|
|
@@ -18,7 +18,13 @@ const STORAGE_KEY = 'mw-theme';
|
|
|
18
18
|
|
|
19
19
|
@Injectable({ providedIn: 'root' })
|
|
20
20
|
export class ThemeService {
|
|
21
|
-
|
|
21
|
+
// With nothing stored the stylesheet already follows the OS, so the initial
|
|
22
|
+
// value only has to agree with what is on screen
|
|
23
|
+
readonly light = signal(
|
|
24
|
+
localStorage.getItem(STORAGE_KEY)
|
|
25
|
+
? localStorage.getItem(STORAGE_KEY) === 'light'
|
|
26
|
+
: matchMedia('(prefers-color-scheme: light)').matches
|
|
27
|
+
);
|
|
22
28
|
|
|
23
29
|
constructor() {
|
|
24
30
|
effect(() => {
|
|
@@ -27,6 +33,9 @@ export class ThemeService {
|
|
|
27
33
|
|
|
28
34
|
root.classList.add('mw-theme-switching');
|
|
29
35
|
document.body.classList.toggle('mw-theme-light', light);
|
|
36
|
+
// the explicit counterpart - without it a dark choice on a light machine
|
|
37
|
+
// falls back to the OS preference
|
|
38
|
+
document.body.classList.toggle('mw-theme-dark', !light);
|
|
30
39
|
void root.offsetHeight; // commit the colours with transitions off
|
|
31
40
|
root.classList.remove('mw-theme-switching');
|
|
32
41
|
|
|
@@ -20,7 +20,7 @@ accordion FAQ.
|
|
|
20
20
|
|
|
21
21
|
<link
|
|
22
22
|
rel="stylesheet"
|
|
23
|
-
href="https://cdn.jsdelivr.net/npm/maverick-wave@5.
|
|
23
|
+
href="https://cdn.jsdelivr.net/npm/maverick-wave@5.9.0/maverick-wave.min.css"
|
|
24
24
|
/>
|
|
25
25
|
<link
|
|
26
26
|
rel="stylesheet"
|
|
@@ -487,7 +487,7 @@ accordion FAQ.
|
|
|
487
487
|
<div class="mw-modal-backdrop" onclick="closeModal('demo')"></div>
|
|
488
488
|
</div>
|
|
489
489
|
|
|
490
|
-
<script src="https://cdn.jsdelivr.net/npm/maverick-wave@5.
|
|
490
|
+
<script src="https://cdn.jsdelivr.net/npm/maverick-wave@5.9.0/maverick-wave.min.js"></script>
|
|
491
491
|
<script>
|
|
492
492
|
// The only thing the shipped script does not cover: opening a modal.
|
|
493
493
|
// Closing works through .mw-modal-close, the backdrop is wired above.
|
|
@@ -505,7 +505,8 @@ accordion FAQ.
|
|
|
505
505
|
## Notes
|
|
506
506
|
|
|
507
507
|
- The theme toggle needs no code - the script persists the choice under
|
|
508
|
-
`localStorage['mw-theme']`, toggles `mw-theme-light`
|
|
508
|
+
`localStorage['mw-theme']`, toggles `mw-theme-light` / `mw-theme-dark` on
|
|
509
|
+
`<body>` and wraps the
|
|
509
510
|
flip in `mw-theme-switching` so it starts no transitions.
|
|
510
511
|
- The scroll spy sets `mw-active` on the `mw-navbar-link` whose `href` matches
|
|
511
512
|
the `section[id]` currently in view - the `<section id="…">` elements are
|
|
@@ -224,13 +224,21 @@ picker.
|
|
|
224
224
|
means it sits right, not left. Everything is vertically centered, so plain text
|
|
225
225
|
lines up with a button next to it. Actions that no longer fit next to each
|
|
226
226
|
other wrap.
|
|
227
|
-
- The
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
227
|
+
- The card measures **itself**, not the window - a card in a three-column grid
|
|
228
|
+
is just as narrow on a 1200px desktop as it is on a phone, and looks the same
|
|
229
|
+
in both. Every card declares `container: mw-card / inline-size` and takes
|
|
230
|
+
`width: 100%`, so it fills its slot instead of sizing to its content. Give a
|
|
231
|
+
card an explicit width if you need it to hug its content.
|
|
232
|
+
- What steps on the card's own width: the footer stacks to full-width children
|
|
233
|
+
below 360px, and the title, the subtitle and the body padding step up at
|
|
234
|
+
420px. The image height is the one that still follows the window, because it
|
|
235
|
+
is declared on the card itself and a container cannot query itself.
|
|
236
|
+
- `mw-tile` works the same way, and so do `mw-stepper` and both timelines: the
|
|
237
|
+
step labels shrink and the two-sided timeline collapses to one side once the
|
|
238
|
+
component is narrow, whatever the window does. One catch on tiles: a tile
|
|
239
|
+
clips its overflow, and being a container makes it the containing block for
|
|
240
|
+
`position: fixed`, so a `data-tooltip` **inside** a tile is cut off at its
|
|
241
|
+
edge. Cards do not clip and are unaffected.
|
|
234
242
|
- `mw-card-badge` (top right corner) and `mw-card-ribbon` (diagonal banner) are
|
|
235
243
|
absolutely positioned overlays; colour them with `mw-card-addon-primary`,
|
|
236
244
|
`-secondary`, `-success`, `-warning`, `-danger`, `-info`.
|
|
@@ -813,6 +821,10 @@ tables you know overflow, leave it off the ones that fit.
|
|
|
813
821
|
A board is a grid of equally wide lanes; a ticket is a plain `mw-card` with
|
|
814
822
|
`mw-kanban-card` on top. Everything except the counters and the composer is CSS.
|
|
815
823
|
|
|
824
|
+
The board puts as many lanes in a row as fit at 260px each and wraps the rest
|
|
825
|
+
onto the next. It measures its own width for that, so a board inside a panel or
|
|
826
|
+
a half-width column wraps on a desktop too.
|
|
827
|
+
|
|
816
828
|
```html
|
|
817
829
|
<div class="mw-kanban" style="--mw-kanban-column-min-height: 390px">
|
|
818
830
|
<div class="mw-kanban-column">
|
|
@@ -1207,8 +1219,10 @@ Parts: `mw-dropdown-menu`, `-item`, `-item-danger`, `-divider`, `-label`,
|
|
|
1207
1219
|
picks rather than acts. Icons inside items keep one column, so labels line up
|
|
1208
1220
|
whether or not every item has one.
|
|
1209
1221
|
|
|
1210
|
-
|
|
1211
|
-
|
|
1222
|
+
Where the browser supports anchor positioning the menu is `position: fixed`,
|
|
1223
|
+
anchored to its trigger and flipped into whichever side has room, so it escapes
|
|
1224
|
+
a clipping ancestor by itself. Everywhere else it is absolutely positioned and
|
|
1225
|
+
is clipped by any ancestor that hides its overflow. The framework's own containers lift that clip while a menu is open; on
|
|
1212
1226
|
your own it is `:has(.mw-dropdown[open]) { overflow: visible }`.
|
|
1213
1227
|
|
|
1214
1228
|
On a coarse pointer the rows grow to 2.75rem and the menu takes at least the
|
|
@@ -33,7 +33,7 @@ classes are the entire contract.
|
|
|
33
33
|
| Mobile nav | Toggles `open` on `mw-menu-btn` and `mw-navbar`, writes `aria-expanded` when the button is a `<button>`, closes on anchor click and on Escape (focus returns to the button) | One signal, bound to both; reset it on navigation end |
|
|
34
34
|
| Scroll spy | Sets `mw-active` on `mw-navbar-link` from the scroll position | Router-based: `routerLinkActive="mw-active"` |
|
|
35
35
|
| Anchor scrolling | Intercepts `a[href^="#"]` and runs its own eased scroll - duration scales with distance, capped at 1.4s, cancelled by wheel or touch. Lands on `scroll-padding-top`, moves focus to the target, writes the hash with `replaceState`, and measures a sticky target unpinned | The router; for in-page anchors `scrollIntoView({ behavior: 'smooth' })` or your own animation |
|
|
36
|
-
| Theme toggle | `localStorage['mw-theme']`, toggles `mw-theme-light` on `<body>` and `mw-active` on the toggle, wrapped in `mw-theme-switching` on `<html>` so the flip starts no transitions
|
|
36
|
+
| Theme toggle | `localStorage['mw-theme']`, toggles `mw-theme-light` / `mw-theme-dark` on `<body>` and `mw-active` on the toggle, wrapped in `mw-theme-switching` on `<html>` so the flip starts no transitions. With nothing stored it takes `prefers-color-scheme` and keeps following it | A theme service - see `examples/angular-services.md` |
|
|
37
37
|
| Progress bar | `IntersectionObserver` sets `width` from `data-value` | Bind `[style.width.%]="value()"` on `mw-progress-fill` |
|
|
38
38
|
| Slider | On `input`, sets `--value` (track fill) and `data-value` (badge text) | Bind `[style.--value.%]` and `[attr.data-value]` |
|
|
39
39
|
| Alerts | Close button adds `mw-alert-closing` (fade out), then `mw-alert-closed` (`display: none`) after `--mw-duration-base` | Remove the alert from the list/signal |
|
|
@@ -183,8 +183,8 @@ as the header's first child under the same condition.
|
|
|
183
183
|
`data-tooltip="..."` is a global attribute hook, not a class - it works on any
|
|
184
184
|
element and shows a tooltip above it on hover or keyboard focus. It is pure CSS
|
|
185
185
|
(a pseudo element on the trigger), which also means anything that clips its
|
|
186
|
-
overflow cuts it off: a scroll container, or a card carrying a ribbon -
|
|
187
|
-
cards do not clip. Close to the screen edge the bubble can run out of the
|
|
186
|
+
overflow cuts it off: a scroll container, a tile, or a card carrying a ribbon -
|
|
187
|
+
plain cards do not clip. Close to the screen edge the bubble can run out of the
|
|
188
188
|
viewport, so keep long tooltips off the outermost elements.
|
|
189
189
|
|
|
190
190
|
## Sections
|
|
@@ -444,7 +444,7 @@ automatically.
|
|
|
444
444
|
`mw-d-inline-block`, `mw-d-grid`, `mw-d-none`, `mw-d-contents`.
|
|
445
445
|
|
|
446
446
|
**Responsive display** - `mw-d-{sm|md|lg|xl}-{none|block|flex|inline-flex|grid|inline-block}`.
|
|
447
|
-
Mobile-first
|
|
447
|
+
Mobile-first like everything else here: `mw-d-md-flex` means
|
|
448
448
|
"flex from the md breakpoint up", and the unprefixed class beside it is what
|
|
449
449
|
applies below that. The pair `mw-d-none mw-d-md-flex` is hidden on a phone and a
|
|
450
450
|
row from a tablet on. `mw-hide-mobile` (gone below md) and `mw-hide-desktop`
|
|
@@ -469,6 +469,11 @@ with it; `mw-text-capitalize` is the plain transform.
|
|
|
469
469
|
**Elevation** - `mw-elevation-0` through `-5`, the same ramp every component
|
|
470
470
|
uses. See the scale table in `SKILL.md`. Never hand-roll a `box-shadow`.
|
|
471
471
|
|
|
472
|
+
**Glow** - `mw-glow` plus
|
|
473
|
+
`mw-glow-{primary,secondary,info,success,warning,danger}`. Elevation without the
|
|
474
|
+
offset, so the surface gives off the light instead of casting it. Once per
|
|
475
|
+
screen at most.
|
|
476
|
+
|
|
472
477
|
**Scroll entrance** - `mw-reveal` lets a block rise briefly as it scrolls into
|
|
473
478
|
view, driven by the browser's scroll timeline (`animation-timeline: view()`).
|
|
474
479
|
The block stays hidden while it is still below the bottom edge and has arrived
|
|
@@ -135,8 +135,19 @@ Two rules that prevent most colour bugs:
|
|
|
135
135
|
|
|
136
136
|
## Light & dark
|
|
137
137
|
|
|
138
|
-
-
|
|
139
|
-
|
|
138
|
+
- Every theme token is declared once on `:root` as `light-dark(light, dark)`.
|
|
139
|
+
Which half applies is decided by `color-scheme` on the element that **uses**
|
|
140
|
+
the token, and `:root` carries `color-scheme: light dark` - so a page follows
|
|
141
|
+
the OS until someone chooses otherwise.
|
|
142
|
+
- `mw-theme-light` and `mw-theme-dark` on `<body>` are that choice. They set
|
|
143
|
+
little more than `color-scheme`, and every token follows. `mw-theme-dark` is
|
|
144
|
+
not redundant: without it, a reader who picks dark on a machine set to light
|
|
145
|
+
would be pulled straight back to the OS preference.
|
|
146
|
+
- Chrome 119-122 and Safari 16.4-17.4 are in the browserslist but predate
|
|
147
|
+
`light-dark()`. `base/_base.scss` keeps the old route for them behind
|
|
148
|
+
`@supports` - dark on `:root`, light by class, no OS tracking.
|
|
149
|
+
- `--mw-hero-image-filter` is the one theme value that is not a colour and so
|
|
150
|
+
cannot ride along; it follows `prefers-color-scheme` instead.
|
|
140
151
|
- Card, footer and border are derived from the page background by scaling its
|
|
141
152
|
OKLch lightness and chroma by one factor. A card steps **away from the text
|
|
142
153
|
colour** - darker than the page in the dark theme, lighter in the light one.
|
|
@@ -176,7 +187,9 @@ Two rules that prevent most colour bugs:
|
|
|
176
187
|
or move it down with the bar.
|
|
177
188
|
- Persisting the choice, the toggle UI and the initial class are the
|
|
178
189
|
application's job in a SPA (`examples/angular-services.md`). The shipped JS
|
|
179
|
-
does it for static pages using `localStorage` under the key `mw-theme`.
|
|
190
|
+
does it for static pages using `localStorage` under the key `mw-theme`. With
|
|
191
|
+
nothing stored it takes the OS preference and keeps tracking it live, so
|
|
192
|
+
changing the system theme moves the page until the reader picks a side.
|
|
180
193
|
- **Suppress transitions while the class flips.** Colour changes on nearly every
|
|
181
194
|
element at once, and the shared `--mw-transition` turns that into thousands of
|
|
182
195
|
concurrent animations - on a documentation-sized page it is seconds of blocked
|
|
@@ -189,29 +202,35 @@ const root = document.documentElement;
|
|
|
189
202
|
|
|
190
203
|
root.classList.add('mw-theme-switching');
|
|
191
204
|
document.body.classList.toggle('mw-theme-light', isLight);
|
|
205
|
+
document.body.classList.toggle('mw-theme-dark', !isLight);
|
|
192
206
|
void root.offsetHeight; // commit the new colours with transitions off
|
|
193
207
|
root.classList.remove('mw-theme-switching');
|
|
194
208
|
```
|
|
195
209
|
|
|
196
210
|
## Site-wide variants
|
|
197
211
|
|
|
198
|
-
|
|
199
|
-
without a rebuild. They stack, and none of them need a class per element.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
|
204
|
-
|
|
|
205
|
-
| `mw-
|
|
206
|
-
| `mw-
|
|
207
|
-
| `mw-
|
|
208
|
-
| `mw-
|
|
209
|
-
| `mw-
|
|
210
|
-
| `mw-
|
|
211
|
-
| `mw-
|
|
212
|
-
| `mw-
|
|
213
|
-
| `mw-
|
|
214
|
-
| `mw-btn-
|
|
212
|
+
Fifteen classes on `<html>` and six custom properties retune the whole look
|
|
213
|
+
without a rebuild. They stack, and none of them need a class per element. The
|
|
214
|
+
showcase at https://maverick-wave.m1well.com has a picker for the set, and its
|
|
215
|
+
URL carries the choice - a link is how a look gets agreed on before it is built.
|
|
216
|
+
|
|
217
|
+
| Class | Effect |
|
|
218
|
+
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
219
|
+
| `mw-corners-even` | Drops the surface signature - every card, panel and modal becomes an evenly rounded `radius('md')` box |
|
|
220
|
+
| `mw-accent-single` | `--mw-secondary-color` follows the primary; `mw-btn-secondary` turns outline so the two stay apart |
|
|
221
|
+
| `mw-shadows-flat` | Elevation 1-3 to `none`. The dropdown (4) and the modal (5) keep their shadow |
|
|
222
|
+
| `mw-surfaces-flush` | Card, panel and footer background drop to the page colour and the border is redrawn from the ink, because the old one is a neighbouring shade of a surface that is now the page |
|
|
223
|
+
| `mw-hover-static` | No hover travels - lifts, image zooms and slides go. Colour and border still respond |
|
|
224
|
+
| `mw-scroll-static` | No scroll entrance - `mw-reveal` and `mw-reveal-stagger` blocks sit where they land |
|
|
225
|
+
| `mw-sections-plain` | The diagonal hatch behind `mw-section-alternate` collapses into the page colour |
|
|
226
|
+
| `mw-headings-caps` | `h1`-`h3` in capitals with 0.045em tracking |
|
|
227
|
+
| `mw-links-underline` | `mw-link`, `mw-link-muted` and `mw-btn-link` carry their underline at rest. The hover signal moves to the stroke, which thickens to 3px - `mw-link` has no colour change of its own |
|
|
228
|
+
| `mw-btn-pill` | `mw-btn` fully rounded. Form fields keep their own radius |
|
|
229
|
+
| `mw-btn-square` | `mw-btn` to `border-radius: 0`. The other end of the same axis, and unlike `--mw-radius-scale: 0` it leaves the rest of the page rounded |
|
|
230
|
+
| `mw-btn-glass` | Filled buttons become tinted glass: translucent wash, vertical ramp, specular top edge. No `backdrop-filter` - it would trap a fixed-position tooltip inside the button |
|
|
231
|
+
| `mw-btn-tactile` | The four solid variants stand on a 3px edge mixed toward black and travel its full height on `:active`. Outline, ghost and link have no fill to darken and are untouched |
|
|
232
|
+
| `mw-density-compact` | Card and panel padding and the three control heights drop one step down the spacing scale. Type is untouched - `--mw-root-font-size` is the switch that scales everything together |
|
|
233
|
+
| `mw-density-roomy` | The same one step up |
|
|
215
234
|
|
|
216
235
|
| Property | Effect |
|
|
217
236
|
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -220,20 +239,24 @@ without a rebuild. They stack, and none of them need a class per element.
|
|
|
220
239
|
| `--mw-font-family-heading` | Headline typeface; body copy is untouched |
|
|
221
240
|
| `--mw-container-width` | Where the content stops growing |
|
|
222
241
|
| `--mw-section-padding-block` | Air above and below each section |
|
|
242
|
+
| `--mw-motion-scale` | Multiplies all six duration tokens - `0.6` brisk, `1.6` relaxed. `prefers-reduced-motion` still overrides it |
|
|
223
243
|
|
|
224
244
|
**Writing your own.** A variant that retunes a _theme-bound_ token - anything in
|
|
225
|
-
the dark/light maps
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
Target both:
|
|
245
|
+
the dark/light maps - cannot be written on `:root` alone: `mw-theme-light`
|
|
246
|
+
re-declares those on `<body>`, which shadows the root value for the entire
|
|
247
|
+
subtree and the switch does nothing in light mode. Target both:
|
|
229
248
|
|
|
230
249
|
```scss
|
|
231
250
|
:root.my-variant,
|
|
232
251
|
:root.my-variant .mw-theme-light {
|
|
233
|
-
--mw-
|
|
252
|
+
--mw-card-background: var(--mw-page-background);
|
|
234
253
|
}
|
|
235
254
|
```
|
|
236
255
|
|
|
256
|
+
`--mw-elevation-1..5` are the exception and need only `:root`: the ramp is one
|
|
257
|
+
set for both themes and reaches the theme through `--mw-shadow-near/-far`, so
|
|
258
|
+
there is no second declaration further down the tree to shadow it.
|
|
259
|
+
|
|
237
260
|
The same trap the other way round: never copy a theme alias _into_ a token on
|
|
238
261
|
`:root` (`--mw-x: var(--mw-page-background)`), because up there it still holds
|
|
239
262
|
the dark value. Write that rule on the element instead.
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@ Patch releases are only for test purposes - here I only document major and minor
|
|
|
6
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [5.10.0] - 2026-09-13
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- technical issues
|
|
14
|
+
|
|
15
|
+
## [5.9.0] - 2026-09-13
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- finalized showcase variants
|
|
20
|
+
- changed some little glow up
|
|
21
|
+
|
|
9
22
|
## [5.8.0] - 2026-09-12
|
|
10
23
|
|
|
11
24
|
### Added
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ The result is a framework that balances utility with simplicity, offering develo
|
|
|
23
23
|
- A five-step elevation ramp and a motion scale, so every shadow and every transition in the framework comes from one place
|
|
24
24
|
- Mobile as a first-class target: 44px touch targets on a coarse pointer, modals that become bottom sheets, press states on everything, and hover effects that do not latch after a tap
|
|
25
25
|
- Easy Customization via CSS Custom Properties
|
|
26
|
-
- Built-in Light & Dark Mode
|
|
26
|
+
- Built-in Light & Dark Mode - follows the OS by default, switchable per reader
|
|
27
27
|
- SCSS Source Files for advanced customization (Dart Sass, `@use`/`@forward`)
|
|
28
28
|
- Modals as `<div>` or as `<dialog>` - the latter brings the focus trap, Escape and the inert background from the platform
|
|
29
29
|
- Native form validation is styled through `:user-invalid`, alongside the class-driven error states for reactive forms
|
|
@@ -42,12 +42,12 @@ The result is a framework that balances utility with simplicity, offering develo
|
|
|
42
42
|
<title>My MaverickWave Project</title>
|
|
43
43
|
<link
|
|
44
44
|
rel="stylesheet"
|
|
45
|
-
href="https://cdn.jsdelivr.net/npm/maverick-wave@5.
|
|
45
|
+
href="https://cdn.jsdelivr.net/npm/maverick-wave@5.9.0/maverick-wave.min.css"
|
|
46
46
|
/>
|
|
47
47
|
</head>
|
|
48
48
|
<body>
|
|
49
49
|
<!-- Your content here -->
|
|
50
|
-
<script src="https://cdn.jsdelivr.net/npm/maverick-wave@5.
|
|
50
|
+
<script src="https://cdn.jsdelivr.net/npm/maverick-wave@5.9.0/maverick-wave.min.js"></script>
|
|
51
51
|
</body>
|
|
52
52
|
</html>
|
|
53
53
|
```
|
|
@@ -130,7 +130,8 @@ assembled rather than designed.
|
|
|
130
130
|
:root {
|
|
131
131
|
/* Elevation. Each level is two shadows - a tight contact layer that gives
|
|
132
132
|
the box weight, and a wide ambient one that says how high it floats.
|
|
133
|
-
|
|
133
|
+
One ramp for both themes; the theme arrives through the shadow tokens
|
|
134
|
+
the levels are built from. */
|
|
134
135
|
--mw-elevation-1: /* resting: inputs, tags, small controls */;
|
|
135
136
|
--mw-elevation-2: /* raised: cards and panels at rest */;
|
|
136
137
|
--mw-elevation-3: /* floating: a card under the pointer */;
|
|
@@ -347,32 +348,42 @@ layer of its own:
|
|
|
347
348
|
|
|
348
349
|
### Site-wide variants
|
|
349
350
|
|
|
350
|
-
|
|
351
|
+
Fifteen classes on `<html>` retune the whole look without touching markup or
|
|
351
352
|
rebuilding. They stack.
|
|
352
353
|
|
|
353
|
-
| Class
|
|
354
|
-
|
|
|
355
|
-
| `mw-corners-even`
|
|
356
|
-
| `mw-accent-single`
|
|
357
|
-
| `mw-shadows-flat`
|
|
358
|
-
| `mw-surfaces-flush`
|
|
359
|
-
| `mw-hover-static`
|
|
360
|
-
| `mw-scroll-static`
|
|
361
|
-
| `mw-sections-plain`
|
|
362
|
-
| `mw-headings-caps`
|
|
363
|
-
| `mw-
|
|
364
|
-
| `mw-btn-
|
|
365
|
-
| `mw-btn-
|
|
366
|
-
| `mw-btn-
|
|
367
|
-
|
|
368
|
-
|
|
354
|
+
| Class | Effect |
|
|
355
|
+
| -------------------- | ----------------------------------------------------------------------- |
|
|
356
|
+
| `mw-corners-even` | Drops the surface signature - every panel becomes an evenly rounded box |
|
|
357
|
+
| `mw-accent-single` | One brand colour instead of two; `mw-btn-secondary` turns outline |
|
|
358
|
+
| `mw-shadows-flat` | Elevation 1-3 to `none`; dropdown and modal keep theirs |
|
|
359
|
+
| `mw-surfaces-flush` | Cards, panels and footer on the page colour, held by their border |
|
|
360
|
+
| `mw-hover-static` | No hover travels - lifts and image zooms go, colour still responds |
|
|
361
|
+
| `mw-scroll-static` | No scroll entrance - `mw-reveal` blocks sit where they land |
|
|
362
|
+
| `mw-sections-plain` | The hatch behind `mw-section-alternate` collapses into the page colour |
|
|
363
|
+
| `mw-headings-caps` | `h1`-`h3` in capitals |
|
|
364
|
+
| `mw-links-underline` | Links underlined at rest; the stroke thickens on hover |
|
|
365
|
+
| `mw-btn-pill` | Fully rounded buttons; form fields keep their radius |
|
|
366
|
+
| `mw-btn-square` | Buttons cut to a hard corner while the page keeps its radius |
|
|
367
|
+
| `mw-btn-glass` | Filled buttons become a translucent wash with a lit top edge |
|
|
368
|
+
| `mw-btn-tactile` | Filled buttons stand on a darker edge and sink onto it when pressed |
|
|
369
|
+
| `mw-density-compact` | Less padding in cards, panels and controls; type stays put |
|
|
370
|
+
| `mw-density-roomy` | More of the same |
|
|
371
|
+
|
|
372
|
+
Six properties do the rest: `--mw-radius-scale` multiplies every radius (`0`
|
|
369
373
|
squares the framework off), `--mw-root-font-size` moves the whole rem scale,
|
|
370
|
-
|
|
374
|
+
`--mw-motion-scale` the tempo of every transition, plus
|
|
375
|
+
`--mw-font-family-heading`, `--mw-container-width` and
|
|
371
376
|
`--mw-section-padding-block`.
|
|
372
377
|
|
|
378
|
+
The [showcase](https://maverick-wave.m1well.com) has a picker for all of them,
|
|
379
|
+
and the URL carries whatever is set - send that link and the next person opens
|
|
380
|
+
the page exactly as you left it. The panel prints the same setup as markup and
|
|
381
|
+
CSS to copy into a project.
|
|
382
|
+
|
|
373
383
|
A variant of your own that retunes a theme-bound token has to target
|
|
374
384
|
`:root.your-class` **and** `:root.your-class .mw-theme-light` - the light theme
|
|
375
|
-
re-declares those on `<body>` and would shadow a root-only value.
|
|
385
|
+
re-declares those on `<body>` and would shadow a root-only value. The elevation
|
|
386
|
+
tokens are the exception and need only `:root`.
|
|
376
387
|
|
|
377
388
|
### SCSS Source
|
|
378
389
|
|
|
@@ -458,7 +469,7 @@ above):
|
|
|
458
469
|
> change detection. The behaviors it covers (accordion, tabs, modal close, mobile
|
|
459
470
|
> nav, scroll spy, theme toggle, progress bars, sliders, alerts, galleries) are a
|
|
460
471
|
> few lines each in a component - the framework's state classes are the whole
|
|
461
|
-
> contract. Theme switching, for example, is a
|
|
472
|
+
> contract. Theme switching, for example, is a pair of classes on `<body>`:
|
|
462
473
|
|
|
463
474
|
```typescript
|
|
464
475
|
// theme.service.ts - mw-theme-switching suppresses the transitions the flip
|
|
@@ -467,10 +478,16 @@ const root = document.documentElement;
|
|
|
467
478
|
|
|
468
479
|
root.classList.add('mw-theme-switching');
|
|
469
480
|
document.body.classList.toggle('mw-theme-light', isLight);
|
|
481
|
+
// the explicit counterpart - without it a dark choice on a light machine
|
|
482
|
+
// falls back to the OS preference
|
|
483
|
+
document.body.classList.toggle('mw-theme-dark', !isLight);
|
|
470
484
|
void root.offsetHeight;
|
|
471
485
|
root.classList.remove('mw-theme-switching');
|
|
472
486
|
```
|
|
473
487
|
|
|
488
|
+
With neither class the page follows `prefers-color-scheme`, so an app that has
|
|
489
|
+
nothing stored yet can simply leave both off.
|
|
490
|
+
|
|
474
491
|
The `mw-field` wrapper groups label, control, hint and error. Bind the error
|
|
475
492
|
state yourself - the framework does not style Angular's `ng-invalid` /
|
|
476
493
|
`ng-touched` classes:
|