maverick-wave 5.28.0 → 5.29.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 (49) hide show
  1. package/.claude/skills/mw-maverick-wave/SKILL.md +4 -2
  2. package/.claude/skills/mw-maverick-wave/references/components.md +212 -0
  3. package/.claude/skills/mw-maverick-wave/references/javascript.md +6 -1
  4. package/.claude/skills/mw-maverick-wave/references/theming.md +53 -51
  5. package/CHANGELOG.md +10 -0
  6. package/CLAUDE.md +4 -4
  7. package/README.md +2 -2
  8. package/index.html +65 -0
  9. package/maverick-wave.min.css +2 -2
  10. package/maverick-wave.min.js +1 -1
  11. package/package.json +2 -2
  12. package/scripts/verify.js +2 -1
  13. package/src/assets/images/photo-balloon.svg +35 -0
  14. package/src/assets/images/photo-forest-path.svg +31 -0
  15. package/src/assets/images/photo-lighthouse.svg +42 -0
  16. package/src/assets/images/photo-palm.svg +27 -0
  17. package/src/assets/images/photo-peak.svg +27 -0
  18. package/src/assets/images/photo-tower.svg +34 -0
  19. package/src/assets/images/photo-waterfall.svg +32 -0
  20. package/src/assets/images/story-aurora.svg +36 -0
  21. package/src/assets/images/story-beach.svg +34 -0
  22. package/src/assets/images/story-stars.svg +35 -0
  23. package/src/assets/media/story-sunrise.jpg +0 -0
  24. package/src/assets/media/story-sunrise.mp4 +0 -0
  25. package/src/js/main.js +403 -0
  26. package/src/partials/avatars-container.html +50 -0
  27. package/src/partials/calendar-container.html +36 -38
  28. package/src/partials/feed-container.html +506 -0
  29. package/src/partials/item-lists-container.html +80 -130
  30. package/src/partials/kanban-container.html +17 -294
  31. package/src/partials/mosaic-container.html +173 -0
  32. package/src/partials/panels-container.html +113 -162
  33. package/src/partials/portrait-gallery-container.html +175 -0
  34. package/src/partials/stories-container.html +182 -0
  35. package/src/partials/tables-container.html +21 -40
  36. package/src/scss/abstracts/_mixins.scss +2 -0
  37. package/src/scss/abstracts/_variables.scss +3 -0
  38. package/src/scss/components/_avatars.scss +52 -0
  39. package/src/scss/components/_cards.scss +7 -3
  40. package/src/scss/components/_feed.scss +203 -0
  41. package/src/scss/components/_index.scss +6 -0
  42. package/src/scss/components/_lightbox.scss +178 -0
  43. package/src/scss/components/_mosaic.scss +83 -0
  44. package/src/scss/components/_overlay-btn.scss +47 -0
  45. package/src/scss/components/_portrait-gallery.scss +180 -0
  46. package/src/scss/components/_stories.scss +443 -0
  47. package/src/scss/components/_tiles.scss +4 -1
  48. package/src/scss/utilities/_fit.scss +29 -0
  49. package/src/scss/utilities/_index.scss +1 -0
@@ -309,8 +309,10 @@ feature frame) ·
309
309
  `inverted`) · `mw-flag` (+ 23 country codes) ·
310
310
  `mw-section-nav` (`references/layout.md`)
311
311
 
312
- **Media & content** `mw-avatar` (+ `initials`, `group`) · `mw-gallery` ·
313
- `mw-image-slider` · `mw-blog-post` · `mw-testimonial` (+ `-source`, `-detail`,
312
+ **Media & content** `mw-avatar` (+ `initials`, `group`, `landscape`,
313
+ `portrait`) · `mw-gallery` · `mw-image-slider` · `mw-mosaic` (+ lightbox) ·
314
+ `mw-portrait-gallery` · `mw-feed` · `mw-stories` / `mw-story-reel` ·
315
+ `mw-overlay-btn` · `mw-blog-post` · `mw-testimonial` (+ `-source`, `-detail`,
314
316
  `-date`, `-featured`) · `mw-prose` · `mw-media` (+ `-caption`) · `mw-code-block` /
315
317
  `mw-terminal` · `mw-techstack-bucket` · `mw-coming-soon` · `mw-divider` ·
316
318
  `mw-kbd` · `mw-list` family
@@ -1473,6 +1473,13 @@ deprecated). Any card fits into the content wrapper.
1473
1473
 
1474
1474
  - Sizes: `mw-avatar-xs` 32, `-sm` 64, default 96, `-lg` 144, `-xl` 210 px.
1475
1475
  - Shape: `mw-avatar-square`.
1476
+ - `mw-avatar-landscape` (3:2) and `mw-avatar-portrait` (9:16) for a photo that
1477
+ is not square - meant for `-lg` and `-xl`, with 20px corners that
1478
+ `mw-avatar-square` still sharpens. A landscape avatar keeps the height of its
1479
+ size step, a portrait one the width; on `-lg` a portrait is 2:3 instead, as
1480
+ tall as the 9:16 one and wider.
1481
+ - Every avatar carries the accent arc on its top-right corner, inside the frame
1482
+ (`--mw-corner-accent`). It stays as it is on hover.
1476
1483
  - Colours `mw-avatar-primary`, `-secondary`, `-success`, `-warning`, `-danger`,
1477
1484
  `-info` tint the **border**. On `mw-avatar-initials` the same class also tints
1478
1485
  the **surface**, so both can be combined.
@@ -1802,6 +1809,211 @@ current button carry `active`. `mw-image-slider` is its own query container
1802
1809
  switch at 420px and 560px of slider width, because the slider caps at 600px and
1803
1810
  past a tablet the viewport says nothing about the room it has.
1804
1811
 
1812
+ ## Mosaic
1813
+
1814
+ Photos of any shape in one grid; a tap opens the lightbox.
1815
+
1816
+ ```html
1817
+ <div class="mw-mosaic">
1818
+ <figure class="mw-mosaic-item mw-mosaic-big">
1819
+ <a href="ridge-full.jpg">
1820
+ <img
1821
+ src="ridge.jpg"
1822
+ alt="Mountain range at sunrise"
1823
+ width="800"
1824
+ height="450"
1825
+ loading="lazy"
1826
+ />
1827
+ </a>
1828
+ <figcaption>First light on the ridge.</figcaption>
1829
+ </figure>
1830
+ <figure class="mw-mosaic-item">…</figure>
1831
+ </div>
1832
+ ```
1833
+
1834
+ - Two columns on a phone, four at most, from the width of the box it sits in.
1835
+ - The script shapes each tile from the photo's proportions - the `width` and
1836
+ `height` attributes, else the natural size once loaded: `mw-mosaic-wide` from
1837
+ 1.3:1, `mw-mosaic-tall` from 1:1.25, square otherwise. A shape class set by
1838
+ hand wins; `mw-mosaic-big` (2×2) is only ever set by hand. Without the script
1839
+ every tile is square.
1840
+ - `grid-auto-flow: dense` fills the holes and moves tiles away from their source
1841
+ order. `reading-flow: grid-rows` makes Tab follow the eye where the browser
1842
+ has it (Chromium so far).
1843
+ - Gaps: `mw-mosaic-gap-sm`, `-gap-lg`.
1844
+ - The tile crops, the lightbox does not. The `figcaption` is hidden in the tile
1845
+ and shown in the lightbox.
1846
+
1847
+ ### Lightbox
1848
+
1849
+ A click on a link that holds an image - inside `mw-mosaic` or any element with
1850
+ `data-mw-lightbox` - opens the photo full screen instead of following the link.
1851
+ The script builds the viewer once, a `<dialog class="mw-lightbox">`, with one
1852
+ slide per image of the group in reading order, the full image from the link's
1853
+ `href` and the caption copied from the `figcaption`. Swipe, arrow keys or the
1854
+ arrows page through it; Escape, the cross or a click on the stage beside the
1855
+ photo close it, and focus returns to the tile of the last photo viewed. A click
1856
+ with a modifier key stays a link.
1857
+
1858
+ Without `main.js`, build it yourself: a `<dialog class="mw-lightbox">` holding
1859
+ `mw-lightbox-bar` (with `mw-lightbox-prev`, `mw-lightbox-count`,
1860
+ `mw-lightbox-next` and `mw-lightbox-close`, all `mw-overlay-btn`) and
1861
+ `mw-lightbox-track` with one `<figure class="mw-lightbox-slide">` per photo. The
1862
+ track is a scroll-snap strip; the page scroll is locked while the dialog is open.
1863
+
1864
+ ## Portrait gallery
1865
+
1866
+ ```html
1867
+ <div class="mw-portrait-gallery">
1868
+ <div
1869
+ class="mw-portrait-gallery-track"
1870
+ tabindex="0"
1871
+ aria-label="Portrait photos"
1872
+ >
1873
+ <figure class="mw-portrait-gallery-slide">
1874
+ <img
1875
+ src="cape.jpg"
1876
+ alt="Lighthouse on a cliff at dusk"
1877
+ width="600"
1878
+ height="800"
1879
+ loading="lazy"
1880
+ />
1881
+ <figcaption>
1882
+ <strong>North Cape</strong>
1883
+ The lamp comes on just after the sun goes.
1884
+ </figcaption>
1885
+ </figure>
1886
+ </div>
1887
+ </div>
1888
+ ```
1889
+
1890
+ - Upright 3:4 slides in a native scroll-snap strip, the next one peeking in.
1891
+ One slide below 560px of gallery width, two and a quarter from there, three
1892
+ from 880px - a container query, so a gallery in a narrow column stays at one.
1893
+ The height is capped at 78svh, which a landscape phone needs.
1894
+ - `mw-portrait-gallery-tall`: 9:16, for stories, reels and screenshots. A
1895
+ `<video>` works in place of an `<img>`.
1896
+ - Gaps: `mw-portrait-gallery-gap-sm`, `-gap-lg`.
1897
+ - The script adds the arrows (`mw-portrait-gallery-prev` / `-next`, for a mouse
1898
+ only) and the dots (`mw-portrait-gallery-dots` / `-dot`, below 560px, the
1899
+ current one `mw-active`).
1900
+ - A photo more than 1.4× off the slide's shape is shown whole over a blurred
1901
+ copy of itself instead of cropped: the script sets `mw-fit-contain` on the
1902
+ slide and prepends a `mw-fit-backdrop` image, and checks again on every
1903
+ resize. `mw-fit-contain` set by hand forces it, `mw-fit-cover` on the slide
1904
+ turns it off.
1905
+
1906
+ ## Photo feed
1907
+
1908
+ The three-column grid from a phone's photo app, in a framed box that scrolls.
1909
+
1910
+ ```html
1911
+ <div class="mw-feed">
1912
+ <div class="mw-feed-item">
1913
+ <button type="button" popovertarget="post-cape">
1914
+ <img
1915
+ src="cape.jpg"
1916
+ alt="Lighthouse on a cliff at dusk"
1917
+ width="600"
1918
+ height="800"
1919
+ loading="lazy"
1920
+ />
1921
+ </button>
1922
+ <figure class="mw-feed-post" id="post-cape" popover>
1923
+ <button
1924
+ type="button"
1925
+ class="mw-overlay-btn mw-feed-post-close"
1926
+ popovertarget="post-cape"
1927
+ popovertargetaction="hide"
1928
+ aria-label="Close"
1929
+ >
1930
+ &#120299;
1931
+ </button>
1932
+ <img
1933
+ src="cape.jpg"
1934
+ alt="Lighthouse on a cliff at dusk"
1935
+ width="600"
1936
+ height="800"
1937
+ loading="lazy"
1938
+ />
1939
+ <figcaption>
1940
+ <p>Stood there until the lamp came on.</p>
1941
+ <time datetime="2026-08-12">12 August 2026</time>
1942
+ </figcaption>
1943
+ </figure>
1944
+ </div>
1945
+ </div>
1946
+ ```
1947
+
1948
+ - 3:4 tiles, 2px apart; `mw-feed-square` for square ones.
1949
+ - Past three rows the box scrolls, with a strip of the next row in view so it
1950
+ reads as scrollable. `mw-feed-rows-2` shows two rows, `mw-feed-plain` drops
1951
+ the frame.
1952
+ - No script: the tile is a button with `popovertarget`, the post a `popover`.
1953
+ In Angular that is `[attr.popovertarget]` and a matching `[id]`. A tile can be
1954
+ an `<a>` to a page of its own instead.
1955
+ - `time` and `small` in the caption are set as a muted line. The page scroll is
1956
+ locked while a post is open.
1957
+
1958
+ ## Story archive
1959
+
1960
+ ```html
1961
+ <div class="mw-stories" aria-label="Story archive">
1962
+ <button type="button" class="mw-story" popovertarget="story-coast">
1963
+ <span class="mw-story-cover"><img src="beach.jpg" alt="" /></span>
1964
+ <span class="mw-story-label">Coast</span>
1965
+ </button>
1966
+ <div class="mw-story-reel" id="story-coast" popover>
1967
+ <div class="mw-story-reel-head">
1968
+ <span>Coast</span>
1969
+ <label class="mw-overlay-btn mw-story-reel-pause"
1970
+ ><input type="checkbox" aria-label="Pause"
1971
+ /></label>
1972
+ <button
1973
+ type="button"
1974
+ class="mw-overlay-btn"
1975
+ popovertarget="story-coast"
1976
+ popovertargetaction="hide"
1977
+ aria-label="Close"
1978
+ >
1979
+ &#120299;
1980
+ </button>
1981
+ </div>
1982
+ <img src="beach.jpg" alt="Beach at sunset" loading="lazy" />
1983
+ <video
1984
+ src="sunrise.mp4"
1985
+ poster="sunrise.jpg"
1986
+ autoplay
1987
+ muted
1988
+ loop
1989
+ playsinline
1990
+ ></video>
1991
+ </div>
1992
+ </div>
1993
+ ```
1994
+
1995
+ - The row scrolls sideways, with a shadow on the side that has more behind it;
1996
+ a mouse gets a thin scrollbar.
1997
+ - Playback is CSS: every frame shows for five seconds, a segmented bar in the
1998
+ head counts them off, and holding the story (`:active`) or ticking the pause
1999
+ checkbox stops it. Full screen on a phone, phone-shaped (9:16) from `sm` up.
2000
+ - Twelve frames at most - the timing is written out for twelve. Later frames
2001
+ are hidden and the story ends after the twelfth.
2002
+ - Under `prefers-reduced-motion` nothing plays: the frames become a vertical
2003
+ strip to swipe, and the bar follows the scroll where the browser has scroll
2004
+ timelines.
2005
+ - The script adds tap zones (`mw-story-reel-prev` on the left 30%, `-next` on
2006
+ the rest), arrow keys, focus on the close button when the story opens, a video
2007
+ that starts when its frame comes up and stops with the story, and closes the
2008
+ story at its end.
2009
+
2010
+ ## Overlay button
2011
+
2012
+ `mw-overlay-btn` is the round 44px button that sits on a photo - dark and
2013
+ translucent in both themes, with light ink and an inline `<svg>` sized for it.
2014
+ `:disabled` hides it. The lightbox, the portrait gallery, the feed post and the
2015
+ story reel use it for their close and arrow buttons.
2016
+
1805
2017
  ## Blog post
1806
2018
 
1807
2019
  ```html
@@ -76,6 +76,10 @@ its own through `light-dark()`.
76
76
  | Checkbox lists | Adds `mw-selected` to the `li`, emits a `checkboxToggle` event, exposes `window.mwToggleCheckbox` | `[class.mw-selected]="item.checked"` |
77
77
  | Gallery | Generates the dots, moves the track, swipe handling (single finger, dominant axis, passive listeners), writes `mw-gallery-desc` | Render dots in the template, bind the track transform and `mw-active` on the current dot |
78
78
  | Image slider | Toggles `mw-active` on the overlay image and the control button with the matching `data-index` | Bind `mw-active` from the selected index |
79
+ | Mosaic | Sets `mw-mosaic-wide` / `mw-mosaic-tall` on a tile from the photo's proportions - its `width`/`height` attributes, else the natural size - unless a shape class is already there | Bind the shape class from the image size you already know |
80
+ | Lightbox | Delegated: an image link inside `mw-mosaic` or `[data-mw-lightbox]` opens a `<dialog class="mw-lightbox">` built once - slides in reading order, images loaded one either side of the current, swipe, arrows and keys, focus back on the tile of the last photo | A component around a `<dialog>`: `showModal()`, a scroll-snap track, the index read from `scrollLeft` |
81
+ | Portrait gallery | Adds arrows and dots and keeps them in step with the scroll position; sets `mw-fit-contain` and a blurred `mw-fit-backdrop` on a slide whose photo is more than 1.4× off its shape, checked again by a `ResizeObserver` | Render arrows and dots, `scrollBy` one slide on click; bind `mw-fit-contain` from the image ratio |
82
+ | Story reel | Playback is CSS. Adds the `mw-story-reel-prev` / `-next` tap zones and arrow keys (seeking through `getAnimations()`), starts a video when its frame comes up and stops it with the story, closes the popover at the end, focuses the close button on open | Keep the CSS playback and the popover; add stepping only if you need it, the same way |
79
83
  | Kanban board | Counts the tickets per lane, moves a card between lanes (`data-kanban-move`) and marks the arrival with `mw-kanban-card-moved-forward` / `-back`, clones `mw-kanban-card-template` on save, derives the next key from `data-kanban-prefix`, toggles `mw-active` on the composer | Keep the tickets in a signal/store and render the lanes from it; `mw-active` on the composer, `mw-kanban-editing` on the ticket it replaces. Neither the `<template>` nor the `mw-kanban-composer-*` hook classes are needed |
80
84
  | 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 |
81
85
  | Viewport check | On a local hostname, warns on the console when the viewport meta has no `viewport-fit=cover` - without it every `env(safe-area-inset-*)` resolves to 0 | Nothing - check the meta tag once |
@@ -130,7 +134,8 @@ Pure CSS, nothing to wire up: hover, focus and press states, the card lift,
130
134
  tooltips (`data-tooltip`), `mw-rating` (via `data-rating`), the responsive table
131
135
  card view (`data-label`), all grids and utilities, the body scroll lock while a
132
136
  modal is open (from the `<dialog>` element, or `body:has(.mw-modal-open)` for an
133
- overlay div), the modal turning into a bottom sheet below 576px, toast entry animations, the sticky table header, the scroll
137
+ overlay div), the modal turning into a bottom sheet below 576px, toast entry animations, the photo feed and its post cards (`popover`), story
138
+ playback with its progress bar and pause, the sticky table header, the scroll
134
139
  hint on a tab bar (four gradients, no scroll listener), the kanban empty-lane
135
140
  placeholder (hidden via `:has()` as soon as the lane holds a ticket), touch
136
141
  target sizing on a coarse pointer, `prefers-reduced-motion` handling.
@@ -50,56 +50,56 @@ Safari 16.4+, Firefox 128+. The same range is declared as `browserslist` in
50
50
 
51
51
  ## Which token for what
52
52
 
53
- | Token | Role |
54
- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
55
- | `--mw-primary-color` | Brand colour as a **fill**: primary buttons, table headers, bars, badges - the label on top is `--mw-primary-accent-text-color` |
56
- | `--mw-primary-text-color` | Brand colour as **ink** on a theme surface: text, icons, focus rings, accent borders. Derived by clamping OKLch lightness (`max(l, .68)` on dark, `min(l, .55)` on light), so a colour already in range is used untouched |
57
- | `--mw-primary-color-hover` | Solid hover surface (derived: base + `$hover-shift` black, in both themes) |
58
- | `--mw-primary-background` | 20% tint - focus halo, alert/badge/tag surface, scrollbar tracks |
59
- | `--mw-primary-background-hover` | 45% tint - row and list hover |
60
- | `--mw-primary-info-background` | Alias of `--mw-primary-background`, so a component can interpolate one name across all six colours |
61
- | `--mw-border-accent` | Translucent accent **line**: panel rules, dividers, tab underlines. Theme-aware (70% of `--mw-*-text-color`) |
62
- | `--mw-secondary-*` | Same set for the second brand colour |
63
- | `--mw-success/warning/danger/info-color` | Status colours, each with `-text-color`, `-color-hover`, `-info-background`, `-info-background-hover` |
64
- | `--mw-accent-text-color` | Text on any solid coloured surface (buttons, table/panel headers, badges, stepper dots) |
65
- | `--mw-primary-accent-text-color` | Per colour override of that label. Same for `secondary`, `success`, `warning`, `danger`, `info`. Defaults to `--mw-accent-text-color`, so set one only when a colour needs the opposite label - a neon primary on a dark palette |
66
- | `--mw-gray-color` | Neutral foreground: muted icons, tooltips |
67
- | `--mw-gray-background` | Subtle neutral surface (20% alpha): zebra rows, disabled fields, tracks, skeletons |
68
- | `--mw-corner-accent` | The accent arc on the two round corners of every card, panel, modal and tile. Theme-aware: the full `--mw-*-text-color` tone in the dark theme, held to 80% in the light one, where the same blue carries far more contrast on a near-white card. Restyle it to recolour the signature, or `mw-corner-plain` on a single box to drop it |
69
- | `--mw-surface-muted` | Alias of `--mw-gray-background` under the name you reach for: a slightly set-off area _inside_ a card - hint block, framed paragraph, form summary |
70
- | `--mw-overlay-background` | Heavy scrim (60%) behind modals and blocking spinners |
71
- | `--mw-page-background`, `--mw-card-background`, `--mw-footer-background`, `--mw-border`, `--mw-shadow`, `--mw-text-color`, `--mw-text-muted-color`, `--mw-hero-image-filter` | The **active theme** - aliases pointing at the `--mw-dark-*` or `--mw-light-*` set |
72
- | `--mw-header-*` | Header chrome: `background`, `text-color`, `navbar-list-color`, `navbar-list-active-color`, `burgerbutton-color`, `burgerbutton-open-color`, `border` - dark in both themes. The two burger tokens default to the primary and secondary label ink, because the burger sits on those two surfaces |
73
- | `--mw-form-elements-background`, `--mw-form-elements-color` | Form controls stay light in both themes and therefore have their own pair |
74
- | `--mw-font-family-base`, `-heading`, `-mono` | Font stacks - system stacks by default (`-mono` leads with Fira Code); no font is bundled. Configurable in SCSS, see below |
75
- | `--mw-hero-background`, `--mw-hero-text-color` | Hero image (`url(...)`) and the ink on it. Fixed across themes - the photo does not change with the theme, so its text must not either. Defaults to the light end of the palette |
76
- | `--mw-transition` | Hover and focus states - an explicit paint-only property list at `--mw-duration-base`, never `all` |
77
- | `--mw-card-img-height` | Per-card image height (default `210px`; `mw-card-lg`/`-xl` set it to 340px/480px, 260px/340px below `sm`) |
78
- | `--mw-card-addon-color` | Background of `mw-card-badge` / `mw-card-ribbon`; the `mw-card-addon-*` classes set it, override it for a custom colour |
79
- | `--mw-card-addon-text-color` | Label on that badge/ribbon; the `mw-card-addon-*` classes point it at the matching `--mw-*-accent-text-color` |
80
- | `--mw-progress-ink` | Label inside `mw-progress-inline-label`; the `mw-progress-*` colour classes point it at the matching `--mw-*-accent-text-color` |
81
- | `--mw-table-scroll-height` | Per-table height cap for `mw-table-responsive-scroll` |
82
- | `--mw-kanban-background`, `--mw-kanban-lane-border`, `--mw-kanban-column-min-height` | Per-board surface, lane border and lane floor (120px, 90px on `mw-kanban-compact`) |
83
- | `--mw-container-gutter`, `--mw-container-width` | Page gutter of `mw-container` (fluid `clamp(1rem, 4.2vw + 0.5rem, 4rem)`, never below the safe-area inset) and the width derived from it (`min(1200px, 100% - 2 * gutter)`) |
84
- | `--mw-section-padding-block` | Top/bottom rhythm of `mw-section` (3.3rem, stepping down to 2.5rem below `md` and 1.75rem below `sm`) |
85
- | `--mw-section-padding-fluid` | The fluid alternative to the value above - 1.75rem on a phone up to 5.5rem at `xl`. Not applied by itself: set `--mw-section-padding-block: var(--mw-section-padding-fluid)` |
86
- | `--mw-section-padding-compact`, `--mw-section-padding-airy` | The two presets, as tokens so the phone steps reach them - point `--mw-section-padding-block` at one |
87
- | `--mw-section-head-step` | One step of the head rhythm, derived from `--mw-section-padding-block`. Every measure in a head and around a subtitle is a multiple of it, so a spacing preset moves them all |
88
- | `--mw-section-nav-height` | Height the sticky `mw-section-nav` reserves (3.75rem). A page carrying a strip adds it to `scroll-padding-top` itself, through the offset token below |
89
- | `--mw-announcement-offset`, `--mw-section-nav-offset` | What each layer of sticky chrome below the header adds to `scroll-padding-top`. `0px` until the component switches it on, so a page with both a ribbon and a strip reserves for both. For a sticky bar of your own, override `scroll-padding-top` on `html` - the calc names these two |
90
- | `--mw-root-font-size` | Percentage the whole rem scale is built on (`100%`). Type, spacing and control heights move with it; px breakpoints do not |
91
- | `--mw-calendar-dot` | Colour of a single calendar dot - set it per dot or per cell; the `mw-calendar-dot-*` classes are presets for it |
92
- | `--mw-scroll-hint-cover` | Colour the scroll hint on a tab bar fades into. Preset to the page, re-pointed to the card background inside `mw-card`, `mw-panel`, `mw-modal`, `mw-tile`, `mw-calendar` |
93
- | `--mw-elevation-1` … `-5` | Every shadow in the framework. Two layers per level - contact plus ambient. Never write a `box-shadow` by hand: a hand-rolled one is the wrong colour in one of the two themes |
94
- | `--mw-shadow-near`, `--mw-shadow-far` | The two tones the ramp above is mixed from, per theme |
95
- | `--mw-transition-fast` | The same property list at `--mw-duration-fast` - for a state change that should feel instant under the pointer |
96
- | `--mw-duration-instant\|fast\|base\|slow\|slower` | 110 / 180 / 300 / 520 / 900ms. Anything built on these is covered by `prefers-reduced-motion` for free |
97
- | `--mw-duration-zoom` | 650ms, for a large surface actually travelling - an image scaling inside a card, a slider track |
98
- | `--mw-ease-out`, `--mw-ease-in-out`, `--mw-ease-spring` | Things arriving (the default) / A to B and back / a pop |
99
- | `--mw-control-height-sm\|·\|lg`, `--mw-control-font-sm\|·\|lg`, `--mw-control-line-height` | One size scale for input, select, textarea and button, so a field and the button beside it line up by construction |
100
- | `--mw-focus-ring-width\|offset\|color` | The keyboard focus ring |
101
- | `--mw-focus-halo-size\|opacity` | The soft ring a form field gets instead of a hard outline |
102
- | `--mw-internal-theme-mode` | Read-only: what `$mw-theme-mode` was compiled to |
53
+ | Token | Role |
54
+ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
55
+ | `--mw-primary-color` | Brand colour as a **fill**: primary buttons, table headers, bars, badges - the label on top is `--mw-primary-accent-text-color` |
56
+ | `--mw-primary-text-color` | Brand colour as **ink** on a theme surface: text, icons, focus rings, accent borders. Derived by clamping OKLch lightness (`max(l, .68)` on dark, `min(l, .55)` on light), so a colour already in range is used untouched |
57
+ | `--mw-primary-color-hover` | Solid hover surface (derived: base + `$hover-shift` black, in both themes) |
58
+ | `--mw-primary-background` | 20% tint - focus halo, alert/badge/tag surface, scrollbar tracks |
59
+ | `--mw-primary-background-hover` | 45% tint - row and list hover |
60
+ | `--mw-primary-info-background` | Alias of `--mw-primary-background`, so a component can interpolate one name across all six colours |
61
+ | `--mw-border-accent` | Translucent accent **line**: panel rules, dividers, tab underlines. Theme-aware (70% of `--mw-*-text-color`) |
62
+ | `--mw-secondary-*` | Same set for the second brand colour |
63
+ | `--mw-success/warning/danger/info-color` | Status colours, each with `-text-color`, `-color-hover`, `-info-background`, `-info-background-hover` |
64
+ | `--mw-accent-text-color` | Text on any solid coloured surface (buttons, table/panel headers, badges, stepper dots) |
65
+ | `--mw-primary-accent-text-color` | Per colour override of that label. Same for `secondary`, `success`, `warning`, `danger`, `info`. Defaults to `--mw-accent-text-color`, so set one only when a colour needs the opposite label - a neon primary on a dark palette |
66
+ | `--mw-gray-color` | Neutral foreground: muted icons, tooltips |
67
+ | `--mw-gray-background` | Subtle neutral surface (20% alpha): zebra rows, disabled fields, tracks, skeletons |
68
+ | `--mw-corner-accent` | The accent arc on the two round corners of every card, panel, modal and tile, and on the top-right corner of an avatar. Theme-aware: the full `--mw-*-text-color` tone in the dark theme, held to 80% in the light one, where the same blue carries far more contrast on a near-white card. Restyle it to recolour the signature, or `mw-corner-plain` on a single box to drop it |
69
+ | `--mw-surface-muted` | Alias of `--mw-gray-background` under the name you reach for: a slightly set-off area _inside_ a card - hint block, framed paragraph, form summary |
70
+ | `--mw-overlay-background` | Heavy scrim (60%) behind modals and blocking spinners |
71
+ | `--mw-page-background`, `--mw-card-background`, `--mw-footer-background`, `--mw-border`, `--mw-shadow`, `--mw-text-color`, `--mw-text-muted-color`, `--mw-hero-image-filter` | The **active theme** - aliases pointing at the `--mw-dark-*` or `--mw-light-*` set |
72
+ | `--mw-header-*` | Header chrome: `background`, `text-color`, `navbar-list-color`, `navbar-list-active-color`, `burgerbutton-color`, `burgerbutton-open-color`, `border` - dark in both themes. The two burger tokens default to the primary and secondary label ink, because the burger sits on those two surfaces |
73
+ | `--mw-form-elements-background`, `--mw-form-elements-color` | Form controls stay light in both themes and therefore have their own pair |
74
+ | `--mw-font-family-base`, `-heading`, `-mono` | Font stacks - system stacks by default (`-mono` leads with Fira Code); no font is bundled. Configurable in SCSS, see below |
75
+ | `--mw-hero-background`, `--mw-hero-text-color` | Hero image (`url(...)`) and the ink on it. Fixed across themes - the photo does not change with the theme, so its text must not either. Defaults to the light end of the palette |
76
+ | `--mw-transition` | Hover and focus states - an explicit paint-only property list at `--mw-duration-base`, never `all` |
77
+ | `--mw-card-img-height` | Per-card image height (default `210px`; `mw-card-lg`/`-xl` set it to 340px/480px, 260px/340px below `sm`) |
78
+ | `--mw-card-addon-color` | Background of `mw-card-badge` / `mw-card-ribbon`; the `mw-card-addon-*` classes set it, override it for a custom colour |
79
+ | `--mw-card-addon-text-color` | Label on that badge/ribbon; the `mw-card-addon-*` classes point it at the matching `--mw-*-accent-text-color` |
80
+ | `--mw-progress-ink` | Label inside `mw-progress-inline-label`; the `mw-progress-*` colour classes point it at the matching `--mw-*-accent-text-color` |
81
+ | `--mw-table-scroll-height` | Per-table height cap for `mw-table-responsive-scroll` |
82
+ | `--mw-kanban-background`, `--mw-kanban-lane-border`, `--mw-kanban-column-min-height` | Per-board surface, lane border and lane floor (120px, 90px on `mw-kanban-compact`) |
83
+ | `--mw-container-gutter`, `--mw-container-width` | Page gutter of `mw-container` (fluid `clamp(1rem, 4.2vw + 0.5rem, 4rem)`, never below the safe-area inset) and the width derived from it (`min(1200px, 100% - 2 * gutter)`) |
84
+ | `--mw-section-padding-block` | Top/bottom rhythm of `mw-section` (3.3rem, stepping down to 2.5rem below `md` and 1.75rem below `sm`) |
85
+ | `--mw-section-padding-fluid` | The fluid alternative to the value above - 1.75rem on a phone up to 5.5rem at `xl`. Not applied by itself: set `--mw-section-padding-block: var(--mw-section-padding-fluid)` |
86
+ | `--mw-section-padding-compact`, `--mw-section-padding-airy` | The two presets, as tokens so the phone steps reach them - point `--mw-section-padding-block` at one |
87
+ | `--mw-section-head-step` | One step of the head rhythm, derived from `--mw-section-padding-block`. Every measure in a head and around a subtitle is a multiple of it, so a spacing preset moves them all |
88
+ | `--mw-section-nav-height` | Height the sticky `mw-section-nav` reserves (3.75rem). A page carrying a strip adds it to `scroll-padding-top` itself, through the offset token below |
89
+ | `--mw-announcement-offset`, `--mw-section-nav-offset` | What each layer of sticky chrome below the header adds to `scroll-padding-top`. `0px` until the component switches it on, so a page with both a ribbon and a strip reserves for both. For a sticky bar of your own, override `scroll-padding-top` on `html` - the calc names these two |
90
+ | `--mw-root-font-size` | Percentage the whole rem scale is built on (`100%`). Type, spacing and control heights move with it; px breakpoints do not |
91
+ | `--mw-calendar-dot` | Colour of a single calendar dot - set it per dot or per cell; the `mw-calendar-dot-*` classes are presets for it |
92
+ | `--mw-scroll-hint-cover` | Colour the scroll hint on a tab bar fades into. Preset to the page, re-pointed to the card background inside `mw-card`, `mw-panel`, `mw-modal`, `mw-tile`, `mw-calendar` |
93
+ | `--mw-elevation-1` … `-5` | Every shadow in the framework. Two layers per level - contact plus ambient. Never write a `box-shadow` by hand: a hand-rolled one is the wrong colour in one of the two themes |
94
+ | `--mw-shadow-near`, `--mw-shadow-far` | The two tones the ramp above is mixed from, per theme |
95
+ | `--mw-transition-fast` | The same property list at `--mw-duration-fast` - for a state change that should feel instant under the pointer |
96
+ | `--mw-duration-instant\|fast\|base\|slow\|slower` | 110 / 180 / 300 / 520 / 900ms. Anything built on these is covered by `prefers-reduced-motion` for free |
97
+ | `--mw-duration-zoom` | 650ms, for a large surface actually travelling - an image scaling inside a card, a slider track |
98
+ | `--mw-ease-out`, `--mw-ease-in-out`, `--mw-ease-spring` | Things arriving (the default) / A to B and back / a pop |
99
+ | `--mw-control-height-sm\|·\|lg`, `--mw-control-font-sm\|·\|lg`, `--mw-control-line-height` | One size scale for input, select, textarea and button, so a field and the button beside it line up by construction |
100
+ | `--mw-focus-ring-width\|offset\|color` | The keyboard focus ring |
101
+ | `--mw-focus-halo-size\|opacity` | The soft ring a form field gets instead of a hard outline |
102
+ | `--mw-internal-theme-mode` | Read-only: what `$mw-theme-mode` was compiled to |
103
103
 
104
104
  `--mw-container-gutter`, `--mw-container-width` and `--mw-section-padding-block`
105
105
  are the knobs a good default cannot settle, because the right answer differs per
@@ -523,4 +523,6 @@ Details worth knowing:
523
523
  (`utilities`, `form-elements`), wasteful for `components`.
524
524
  - Some components expect a sibling: `lists` styles checkbox rows and looks best
525
525
  with `form-elements/checkbox`; `tags` uses `mw-btn-mini` from `buttons` for
526
- its remove button.
526
+ its remove button. `mosaic` needs `lightbox` for its viewer, and
527
+ `portrait-gallery` needs `utilities/fit` for a photo it shows whole instead of
528
+ cropping. `overlay-btn` comes along with every component that uses it.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,16 @@ 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.29.0] - 2026-09-23
10
+
11
+ ### Added
12
+
13
+ - new gallery styles
14
+
15
+ ### Changed
16
+
17
+ - removed some duplicated html showcase with toggle buttons for variants
18
+
9
19
  ## [5.28.0] - 2026-09-21
10
20
 
11
21
  ### Changed
package/CLAUDE.md CHANGED
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
 
5
5
  ## Project Overview
6
6
 
7
- MaverickWave is a lightweight, modern CSS framework providing a grid system, 48 UI components, utility classes, and light/dark mode support. The repo also contains the showcase/documentation website bundled into the distribution.
7
+ MaverickWave is a lightweight, modern CSS framework providing a grid system, 59 UI components, utility classes, and light/dark mode support. The repo also contains the showcase/documentation website bundled into the distribution.
8
8
 
9
9
  ## Commands
10
10
 
@@ -30,7 +30,7 @@ There are no test or lint scripts.
30
30
 
31
31
  - `abstracts/` - CSS custom properties, mixins, SCSS functions (color variant generation)
32
32
  - `base/` - Reset, element defaults, typography
33
- - `components/` - Self-contained component styles (48 files, one per component)
33
+ - `components/` - Self-contained component styles (59 files, one per component)
34
34
  - `form-elements/` - Form input/select/checkbox-specific styles
35
35
  - `layout/` - Grid, container, header/footer, section
36
36
  - `utilities/` - Spacing, flex, display utility classes
@@ -40,9 +40,9 @@ There are no test or lint scripts.
40
40
  to it. `media-down($bp)` only for rules that set what the base never sets, or
41
41
  where the narrow layout costs more properties than the wide one.
42
42
 
43
- **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.
43
+ **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, portrait gallery, mosaic, lightbox, story reels.
44
44
 
45
- **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`.
45
+ **HTML showcase** (`index.html` + `src/partials/`) - The top-level `index.html` uses `@@include()` syntax to pull in 72 partials from `src/partials/`. These compile into `dist/index.html`.
46
46
 
47
47
  ## Naming Conventions
48
48
 
package/README.md CHANGED
@@ -45,12 +45,12 @@ The result is a framework that balances utility with simplicity, offering develo
45
45
  <title>My MaverickWave Project</title>
46
46
  <link
47
47
  rel="stylesheet"
48
- href="https://cdn.jsdelivr.net/npm/maverick-wave@5.28.0/maverick-wave.min.css"
48
+ href="https://cdn.jsdelivr.net/npm/maverick-wave@5.29.0/maverick-wave.min.css"
49
49
  />
50
50
  </head>
51
51
  <body>
52
52
  <!-- Your content here -->
53
- <script src="https://cdn.jsdelivr.net/npm/maverick-wave@5.28.0/maverick-wave.min.js"></script>
53
+ <script src="https://cdn.jsdelivr.net/npm/maverick-wave@5.29.0/maverick-wave.min.js"></script>
54
54
  </body>
55
55
  </html>
56
56
  ```