maverick-wave 4.13.0 → 4.15.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/maverick-wave/SKILL.md +13 -1
- package/.claude/skills/maverick-wave/references/components.md +158 -0
- package/.claude/skills/maverick-wave/references/layout.md +10 -0
- package/.claude/skills/maverick-wave/references/theming.md +44 -43
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/index.html +68 -0
- package/maverick-wave.min.css +3 -3
- package/package.json +2 -2
- package/src/partials/grid-container.html +68 -48
- package/src/partials/pricing-container.html +521 -0
- package/src/partials/utilities-container.html +45 -26
- package/src/scss/abstracts/_mixins.scss +70 -0
- package/src/scss/abstracts/_variables.scss +7 -0
- package/src/scss/base/_reset.scss +9 -0
- package/src/scss/components/_accordions.scss +1 -1
- package/src/scss/components/_calendar.scss +1 -1
- package/src/scss/components/_cards.scss +1 -2
- package/src/scss/components/_index.scss +1 -0
- package/src/scss/components/_kanban.scss +1 -1
- package/src/scss/components/_modals.scss +1 -1
- package/src/scss/components/_pagination.scss +1 -1
- package/src/scss/components/_panels.scss +10 -1
- package/src/scss/components/_pricing.scss +222 -0
- package/src/scss/components/_segmented.scss +12 -0
- package/src/scss/components/_tiles.scss +1 -2
- package/src/scss/form-elements/_login.scss +1 -2
- package/src/scss/utilities/_corner.scss +8 -0
- package/src/scss/utilities/_index.scss +1 -0
|
@@ -155,6 +155,14 @@ keys at all (negative gap is invalid CSS and is not generated).
|
|
|
155
155
|
|
|
156
156
|
**Radius** (`mw-radius-none|xs|sm|md|lg|xl|2xl|full`): 0, 2, 5, 10, 15, 20, 30 px, 50%.
|
|
157
157
|
|
|
158
|
+
**The surface signature.** Every panel-like component - card, panel, modal,
|
|
159
|
+
accordion, tile, calendar, pagination, login box - shares one silhouette: sharp
|
|
160
|
+
5px corners on the top-left/bottom-right diagonal, round 20px ones on the other,
|
|
161
|
+
plus a 2px **corner accent** in the primary tone (`--mw-corner-accent`) sitting
|
|
162
|
+
on the two round corners. It is not a class and not opt-in; the components carry
|
|
163
|
+
it. `mw-corner-plain` drops the accent on a box too small to hold it - see
|
|
164
|
+
`references/layout.md`.
|
|
165
|
+
|
|
158
166
|
**Elevation** (`mw-elevation-0` … `-5`, and `var(--mw-elevation-N)` inside SCSS).
|
|
159
167
|
Two shadows per level - a tight contact layer plus a wide ambient one:
|
|
160
168
|
`1` resting (inputs, tags), `2` raised (cards, panels at rest), `3` floating
|
|
@@ -194,7 +202,10 @@ Everything below is documented in `references/components.md` unless marked other
|
|
|
194
202
|
**Containers** `mw-card` (+ `simple`, `lg`, `xl`, `stack`, badge, ribbon,
|
|
195
203
|
feature frame) ·
|
|
196
204
|
`mw-panel` · `mw-tile` · `mw-accordion` · `mw-tabs` · `mw-modal` ·
|
|
197
|
-
`mw-item-list` family
|
|
205
|
+
`mw-item-list` family · `mw-offer` (+ `-head`, `-name`)
|
|
206
|
+
|
|
207
|
+
**Pricing** `mw-price` (+ `-amount`, `-fraction`, `-currency`, `-original`,
|
|
208
|
+
`-period`, `-word`, `-note`, `sm`/`lg`, `stacked`, `center`, `plain`)
|
|
198
209
|
|
|
199
210
|
**Data & status** `mw-table` (+ `subtle`, `sticky-head`, `cards`, `compact`,
|
|
200
211
|
`hover`, responsive wrappers) · `mw-kanban` (+ `plain`, `compact`) ·
|
|
@@ -230,6 +241,7 @@ feature frame) ·
|
|
|
230
241
|
`mw-text-currency` · `mw-text-truncate` / `mw-text-clamp-2..5` /
|
|
231
242
|
`mw-text-break` / `mw-text-nowrap` · `mw-text-balance` / `mw-text-pretty` /
|
|
232
243
|
`mw-text-eyebrow` / `mw-text-measure` · `mw-elevation-0..5` ·
|
|
244
|
+
`mw-corner-plain` ·
|
|
233
245
|
`mw-aspect-square|video|wide|portrait|photo` · `mw-d-{sm,md,lg,xl}-*` /
|
|
234
246
|
`mw-hide-mobile` / `mw-hide-desktop` · `mw-overflow-*` / `mw-snap-x` ·
|
|
235
247
|
spacing, flex, display, text
|
|
@@ -299,6 +299,164 @@ tech-stack grids:
|
|
|
299
299
|
</div>
|
|
300
300
|
```
|
|
301
301
|
|
|
302
|
+
## Pricing
|
|
303
|
+
|
|
304
|
+
Two pieces that are almost always needed together: `mw-price`, the figure on its
|
|
305
|
+
own, and `mw-offer`, the card it is sold from.
|
|
306
|
+
|
|
307
|
+
### Price
|
|
308
|
+
|
|
309
|
+
One baseline row - what it cost before, what it costs now, the unit, a discount
|
|
310
|
+
tag:
|
|
311
|
+
|
|
312
|
+
```html
|
|
313
|
+
<p class="mw-price">
|
|
314
|
+
<span class="mw-price-original">
|
|
315
|
+
<span class="mw-sr-only">Regular price:</span>159 Euro
|
|
316
|
+
</span>
|
|
317
|
+
<span class="mw-sr-only">Now:</span>
|
|
318
|
+
<span class="mw-price-amount"
|
|
319
|
+
>129<span class="mw-price-fraction">,90</span></span
|
|
320
|
+
>
|
|
321
|
+
<span class="mw-price-currency">Euro</span>
|
|
322
|
+
<span class="mw-price-period">/ month</span>
|
|
323
|
+
<span class="mw-tag mw-tag-secondary">-20%</span>
|
|
324
|
+
</p>
|
|
325
|
+
<p class="mw-price-note">Billed yearly, cancel any time.</p>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
- Everything in the row is sized in `em` off `--mw-price-size`, so
|
|
329
|
+
`mw-price-sm` (1.5rem) and `mw-price-lg` (3rem) move the whole row at once -
|
|
330
|
+
cents, unit and struck original included. Set the token yourself for any other
|
|
331
|
+
size.
|
|
332
|
+
- `mw-price-original` is muted **and** struck. Muted alone reads as a footnote;
|
|
333
|
+
the line is what says this is no longer the price. The rule is drawn
|
|
334
|
+
explicitly, because the browser default hairline disappears at this size
|
|
335
|
+
against a muted tone.
|
|
336
|
+
- `mw-price-fraction` goes **inside** the amount and sits one step down on the
|
|
337
|
+
baseline, not raised to the cap height - raised cents are a discounter's idiom
|
|
338
|
+
and make a service price read cheaper than it is.
|
|
339
|
+
- `mw-price-currency` and `mw-price-period` stop shrinking at 0.8rem, and
|
|
340
|
+
`mw-price-original` at 0.9rem. Below `mw-price-sm` the `em` chain would put
|
|
341
|
+
them under 12px, and that is the size most offer cards run at.
|
|
342
|
+
- `mw-price-word` for "On request" or "Free" - bold, so it holds the same slot in
|
|
343
|
+
a row of cards, but set well below a figure. Its line box is scaled back up to
|
|
344
|
+
the amount's, so a word and a number in neighbouring cards land on one line.
|
|
345
|
+
- `mw-price-note` is a **sibling** of the row, not a child - inside it, it would
|
|
346
|
+
inherit the price size and join the baseline.
|
|
347
|
+
- A `mw-tag` or `mw-badge` placed directly in the row is centred against the
|
|
348
|
+
digits rather than hung off their baseline.
|
|
349
|
+
|
|
350
|
+
| Class | What it does |
|
|
351
|
+
| ------------------ | --------------------------------------------- |
|
|
352
|
+
| `mw-price-sm/lg` | Retunes `--mw-price-size`, everything follows |
|
|
353
|
+
| `mw-price-stacked` | Old price on its own line above the new one |
|
|
354
|
+
| `mw-price-center` | Centres the row |
|
|
355
|
+
| `mw-price-plain` | Body colour instead of the brand |
|
|
356
|
+
|
|
357
|
+
A struck-through price is a visual convention a screen reader does not pass on -
|
|
358
|
+
`<s>` is announced by almost none of them. Wherever an old and a new price stand
|
|
359
|
+
next to each other, name them with `mw-sr-only`, as above. Without it the reader
|
|
360
|
+
hears two prices and no way to tell which one is charged.
|
|
361
|
+
|
|
362
|
+
### Offer card
|
|
363
|
+
|
|
364
|
+
`mw-offer` goes on a `mw-card` and does the one thing the card cannot: it pushes
|
|
365
|
+
the price to the bottom of the body, so the prices in a row line up even though
|
|
366
|
+
the feature lists have different lengths.
|
|
367
|
+
|
|
368
|
+
```html
|
|
369
|
+
<article class="mw-card mw-offer">
|
|
370
|
+
<div class="mw-card-body">
|
|
371
|
+
<h3 class="mw-card-title">Studio</h3>
|
|
372
|
+
<hr class="mw-card-title-divider" />
|
|
373
|
+
<p class="mw-card-text">Description.</p>
|
|
374
|
+
<ul class="mw-list mw-list-check">
|
|
375
|
+
<li>10 projects</li>
|
|
376
|
+
</ul>
|
|
377
|
+
<p class="mw-price mw-price-sm">…</p>
|
|
378
|
+
<p class="mw-price-note">Per month, billed yearly.</p>
|
|
379
|
+
<div class="mw-card-footer">
|
|
380
|
+
<button class="mw-btn mw-btn-primary">Choose</button>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
</article>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
- Bottom-aligned, note included: a card that carries a `mw-price-note` and one
|
|
387
|
+
that does not will **not** have their prices on the same line. Give every card
|
|
388
|
+
in a row a note, or none of them.
|
|
389
|
+
- To lift one plan out of the row, wrap it in `mw-card-feature` - the frame is
|
|
390
|
+
the card's, not the offer's.
|
|
391
|
+
- A price dropped into a `mw-card-footer` instead needs no modifier: it lines up
|
|
392
|
+
with the button beside it on its own, and `mw-price-sm` keeps it from
|
|
393
|
+
outweighing the action. That is the layout for a dated event, where the price
|
|
394
|
+
is a detail rather than the offer.
|
|
395
|
+
|
|
396
|
+
**Head band** - name and price in a solid bar at the top, for when the price is
|
|
397
|
+
the thing being compared and the feature list is only its justification:
|
|
398
|
+
|
|
399
|
+
```html
|
|
400
|
+
<article class="mw-card mw-offer mw-card-addon-secondary">
|
|
401
|
+
<div class="mw-offer-head">
|
|
402
|
+
<p class="mw-offer-name">Studio</p>
|
|
403
|
+
<p class="mw-price mw-price-sm">…</p>
|
|
404
|
+
</div>
|
|
405
|
+
<div class="mw-card-body">…</div>
|
|
406
|
+
</article>
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
The band reads `mw-card-addon-*` for its colour - the same set the card badge and
|
|
410
|
+
the ribbon use, primary without one. The price inherits the band's ink, and the
|
|
411
|
+
card drops its top corner arc, which the band already owns.
|
|
412
|
+
|
|
413
|
+
### Billing cycle
|
|
414
|
+
|
|
415
|
+
A subscription sold on several terms, where the longer one is cheaper. The
|
|
416
|
+
switch is a `mw-segmented` **above** the grid, not one per card - three cards
|
|
417
|
+
with three switches are three states the visitor has to keep in their head.
|
|
418
|
+
|
|
419
|
+
```html
|
|
420
|
+
<div
|
|
421
|
+
class="mw-segmented mw-segmented-auto"
|
|
422
|
+
role="group"
|
|
423
|
+
aria-label="Billing cycle"
|
|
424
|
+
>
|
|
425
|
+
<button type="button" class="mw-segmented-item mw-active" aria-pressed="true">
|
|
426
|
+
Monthly
|
|
427
|
+
</button>
|
|
428
|
+
<button type="button" class="mw-segmented-item" aria-pressed="false">
|
|
429
|
+
6 months <span class="mw-tag mw-tag-secondary">-10%</span>
|
|
430
|
+
</button>
|
|
431
|
+
<button type="button" class="mw-segmented-item" aria-pressed="false">
|
|
432
|
+
Yearly <span class="mw-tag mw-tag-secondary">-20%</span>
|
|
433
|
+
</button>
|
|
434
|
+
</div>
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
- The headline figure stays **per month** on every term. "288 Euro" next to
|
|
438
|
+
"30 Euro" is not a comparison a reader can make in their head; the amount that
|
|
439
|
+
actually leaves the account belongs in the `mw-price-note` below
|
|
440
|
+
("288 Euro charged once for the year, cancel monthly").
|
|
441
|
+
- The full monthly rate goes in `mw-price-original`, the saving in a `mw-tag`
|
|
442
|
+
next to it. Both carry `hidden` on the monthly term - the framework resets
|
|
443
|
+
`[hidden]` to `display: none !important`, so it beats a component's own
|
|
444
|
+
`display` and an `el.hidden = true` or an Angular `[hidden]` binding works on
|
|
445
|
+
a `mw-tag` too.
|
|
446
|
+
- Wrap the price in `aria-live="polite"` and put `aria-pressed` on the switch
|
|
447
|
+
items. The figure changes without the page moving, which a screen reader
|
|
448
|
+
otherwise never hears.
|
|
449
|
+
- Drop trailing zero cents: "24 Euro", not "24,00 Euro". `mw-price-amount` sets
|
|
450
|
+
tabular figures, so the row does not jump when the term changes.
|
|
451
|
+
- A tag inside the **active** item drops its colour and outlines itself in the
|
|
452
|
+
item's ink - the colour variants mix their background from the page, and an
|
|
453
|
+
orange pill on the blue fill comes out purple. Inactive items keep the colour,
|
|
454
|
+
which is what makes a visitor click them.
|
|
455
|
+
|
|
456
|
+
The switching itself is the application's job - the framework ships the look,
|
|
457
|
+
not the arithmetic. The showcase wires it in a few lines at the bottom of
|
|
458
|
+
`index.html`.
|
|
459
|
+
|
|
302
460
|
## Panels
|
|
303
461
|
|
|
304
462
|
A bordered box with a header rule - the "titled section" of an application.
|
|
@@ -340,6 +340,16 @@ with it; `mw-text-capitalize` is the plain transform.
|
|
|
340
340
|
**Elevation** - `mw-elevation-0` through `-5`, the same ramp every component
|
|
341
341
|
uses. See the scale table in `SKILL.md`. Never hand-roll a `box-shadow`.
|
|
342
342
|
|
|
343
|
+
**Corner accent** - `mw-corner-plain` drops the accent arc that sits on the
|
|
344
|
+
two round corners of every card, panel, modal, tile, accordion, calendar,
|
|
345
|
+
pagination and login box. Everything else stays: the radii, the border, the
|
|
346
|
+
shadow, the hover. It is for a box too small to carry the mark - the arc reaches
|
|
347
|
+
about 34px in from its corner, so under roughly 90px the two of them take up
|
|
348
|
+
most of the outline and the pair reads as a frame instead of as a detail. A
|
|
349
|
+
chip-sized `mw-card-simple`, a swatch, a marker in a layout demo. A card with
|
|
350
|
+
content in it never needs this, and it is not the way to switch the signature
|
|
351
|
+
off across a project - restyle `--mw-corner-accent` for that.
|
|
352
|
+
|
|
343
353
|
**Aspect ratio** - `mw-aspect-square|video|wide|portrait|photo`. Reserves the
|
|
344
354
|
box before the image inside it has loaded, so the page does not reflow when the
|
|
345
355
|
picture arrives. The child fills the box and crops rather than stretching.
|
|
@@ -46,49 +46,50 @@ Safari 16.4+, Firefox 128+. The same range is declared as `browserslist` in
|
|
|
46
46
|
|
|
47
47
|
## Which token for what
|
|
48
48
|
|
|
49
|
-
| Token | Role
|
|
50
|
-
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
-
| `--mw-primary-color` | Brand colour as a **fill**: primary buttons, table headers, bars, badges - the label on top is `--mw-primary-accent-text-color`
|
|
52
|
-
| `--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
|
|
53
|
-
| `--mw-primary-color-hover` | Solid hover surface (derived: base + `$hover-shift` black, in both themes)
|
|
54
|
-
| `--mw-primary-background` | 20% tint - focus halo, alert/badge/tag surface, scrollbar tracks
|
|
55
|
-
| `--mw-primary-background-hover` | 45% tint - row and list hover
|
|
56
|
-
| `--mw-primary-info-background` | Alias of `--mw-primary-background`, so a component can interpolate one name across all six colours
|
|
57
|
-
| `--mw-border-accent` | Translucent accent **line**: panel rules, dividers, tab underlines. Theme-aware (70% of `--mw-*-text-color`)
|
|
58
|
-
| `--mw-secondary-*` | Same set for the second brand colour
|
|
59
|
-
| `--mw-success/warning/danger/info-color` | Status colours, each with `-text-color`, `-color-hover`, `-info-background`, `-info-background-hover`
|
|
60
|
-
| `--mw-accent-text-color` | Text on any solid coloured surface (buttons, table/panel headers, badges, stepper dots)
|
|
61
|
-
| `--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
|
|
62
|
-
| `--mw-gray-color` | Neutral foreground: muted icons, tooltips
|
|
63
|
-
| `--mw-gray-background` | Subtle neutral surface (20% alpha): zebra rows, disabled fields, tracks, skeletons
|
|
64
|
-
| `--mw-
|
|
65
|
-
| `--mw-
|
|
66
|
-
| `--mw-
|
|
67
|
-
| `--mw-
|
|
68
|
-
| `--mw-
|
|
69
|
-
| `--mw-
|
|
70
|
-
| `--mw-
|
|
71
|
-
| `--mw-
|
|
72
|
-
| `--mw-
|
|
73
|
-
| `--mw-card-
|
|
74
|
-
| `--mw-card-addon-
|
|
75
|
-
| `--mw-
|
|
76
|
-
| `--mw-
|
|
77
|
-
| `--mw-
|
|
78
|
-
| `--mw-
|
|
79
|
-
| `--mw-
|
|
80
|
-
| `--mw-
|
|
81
|
-
| `--mw-
|
|
82
|
-
| `--mw-
|
|
83
|
-
| `--mw-shadow-
|
|
84
|
-
| `--mw-
|
|
85
|
-
| `--mw-
|
|
86
|
-
| `--mw-duration-
|
|
87
|
-
| `--mw-
|
|
88
|
-
| `--mw-
|
|
89
|
-
| `--mw-
|
|
90
|
-
| `--mw-focus-
|
|
91
|
-
| `--mw-
|
|
49
|
+
| Token | Role |
|
|
50
|
+
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| `--mw-primary-color` | Brand colour as a **fill**: primary buttons, table headers, bars, badges - the label on top is `--mw-primary-accent-text-color` |
|
|
52
|
+
| `--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 |
|
|
53
|
+
| `--mw-primary-color-hover` | Solid hover surface (derived: base + `$hover-shift` black, in both themes) |
|
|
54
|
+
| `--mw-primary-background` | 20% tint - focus halo, alert/badge/tag surface, scrollbar tracks |
|
|
55
|
+
| `--mw-primary-background-hover` | 45% tint - row and list hover |
|
|
56
|
+
| `--mw-primary-info-background` | Alias of `--mw-primary-background`, so a component can interpolate one name across all six colours |
|
|
57
|
+
| `--mw-border-accent` | Translucent accent **line**: panel rules, dividers, tab underlines. Theme-aware (70% of `--mw-*-text-color`) |
|
|
58
|
+
| `--mw-secondary-*` | Same set for the second brand colour |
|
|
59
|
+
| `--mw-success/warning/danger/info-color` | Status colours, each with `-text-color`, `-color-hover`, `-info-background`, `-info-background-hover` |
|
|
60
|
+
| `--mw-accent-text-color` | Text on any solid coloured surface (buttons, table/panel headers, badges, stepper dots) |
|
|
61
|
+
| `--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 |
|
|
62
|
+
| `--mw-gray-color` | Neutral foreground: muted icons, tooltips |
|
|
63
|
+
| `--mw-gray-background` | Subtle neutral surface (20% alpha): zebra rows, disabled fields, tracks, skeletons |
|
|
64
|
+
| `--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 |
|
|
65
|
+
| `--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 |
|
|
66
|
+
| `--mw-overlay-background` | Heavy scrim (60%) behind modals and blocking spinners |
|
|
67
|
+
| `--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 |
|
|
68
|
+
| `--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 |
|
|
69
|
+
| `--mw-form-elements-background`, `--mw-form-elements-color` | Form controls stay light in both themes and therefore have their own pair |
|
|
70
|
+
| `--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 |
|
|
71
|
+
| `--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 |
|
|
72
|
+
| `--mw-transition` | Hover and focus states - an explicit paint-only property list at `--mw-duration-base`, never `all` |
|
|
73
|
+
| `--mw-card-img-height` | Per-card image height (default `210px`; `mw-card-lg`/`-xl` set it to 340px/480px, 260px/340px below `sm`) |
|
|
74
|
+
| `--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 |
|
|
75
|
+
| `--mw-card-addon-text-color` | Label on that badge/ribbon; the `mw-card-addon-*` classes point it at the matching `--mw-*-accent-text-color` |
|
|
76
|
+
| `--mw-progress-ink` | Label inside `mw-progress-inline-label`; the `mw-progress-*` colour classes point it at the matching `--mw-*-accent-text-color` |
|
|
77
|
+
| `--mw-table-scroll-height` | Per-table height cap for `mw-table-responsive-scroll` |
|
|
78
|
+
| `--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`) |
|
|
79
|
+
| `--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)`) |
|
|
80
|
+
| `--mw-section-padding-block` | Top/bottom rhythm of `mw-section` (1.75rem) |
|
|
81
|
+
| `--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 |
|
|
82
|
+
| `--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` |
|
|
83
|
+
| `--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 |
|
|
84
|
+
| `--mw-shadow-near`, `--mw-shadow-far` | The two tones the ramp above is mixed from, per theme |
|
|
85
|
+
| `--mw-transition-fast` | The same property list at `--mw-duration-fast` - for a state change that should feel instant under the pointer |
|
|
86
|
+
| `--mw-duration-instant\|fast\|base\|slow\|slower` | 110 / 180 / 300 / 520 / 900ms. Anything built on these is covered by `prefers-reduced-motion` for free |
|
|
87
|
+
| `--mw-duration-zoom` | 650ms, for a large surface actually travelling - an image scaling inside a card, a slider track |
|
|
88
|
+
| `--mw-ease-out`, `--mw-ease-in-out`, `--mw-ease-spring` | Things arriving (the default) / A to B and back / a pop |
|
|
89
|
+
| `--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 |
|
|
90
|
+
| `--mw-focus-ring-width\|offset\|color` | The keyboard focus ring |
|
|
91
|
+
| `--mw-focus-halo-size\|opacity` | The soft ring a form field gets instead of a hard outline |
|
|
92
|
+
| `--mw-internal-theme-mode` | Read-only: what `$mw-theme-mode` was compiled to |
|
|
92
93
|
|
|
93
94
|
`--mw-container-gutter`, `--mw-container-width` and `--mw-section-padding-block`
|
|
94
95
|
are the knobs a good default cannot settle, because the right answer differs per
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,18 @@ 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
|
+
## [4.15.0] - 2026-08-29
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `mw-price` - the price figure: struck-through original, discounted amount, cents, unit, period and note, ...
|
|
14
|
+
|
|
15
|
+
## [4.14.0] - 2026-08-27
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- unique maverick-wave corner colors in cards
|
|
20
|
+
|
|
9
21
|
## [4.13.0] - 2026-08-27
|
|
10
22
|
|
|
11
23
|
### Added
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ The result is a framework that balances utility with simplicity, offering develo
|
|
|
17
17
|
## Features
|
|
18
18
|
|
|
19
19
|
- Responsive Grid System
|
|
20
|
-
- 30+ UI Components: Buttons, Cards, Panels, Tabs, Accordions, Modals, Tiles, Alerts, Spinners, Progress Bars, Avatars, Tags, Badges, Dropdown, Ratings, Stepper, Skeleton Loader, Empty State, Divider, and more
|
|
20
|
+
- 30+ UI Components: Buttons, Cards, Panels, Tabs, Accordions, Modals, Tiles, Alerts, Spinners, Progress Bars, Avatars, Tags, Badges, Dropdown, Ratings, Stepper, Skeleton Loader, Empty State, Price, Offer Cards, Divider, and more
|
|
21
21
|
- Form Elements: Input, Select, Textarea, Checkbox, Radio, Toggle, Input Group, with `mw-field` wrapper pattern for Angular Reactive Forms
|
|
22
22
|
- Utility Classes for spacing, flex, display, typography, text overflow, elevation and aspect ratio
|
|
23
23
|
- A five-step elevation ramp and a motion scale, so every shadow and every transition in the framework comes from one place
|
package/index.html
CHANGED
|
@@ -280,6 +280,7 @@
|
|
|
280
280
|
<a href="#code" class="mw-section-btn">Code</a>
|
|
281
281
|
<a href="#skeleton" class="mw-section-btn">Skeleton</a>
|
|
282
282
|
<a href="#empty-state" class="mw-section-btn">Empty State</a>
|
|
283
|
+
<a href="#pricing" class="mw-section-btn">Pricing</a>
|
|
283
284
|
</nav>
|
|
284
285
|
|
|
285
286
|
<!-- Buttons container -->
|
|
@@ -348,6 +349,11 @@
|
|
|
348
349
|
<div id="empty-state">
|
|
349
350
|
@@include('./src/partials/empty-state-container.html')
|
|
350
351
|
</div>
|
|
352
|
+
|
|
353
|
+
<!-- Pricing container -->
|
|
354
|
+
<div id="pricing">
|
|
355
|
+
@@include('./src/partials/pricing-container.html')
|
|
356
|
+
</div>
|
|
351
357
|
</div>
|
|
352
358
|
</section>
|
|
353
359
|
|
|
@@ -497,6 +503,68 @@
|
|
|
497
503
|
}
|
|
498
504
|
});
|
|
499
505
|
|
|
506
|
+
// just for the showcase: one segmented switch above the offer grid drives
|
|
507
|
+
// every card in it. The framework ships the look, not the arithmetic - a
|
|
508
|
+
// real application has its own prices and its own state, so this stays
|
|
509
|
+
// here rather than in maverick-wave.js.
|
|
510
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
511
|
+
const cycleSwitch = document.getElementById('cycle-switch');
|
|
512
|
+
const offers = document.getElementById('cycle-offers');
|
|
513
|
+
if (!cycleSwitch || !offers) return;
|
|
514
|
+
|
|
515
|
+
// Trailing zero cents are noise on a price - "24 Euro", not "24,00
|
|
516
|
+
// Euro". The tabular figures keep the row from jumping anyway.
|
|
517
|
+
const money = (n) => {
|
|
518
|
+
const fixed = n.toFixed(2);
|
|
519
|
+
return fixed.endsWith('.00')
|
|
520
|
+
? fixed.slice(0, -3)
|
|
521
|
+
: fixed.replace('.', ',');
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
const apply = (button) => {
|
|
525
|
+
const months = Number(button.dataset.cycle);
|
|
526
|
+
const off = Number(button.dataset.cycleOff || 0);
|
|
527
|
+
|
|
528
|
+
cycleSwitch.querySelectorAll('.mw-segmented-item').forEach((item) => {
|
|
529
|
+
const on = item === button;
|
|
530
|
+
item.classList.toggle('mw-active', on);
|
|
531
|
+
item.setAttribute('aria-pressed', String(on));
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
offers.querySelectorAll('.mw-offer').forEach((card) => {
|
|
535
|
+
const rate = Number(card.dataset.rate);
|
|
536
|
+
const perMonth = rate * (1 - off / 100);
|
|
537
|
+
const [whole, cents] = money(perMonth).split(',');
|
|
538
|
+
|
|
539
|
+
card.querySelector('[data-price-amount]').innerHTML = cents
|
|
540
|
+
? whole + '<span class="mw-price-fraction">,' + cents + '</span>'
|
|
541
|
+
: whole;
|
|
542
|
+
|
|
543
|
+
const original = card.querySelector('.mw-price-original');
|
|
544
|
+
original.hidden = off === 0;
|
|
545
|
+
original.querySelector('[data-price-rate]').textContent =
|
|
546
|
+
money(rate);
|
|
547
|
+
|
|
548
|
+
const tag = card.querySelector('[data-price-off]');
|
|
549
|
+
tag.hidden = off === 0;
|
|
550
|
+
tag.textContent = '-' + off + '%';
|
|
551
|
+
|
|
552
|
+
card.querySelector('[data-price-billing]').textContent =
|
|
553
|
+
off === 0
|
|
554
|
+
? button.dataset.cycleLabel + '.'
|
|
555
|
+
: money(perMonth * months) +
|
|
556
|
+
' Euro ' +
|
|
557
|
+
button.dataset.cycleLabel +
|
|
558
|
+
'.';
|
|
559
|
+
});
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
cycleSwitch.querySelectorAll('.mw-segmented-item').forEach((item) => {
|
|
563
|
+
item.addEventListener('click', () => apply(item));
|
|
564
|
+
});
|
|
565
|
+
apply(cycleSwitch.querySelector('.mw-active'));
|
|
566
|
+
});
|
|
567
|
+
|
|
500
568
|
// for the modals
|
|
501
569
|
function openModal(modalId) {
|
|
502
570
|
document.getElementById(modalId).classList.add('mw-modal-open');
|