maverick-wave 5.4.0 → 5.5.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 +2 -2
- package/.claude/skills/mw-maverick-wave/examples/angular-services.md +91 -0
- package/.claude/skills/mw-maverick-wave/examples/static-landing-page.md +5 -4
- package/.claude/skills/mw-maverick-wave/references/components.md +3 -0
- package/.claude/skills/mw-maverick-wave/references/javascript.md +19 -2
- package/.claude/skills/mw-maverick-wave/references/layout.md +20 -14
- package/.claude/skills/mw-maverick-wave/references/theming.md +4 -0
- package/CHANGELOG.md +11 -0
- package/CLAUDE.md +1 -1
- package/README.md +3 -4
- package/index.html +257 -0
- package/maverick-wave.min.css +4 -4
- package/maverick-wave.min.js +1 -1
- package/package.json +2 -2
- package/scripts/verify.js +4 -7
- package/src/js/main.js +157 -2
- package/src/partials/documentation-container.html +4 -0
- package/src/partials/footer-container.html +3 -0
- package/src/partials/history-container.html +67 -0
- package/src/partials/palette-container.html +142 -0
- package/src/scss/abstracts/_mixins.scss +6 -5
- package/src/scss/base/_reset.scss +4 -1
- package/src/scss/components/_accordions.scss +31 -7
- package/src/scss/components/_toasts.scss +1 -1
- package/src/scss/form-elements/_slider.scss +5 -23
- package/src/scss/layout/_header-reveal.scss +33 -0
- package/src/scss/layout/_parallax.scss +20 -0
- package/src/scss/utilities/_reveal.scss +22 -0
- package/.impeccable/config.local.json +0 -5
- package/.impeccable/hook.cache.json +0 -75
|
@@ -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.5.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.5.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 -
|
|
@@ -369,3 +369,94 @@ about the class name.
|
|
|
369
369
|
|
|
370
370
|
`mw-progress-percent` only appends the `%` sign - the number comes from you, and
|
|
371
371
|
so does the width.
|
|
372
|
+
|
|
373
|
+
## Scroll reveal
|
|
374
|
+
|
|
375
|
+
`mw-reveal` runs on the browser's scroll timeline and needs nothing from you -
|
|
376
|
+
except in Firefox, which ships none, where the block appears without motion.
|
|
377
|
+
This directive is the Firefox half: it adds to the class, never replaces it.
|
|
378
|
+
|
|
379
|
+
```ts
|
|
380
|
+
import {
|
|
381
|
+
DestroyRef,
|
|
382
|
+
Directive,
|
|
383
|
+
ElementRef,
|
|
384
|
+
afterNextRender,
|
|
385
|
+
inject,
|
|
386
|
+
signal,
|
|
387
|
+
} from '@angular/core';
|
|
388
|
+
|
|
389
|
+
@Directive({
|
|
390
|
+
selector: '[mwReveal]',
|
|
391
|
+
host: {
|
|
392
|
+
'[class.mw-reveal-hidden]': "state() === 'hidden'",
|
|
393
|
+
'[class.mw-reveal-run]': "state() === 'run'",
|
|
394
|
+
'[style.animation-delay.ms]': 'delay()',
|
|
395
|
+
},
|
|
396
|
+
})
|
|
397
|
+
export class MwRevealDirective {
|
|
398
|
+
private readonly host =
|
|
399
|
+
inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;
|
|
400
|
+
|
|
401
|
+
protected readonly state = signal<'off' | 'hidden' | 'run'>('off');
|
|
402
|
+
protected readonly delay = signal(0);
|
|
403
|
+
|
|
404
|
+
constructor() {
|
|
405
|
+
const destroyRef = inject(DestroyRef);
|
|
406
|
+
|
|
407
|
+
afterNextRender(() => {
|
|
408
|
+
if (CSS.supports('animation-timeline', 'view()')) return;
|
|
409
|
+
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
|
410
|
+
// Already on screen: past its entry range in a timeline browser too
|
|
411
|
+
if (this.host.getBoundingClientRect().top < innerHeight) return;
|
|
412
|
+
|
|
413
|
+
this.state.set('hidden');
|
|
414
|
+
|
|
415
|
+
const observer = new IntersectionObserver(
|
|
416
|
+
([entry]) => {
|
|
417
|
+
if (!entry.isIntersecting) return;
|
|
418
|
+
observer.disconnect();
|
|
419
|
+
this.delay.set(this.columnDelay());
|
|
420
|
+
this.state.set('run');
|
|
421
|
+
},
|
|
422
|
+
{ threshold: 0.15 }
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
observer.observe(this.host);
|
|
426
|
+
destroyRef.onDestroy(() => observer.disconnect());
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// The wave a grid draws in CSS, read back from the columns the browser
|
|
431
|
+
// rendered - one on a phone, so the stagger disappears with them.
|
|
432
|
+
private columnDelay(): number {
|
|
433
|
+
const grid = this.host.parentElement;
|
|
434
|
+
if (!grid?.classList.contains('mw-reveal-stagger')) return 0;
|
|
435
|
+
const columns =
|
|
436
|
+
getComputedStyle(grid).gridTemplateColumns.split(' ').length;
|
|
437
|
+
return ([...grid.children].indexOf(this.host) % columns) * 90;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
A single block carries `mw-reveal`, a grid carries `mw-reveal-stagger` and its
|
|
443
|
+
children carry nothing but the directive:
|
|
444
|
+
|
|
445
|
+
```html
|
|
446
|
+
<article class="mw-card mw-reveal" mwReveal>...</article>
|
|
447
|
+
|
|
448
|
+
<div class="mw-grid-4 mw-reveal-stagger">
|
|
449
|
+
@for (item of items(); track item.id) {
|
|
450
|
+
<article class="mw-card" mwReveal>...</article>
|
|
451
|
+
}
|
|
452
|
+
</div>
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Unlike the shipped script this also covers what a route renders later - the
|
|
456
|
+
directive runs per element, not once per page.
|
|
457
|
+
|
|
458
|
+
`mw-header-reveal` and `mw-parallax` need the same treatment and the same
|
|
459
|
+
`CSS.supports` guard. The header is a class toggled past a scroll threshold; the
|
|
460
|
+
parallax writes `--mw-parallax-progress` (0 to 1) on each `mw-parallax-media`
|
|
461
|
+
from a `requestAnimationFrame` loop, reading every layer's rect before writing
|
|
462
|
+
to any of them.
|
|
@@ -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.5.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.5.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.
|
|
@@ -523,8 +523,9 @@ accordion FAQ.
|
|
|
523
523
|
- `mw-reveal-stagger` goes on the grid (or `mw-columns-*`) and reveals every
|
|
524
524
|
child as it scrolls in, the second and third of each three a beat later.
|
|
525
525
|
`mw-reveal` on a single block reveals that block as one. Both are off under
|
|
526
|
-
`prefers-reduced-motion
|
|
527
|
-
|
|
526
|
+
`prefers-reduced-motion`. A page like this one loads the script, so Firefox -
|
|
527
|
+
which has no scroll timelines - runs the same entrance off an observer;
|
|
528
|
+
without the script the blocks stand in place, never invisible.
|
|
528
529
|
- `mw-offer` on a card pushes the price row to the bottom of the body, so the
|
|
529
530
|
prices in a row line up. The `mw-card-feature` frame around the middle plan
|
|
530
531
|
reaches 29px above its card - the `mw-mt-10` on the grid is that room.
|
|
@@ -536,6 +536,9 @@ still renders the same, for markup written before 4.12.
|
|
|
536
536
|
Open state = `mw-active` on header **and** content (bare `active` still works
|
|
537
537
|
but is deprecated). The icon rotates via the **header** state - putting the
|
|
538
538
|
class on the icon instead does nothing. Content taller than 500px scrolls.
|
|
539
|
+
The `mw-accordion-content-inner` wrapper is required and not decoration: the
|
|
540
|
+
panel opens by animating a grid row and that wrapper is the row, so content
|
|
541
|
+
placed straight into `mw-accordion-content` never collapses when closed.
|
|
539
542
|
Toggling is JS - see `references/javascript.md`; the shipped script keeps
|
|
540
543
|
`aria-expanded` in step when the header is a button.
|
|
541
544
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What `maverick-wave.min.js` is
|
|
4
4
|
|
|
5
|
-
One vanilla IIFE, no dependencies, ~
|
|
5
|
+
One vanilla IIFE, no dependencies, ~23 kB. It queries the DOM **once** on
|
|
6
6
|
`DOMContentLoaded` and attaches listeners. There is no re-init API, no
|
|
7
7
|
`MutationObserver`, no exported module - it is built for a server-rendered or
|
|
8
8
|
static page.
|
|
@@ -44,9 +44,26 @@ classes are the entire contract.
|
|
|
44
44
|
| Calendar | Renders the month or week grid from `data-calendar="month | week"`, pages with `data-calendar-nav`, draws the status dots from `data-calendar-markers`, toggles `mw-selected`and emits`mw-calendar-select` | Render the cells from a signal and bind `mw-calendar-adjacent`, `mw-calendar-weekend`, `mw-calendar-today` and `mw-selected` yourself; none of the `data-calendar-*` attributes are needed |
|
|
45
45
|
| Localhost indicator | On a local hostname, prepends `mw-localhost-indicator-pulse` to the header when it carries `mw-localhost-indicator-activated` | Render the element conditionally |
|
|
46
46
|
| Header login button | Swaps the FontAwesome lock icon | Bind the icon class |
|
|
47
|
-
| Color swatches | Showcase-only (prints computed hex values)
|
|
47
|
+
| Color swatches | Showcase-only (prints computed hex values); exposes `window.mwRefreshColorSwatches` to read them again after a root colour changed | Not needed |
|
|
48
48
|
| Dropdown | Delegated to the document: closes the open `mw-dropdown` on Escape, on a click elsewhere and on a click on a `mw-dropdown-item`, and returns focus to the `summary` | The `<details>` does the opening, the keyboard and the state on its own. Rebuild only the two behaviours markup cannot express - or bind `[attr.open]` and keep them in the component |
|
|
49
49
|
| Language switcher | Keeps the trigger's flag and code in step with the chosen item, moves `mw-active` and `aria-current`, and fires `mw-language-change` (`detail: { lang, name }`) on the switcher | Bind the trigger from your locale signal and switch the language in your own i18n service; the menu itself is a `<details>` and needs nothing |
|
|
50
|
+
| Scroll reveal | Firefox only: an `IntersectionObserver` adds `mw-reveal-hidden` to what is still below the fold and swaps it for `mw-reveal-run` on entry, with an `animation-delay` per grid column | A directive per element - see `examples/angular-services.md` |
|
|
51
|
+
| Header reveal | Firefox only: toggles `mw-header-away` and `mw-announcement-away` past 270px of scroll, and adds the transition class one frame later so the bar does not slide away on load | The same two classes bound to a scroll signal, behind the same guard |
|
|
52
|
+
| Parallax | Firefox only: writes `--mw-parallax-progress` (0 to 1) on every `mw-parallax-media` from a `requestAnimationFrame` loop | The same, reading every layer's rect before writing to any of them |
|
|
53
|
+
|
|
54
|
+
## Scroll-driven animations
|
|
55
|
+
|
|
56
|
+
Four things ride the browser's own scroll timeline: `mw-reveal`,
|
|
57
|
+
`mw-header-reveal`, `mw-parallax` and the `mw-progress-fill` scrub. In Chrome,
|
|
58
|
+
Edge and Safari they need no script at all. Firefox ships no scroll timelines,
|
|
59
|
+
so there - and only there - the shipped JS stands in for them. Each one checks
|
|
60
|
+
`CSS.supports('animation-timeline', ...)` first and does nothing where the
|
|
61
|
+
browser has it.
|
|
62
|
+
|
|
63
|
+
Without the script Firefox loses the motion and nothing else: cards stand in
|
|
64
|
+
place, the picture holds still. The exception is `mw-header-reveal`, where the
|
|
65
|
+
bar then sits over the hero from the first paint - a layout difference, not a
|
|
66
|
+
missing effect.
|
|
50
67
|
|
|
51
68
|
## Modals and progress bars
|
|
52
69
|
|
|
@@ -142,8 +142,9 @@ A fixed `mw-announcement` rides along without a class of its own - both cover
|
|
|
142
142
|
the height of the pair, so they arrive as one block rather than the ribbon
|
|
143
143
|
catching up. Focus inside either brings both back regardless of the scroll
|
|
144
144
|
position, so tabbing never lands on a link that is off screen (WCAG 2.4.11).
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
Under `prefers-reduced-motion` the bar is simply there, and so it is without
|
|
146
|
+
scroll timelines unless `maverick-wave.min.js` is on the page - in Firefox that
|
|
147
|
+
is what rides it in (`javascript.md`).
|
|
147
148
|
|
|
148
149
|
**Localhost indicator.** Put `mw-localhost-indicator-activated` on the header
|
|
149
150
|
and the shipped JS prepends a pulsing bar when the host is localhost/127.0.0.1/
|
|
@@ -322,8 +323,10 @@ is taken on a phone. The bobbing stops under `prefers-reduced-motion`.
|
|
|
322
323
|
**Parallax** - `mw-parallax` on the container plus a `mw-parallax-media` child
|
|
323
324
|
moves the picture into its own layer. The container drops its own background and
|
|
324
325
|
the layer reads `--mw-hero-background`, so the image stays configured in one
|
|
325
|
-
place. Both modes run on the browser's scroll timeline
|
|
326
|
-
`background-attachment: fixed`, which iOS ignores.
|
|
326
|
+
place. Both modes run on the browser's scroll timeline, and on no
|
|
327
|
+
`background-attachment: fixed`, which iOS ignores. Firefox has neither timeline;
|
|
328
|
+
there the shipped JS moves the layers instead, and without it the picture simply
|
|
329
|
+
sits still (`javascript.md`).
|
|
327
330
|
|
|
328
331
|
```html
|
|
329
332
|
<header class="mw-header mw-header-reveal">...</header>
|
|
@@ -467,19 +470,22 @@ with it; `mw-text-capitalize` is the plain transform.
|
|
|
467
470
|
uses. See the scale table in `SKILL.md`. Never hand-roll a `box-shadow`.
|
|
468
471
|
|
|
469
472
|
**Scroll entrance** - `mw-reveal` lets a block rise briefly as it scrolls into
|
|
470
|
-
view, driven by the browser's scroll timeline (`animation-timeline: view()`)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
off, and a browser without scroll timelines
|
|
476
|
-
|
|
477
|
-
|
|
473
|
+
view, driven by the browser's scroll timeline (`animation-timeline: view()`).
|
|
474
|
+
The block stays hidden while it is still below the bottom edge and has arrived
|
|
475
|
+
three fifths of the way in - measured along its own entry, so it is never still
|
|
476
|
+
transparent once it stands in its place, whatever its height. A block taller
|
|
477
|
+
than the screen is capped at one viewport by the entry phase itself. Doubly guarded:
|
|
478
|
+
`prefers-reduced-motion` turns it off, and a browser without scroll timelines
|
|
479
|
+
renders the block in place instead of leaving it invisible. Firefox is that
|
|
480
|
+
browser - there the shipped JS runs the entrance off an `IntersectionObserver`,
|
|
481
|
+
so a page without the script keeps the block and loses only the motion
|
|
482
|
+
(`javascript.md`). Put it on section content, not on the section itself - a
|
|
483
|
+
screen-high band finishes its entrance before its content is halfway up.
|
|
478
484
|
|
|
479
485
|
A row of cards crosses the viewport edge together, so `mw-reveal` on each of
|
|
480
486
|
them rises as one slab. `mw-reveal-stagger` goes on the **grid** instead: every
|
|
481
|
-
child reveals, and each one in a row
|
|
482
|
-
a wave across the row. One class, nothing per card. On `mw-grid-2` to `mw-grid-5` and
|
|
487
|
+
child reveals, and each one in a row a tenth of that entry after the one
|
|
488
|
+
before it - a wave across the row. One class, nothing per card. On `mw-grid-2` to `mw-grid-5` and
|
|
483
489
|
their `-lg` variants the wave follows the actual columns at every breakpoint:
|
|
484
490
|
four steps in a four-column row, two once it has collapsed to two. Any other
|
|
485
491
|
container - `mw-columns-*`, a layout of your own - gets a fixed cycle of three.
|
|
@@ -9,6 +9,10 @@ borders, the theme's surface stack, the ink variant the dark theme needs. That i
|
|
|
9
9
|
the difference to pre-3.4.0, where derived values were baked in at compile time
|
|
10
10
|
and a palette switch meant setting 41 variables.
|
|
11
11
|
|
|
12
|
+
Writing those tokens on `<html>` at runtime is how a palette is swapped without
|
|
13
|
+
a reload. It repaints the same breadth the theme flip does and needs the same
|
|
14
|
+
guard against a page-wide interpolation - see **Light & dark** below.
|
|
15
|
+
|
|
12
16
|
Browser floor for that: `color-mix()` **and** `oklch(from ...)` - Chrome 119+,
|
|
13
17
|
Safari 16.4+, Firefox 128+. The same range is declared as `browserslist` in
|
|
14
18
|
`package.json`, so Autoprefixer and cssnano target exactly it.
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ 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.5.0] - 2026-09-12
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- showcase themes
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- some firefox fixes
|
|
18
|
+
- scrolling
|
|
19
|
+
|
|
9
20
|
## [5.4.0] - 2026-09-11
|
|
10
21
|
|
|
11
22
|
### Added
|
package/CLAUDE.md
CHANGED
|
@@ -38,7 +38,7 @@ There are no test or lint scripts.
|
|
|
38
38
|
|
|
39
39
|
**JavaScript** (`src/js/main.js`) - Single file. All components auto-initialize on `DOMContentLoaded`. Includes: gallery and image sliders, theme toggle (persisted to localStorage, transitions suppressed during the flip), accordion, mobile nav, progress bar (IntersectionObserver), scroll spy, tabs (which also get their ARIA and arrow-key handling here), alerts/toasts, modals, range inputs, dropdowns, language switcher, checkbox lists, kanban board, calendar.
|
|
40
40
|
|
|
41
|
-
**HTML showcase** (`index.html` + `src/partials/`) - The top-level `index.html` uses `@@include()` syntax to pull in
|
|
41
|
+
**HTML showcase** (`index.html` + `src/partials/`) - The top-level `index.html` uses `@@include()` syntax to pull in 63 partials from `src/partials/`. These compile into `dist/index.html`.
|
|
42
42
|
|
|
43
43
|
## Naming Conventions
|
|
44
44
|
|
package/README.md
CHANGED
|
@@ -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.5.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.5.0/maverick-wave.min.js"></script>
|
|
51
51
|
</body>
|
|
52
52
|
</html>
|
|
53
53
|
```
|
|
@@ -515,9 +515,8 @@ maverick-wave/
|
|
|
515
515
|
│ ├── utilities/ # Spacing, flex, display helpers
|
|
516
516
|
│ └── main.scss # SCSS entry point
|
|
517
517
|
├── .claude/
|
|
518
|
-
│ ├── commands/mw.md # Claude Code slash command (Angular quick reference)
|
|
519
518
|
│ └── skills/
|
|
520
|
-
│ └── maverick-wave/ # Claude Code skill: full usage guide + examples
|
|
519
|
+
│ └── mw-maverick-wave/ # Claude Code skill: full usage guide + examples
|
|
521
520
|
├── scripts/verify.js # Class and token consistency check (npm run verify)
|
|
522
521
|
├── .prettierrc.json # Prettier configuration
|
|
523
522
|
├── gulpfile.js # Gulp tasks configuration
|
package/index.html
CHANGED
|
@@ -118,6 +118,144 @@
|
|
|
118
118
|
height: 50px;
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
/* Palette switcher, showcase only. The framework derives every tone from
|
|
123
|
+
the root colors, so the panel writes nothing but those. */
|
|
124
|
+
.palette-fab {
|
|
125
|
+
position: fixed;
|
|
126
|
+
right: 18px;
|
|
127
|
+
bottom: 18px;
|
|
128
|
+
z-index: 400;
|
|
129
|
+
width: 52px;
|
|
130
|
+
height: 52px;
|
|
131
|
+
border-radius: 50%;
|
|
132
|
+
border: 1px solid var(--mw-border-accent);
|
|
133
|
+
background: var(--mw-primary-color);
|
|
134
|
+
color: var(--mw-primary-accent-text-color);
|
|
135
|
+
box-shadow: var(--mw-elevation-4);
|
|
136
|
+
font-size: 1.25rem;
|
|
137
|
+
cursor: pointer;
|
|
138
|
+
transition: var(--mw-transition);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.palette-fab:hover {
|
|
142
|
+
background: var(--mw-primary-color-hover);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.palette-fab:active {
|
|
146
|
+
transform: scale(0.92);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.palette-panel {
|
|
150
|
+
position: fixed;
|
|
151
|
+
right: 18px;
|
|
152
|
+
bottom: 82px;
|
|
153
|
+
z-index: 400;
|
|
154
|
+
width: 300px;
|
|
155
|
+
padding: 18px;
|
|
156
|
+
border-radius: 12px;
|
|
157
|
+
border: 1px solid var(--mw-border);
|
|
158
|
+
background: var(--mw-card-background);
|
|
159
|
+
box-shadow: var(--mw-elevation-5);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.palette-heading {
|
|
163
|
+
font-size: 1.1rem;
|
|
164
|
+
margin-bottom: 4px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.palette-hint {
|
|
168
|
+
font-size: 0.8rem;
|
|
169
|
+
line-height: 1.35;
|
|
170
|
+
color: var(--mw-text-muted-color);
|
|
171
|
+
margin-bottom: 14px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.palette-grid {
|
|
175
|
+
display: grid;
|
|
176
|
+
grid-template-columns: 1fr 1fr;
|
|
177
|
+
gap: 6px;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.palette-item {
|
|
181
|
+
display: flex;
|
|
182
|
+
align-items: center;
|
|
183
|
+
gap: 8px;
|
|
184
|
+
padding: 6px 8px;
|
|
185
|
+
border-radius: 8px;
|
|
186
|
+
border: 1px solid var(--mw-border);
|
|
187
|
+
background: transparent;
|
|
188
|
+
color: var(--mw-text-color);
|
|
189
|
+
font-size: 0.8rem;
|
|
190
|
+
text-align: left;
|
|
191
|
+
cursor: pointer;
|
|
192
|
+
transition: var(--mw-transition);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.palette-item::before {
|
|
196
|
+
content: '';
|
|
197
|
+
width: 16px;
|
|
198
|
+
height: 16px;
|
|
199
|
+
flex-shrink: 0;
|
|
200
|
+
border-radius: 50%;
|
|
201
|
+
background: linear-gradient(
|
|
202
|
+
135deg,
|
|
203
|
+
var(--swatch-primary) 50%,
|
|
204
|
+
var(--swatch-secondary) 50%
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.palette-item:hover,
|
|
209
|
+
.palette-item.mw-active {
|
|
210
|
+
border-color: var(--mw-primary-text-color);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.palette-item.mw-active {
|
|
214
|
+
background: var(--mw-primary-background);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.palette-custom {
|
|
218
|
+
display: flex;
|
|
219
|
+
gap: 10px;
|
|
220
|
+
margin-top: 14px;
|
|
221
|
+
padding-top: 12px;
|
|
222
|
+
border-top: 1px solid var(--mw-border);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.palette-field {
|
|
226
|
+
flex: 1;
|
|
227
|
+
display: flex;
|
|
228
|
+
flex-direction: column;
|
|
229
|
+
gap: 4px;
|
|
230
|
+
font-size: 0.75rem;
|
|
231
|
+
color: var(--mw-text-muted-color);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.palette-field input {
|
|
235
|
+
width: 100%;
|
|
236
|
+
height: 30px;
|
|
237
|
+
padding: 2px;
|
|
238
|
+
border: 1px solid var(--mw-border);
|
|
239
|
+
border-radius: 6px;
|
|
240
|
+
background: var(--mw-card-background);
|
|
241
|
+
cursor: pointer;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@media (max-width: 576px) {
|
|
245
|
+
.palette-fab {
|
|
246
|
+
right: 12px;
|
|
247
|
+
bottom: 12px;
|
|
248
|
+
width: 46px;
|
|
249
|
+
height: 46px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.palette-panel {
|
|
253
|
+
right: 12px;
|
|
254
|
+
left: 12px;
|
|
255
|
+
bottom: 68px;
|
|
256
|
+
width: auto;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
121
259
|
</style>
|
|
122
260
|
|
|
123
261
|
<script src="maverick-wave.min.js"></script>
|
|
@@ -538,6 +676,17 @@
|
|
|
538
676
|
</div>
|
|
539
677
|
</div>
|
|
540
678
|
</section>
|
|
679
|
+
<!-- History section -->
|
|
680
|
+
<section id="history" class="mw-section mw-section-alternate">
|
|
681
|
+
<div class="mw-container">
|
|
682
|
+
<h2 class="mw-section-title">History</h2>
|
|
683
|
+
<p class="mw-section-intro">
|
|
684
|
+
The major releases so far. Everything in between - the minors and
|
|
685
|
+
the patch releases - sits in the changelog.
|
|
686
|
+
</p>
|
|
687
|
+
@@include('./src/partials/history-container.html')
|
|
688
|
+
</div>
|
|
689
|
+
</section>
|
|
541
690
|
</main>
|
|
542
691
|
|
|
543
692
|
<!-- Footer -->
|
|
@@ -546,6 +695,9 @@
|
|
|
546
695
|
@@include('./src/partials/footer-container.html')
|
|
547
696
|
</div>
|
|
548
697
|
</footer>
|
|
698
|
+
|
|
699
|
+
<!-- Palette switcher -->
|
|
700
|
+
@@include('./src/partials/palette-container.html')
|
|
549
701
|
<script>
|
|
550
702
|
// just for the showcase (login button triggers login error)
|
|
551
703
|
document.addEventListener('DOMContentLoaded', () => {
|
|
@@ -629,6 +781,111 @@
|
|
|
629
781
|
apply(cycleSwitch.querySelector('.mw-active'));
|
|
630
782
|
});
|
|
631
783
|
|
|
784
|
+
// just for the showcase: the palette switcher. Every tone in the framework
|
|
785
|
+
// is derived from the root colors with color-mix(), so writing those on the
|
|
786
|
+
// root element repaints the page - bar, ink, borders and tints included.
|
|
787
|
+
// Nothing is persisted - a palette is for looking at, a reload is the way back.
|
|
788
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
789
|
+
const fab = document.getElementById('palette-fab');
|
|
790
|
+
const panel = document.getElementById('palette-panel');
|
|
791
|
+
const primaryInput = document.getElementById('palette-primary');
|
|
792
|
+
const secondaryInput = document.getElementById('palette-secondary');
|
|
793
|
+
const items = [...document.querySelectorAll('.palette-item')];
|
|
794
|
+
if (!fab || !panel) return;
|
|
795
|
+
|
|
796
|
+
const root = document.documentElement;
|
|
797
|
+
|
|
798
|
+
const DARK_INK = '#131925';
|
|
799
|
+
const LIGHT_INK = '#f2f6fc';
|
|
800
|
+
|
|
801
|
+
const luminance = (hex) => {
|
|
802
|
+
const [r, g, b] = [1, 3, 5].map((i) => {
|
|
803
|
+
const channel = parseInt(hex.slice(i, i + 2), 16) / 255;
|
|
804
|
+
return channel <= 0.04045
|
|
805
|
+
? channel / 12.92
|
|
806
|
+
: ((channel + 0.055) / 1.055) ** 2.4;
|
|
807
|
+
});
|
|
808
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
const contrast = (a, b) => {
|
|
812
|
+
const [high, low] = [luminance(a), luminance(b)].sort(
|
|
813
|
+
(x, y) => y - x
|
|
814
|
+
);
|
|
815
|
+
return (high + 0.05) / (low + 0.05);
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
// Measured, not thresholded - a lightness cutoff gets a saturated mid
|
|
819
|
+
// tone wrong
|
|
820
|
+
const ink = (fill) =>
|
|
821
|
+
contrast(fill, DARK_INK) > contrast(fill, LIGHT_INK)
|
|
822
|
+
? DARK_INK
|
|
823
|
+
: LIGHT_INK;
|
|
824
|
+
|
|
825
|
+
// Cuts the transition the new colours started: the whole page
|
|
826
|
+
// interpolating at once is the expensive part, and the hex labels in the
|
|
827
|
+
// Colors section read computed values, so they need the landed ones
|
|
828
|
+
const settle = () => {
|
|
829
|
+
root.classList.add('mw-theme-switching');
|
|
830
|
+
void root.offsetHeight;
|
|
831
|
+
root.classList.remove('mw-theme-switching');
|
|
832
|
+
window.mwRefreshColorSwatches?.();
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
const paint = (name, color) => {
|
|
836
|
+
root.style.setProperty(`--mw-${name}-color`, color);
|
|
837
|
+
root.style.setProperty(`--mw-${name}-accent-text-color`, ink(color));
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
items.forEach((item) => {
|
|
841
|
+
const { primary, secondary, dark, light } = item.dataset;
|
|
842
|
+
item.style.setProperty('--swatch-primary', primary);
|
|
843
|
+
item.style.setProperty('--swatch-secondary', secondary);
|
|
844
|
+
|
|
845
|
+
item.addEventListener('click', () => {
|
|
846
|
+
items.forEach((other) =>
|
|
847
|
+
other.classList.toggle('mw-active', other === item)
|
|
848
|
+
);
|
|
849
|
+
paint('primary', primary);
|
|
850
|
+
paint('secondary', secondary);
|
|
851
|
+
root.style.setProperty('--mw-dark-page-background', dark);
|
|
852
|
+
root.style.setProperty('--mw-light-page-background', light);
|
|
853
|
+
primaryInput.value = primary;
|
|
854
|
+
secondaryInput.value = secondary;
|
|
855
|
+
settle();
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
// A hand-picked color belongs to no preset - the page backgrounds stay put
|
|
860
|
+
[
|
|
861
|
+
['primary', primaryInput],
|
|
862
|
+
['secondary', secondaryInput],
|
|
863
|
+
].forEach(([name, input]) => {
|
|
864
|
+
input.addEventListener('input', () => {
|
|
865
|
+
items.forEach((item) => item.classList.remove('mw-active'));
|
|
866
|
+
paint(name, input.value);
|
|
867
|
+
});
|
|
868
|
+
// Dragging the picker fires `input` per pixel - the rest waits for the end
|
|
869
|
+
input.addEventListener('change', settle);
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
const toggle = (open) => {
|
|
873
|
+
panel.hidden = !open;
|
|
874
|
+
fab.setAttribute('aria-expanded', String(open));
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
fab.addEventListener('click', (event) => {
|
|
878
|
+
event.stopPropagation();
|
|
879
|
+
toggle(panel.hidden);
|
|
880
|
+
});
|
|
881
|
+
document.addEventListener('click', (event) => {
|
|
882
|
+
if (!panel.hidden && !panel.contains(event.target)) toggle(false);
|
|
883
|
+
});
|
|
884
|
+
document.addEventListener('keydown', (event) => {
|
|
885
|
+
if (event.key === 'Escape') toggle(false);
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
|
|
632
889
|
// for the modals - the framework handles both shapes, div and <dialog>
|
|
633
890
|
const openModal = window.mwOpenModal;
|
|
634
891
|
const closeModal = window.mwCloseModal;
|