maverick-wave 3.11.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/maverick-wave/SKILL.md +59 -30
- package/.claude/skills/maverick-wave/examples/angular-form.md +1 -1
- package/.claude/skills/maverick-wave/examples/angular-services.md +15 -8
- package/.claude/skills/maverick-wave/examples/static-landing-page.md +4 -4
- package/.claude/skills/maverick-wave/references/components.md +269 -25
- package/.claude/skills/maverick-wave/references/forms.md +8 -4
- package/.claude/skills/maverick-wave/references/javascript.md +20 -8
- package/.claude/skills/maverick-wave/references/layout.md +48 -3
- package/.claude/skills/maverick-wave/references/theming.md +34 -25
- package/CHANGELOG.md +46 -0
- package/index.html +25 -0
- package/maverick-wave.min.css +2 -2
- package/maverick-wave.min.js +1 -1
- package/package.json +2 -2
- package/src/js/main.js +314 -20
- package/src/partials/accordions-container.html +2 -2
- package/src/partials/button-bar-container.html +4 -2
- package/src/partials/buttons-container.html +35 -0
- package/src/partials/calendar-container.html +205 -0
- package/src/partials/cards-container.html +43 -0
- package/src/partials/documentation-container.html +3 -0
- package/src/partials/form-container.html +1 -1
- package/src/partials/get-started-container.html +2 -2
- package/src/partials/kanban-container.html +680 -715
- package/src/partials/modals-container.html +5 -5
- package/src/partials/pagination-container.html +50 -13
- package/src/partials/segmented-container.html +66 -0
- package/src/partials/tables-container.html +96 -24
- package/src/partials/tabs-container.html +16 -6
- package/src/partials/tags-container.html +15 -0
- package/src/partials/timelines-container.html +2 -2
- package/src/partials/utilities-container.html +81 -0
- package/src/scss/abstracts/_mixins.scss +34 -0
- package/src/scss/abstracts/_variables.scss +4 -0
- package/src/scss/base/_base.scss +21 -0
- package/src/scss/components/_accordions.scss +2 -0
- package/src/scss/components/_button-bar.scss +6 -0
- package/src/scss/components/_buttons.scss +67 -0
- package/src/scss/components/_calendar.scss +290 -0
- package/src/scss/components/_content-slider.scss +1 -0
- package/src/scss/components/_index.scss +2 -0
- package/src/scss/components/_modals.scss +15 -4
- package/src/scss/components/_pagination.scss +68 -64
- package/src/scss/components/_segmented.scss +85 -0
- package/src/scss/components/_tables.scss +16 -0
- package/src/scss/components/_tabs.scss +15 -0
- package/src/scss/components/_tags.scss +16 -0
- package/src/scss/components/_theme-toggle.scss +2 -0
- package/src/scss/components/_timelines.scss +3 -0
- package/src/scss/form-elements/_form.scss +13 -7
- package/src/scss/layout/_header.scss +3 -0
- package/src/scss/layout/_main.scss +5 -1
- package/src/scss/layout/_section.scss +1 -1
- package/src/scss/utilities/_accessibility.scss +14 -0
- package/src/scss/utilities/_flex.scss +17 -0
- package/src/scss/utilities/_index.scss +2 -0
- package/src/scss/utilities/_text.scss +17 -5
- package/src/scss/utilities/_touch-targets.scss +54 -0
|
@@ -19,9 +19,27 @@ exhaustive: what is not named does not exist.
|
|
|
19
19
|
- Sizes: `mw-btn-sm`, `mw-btn-lg`
|
|
20
20
|
- `mw-btn` is `inline-flex` with a gap - icons need no wrapper and no extra class
|
|
21
21
|
- `disabled` gets `opacity: .6` and `not-allowed`; there is no disabled class
|
|
22
|
-
- `active`
|
|
22
|
+
- `mw-active` gives the pressed/selected look on the coloured variants
|
|
23
23
|
- `mw-btn-link` still has the button padding - `mw-p-0` makes it read as inline
|
|
24
24
|
text
|
|
25
|
+
- On a coarse pointer or below 768px `mw-btn-sm` grows to a 2.5rem minimum
|
|
26
|
+
height on its own
|
|
27
|
+
|
|
28
|
+
### `mw-btn-plain` - a button with no button in it
|
|
29
|
+
|
|
30
|
+
For everywhere a `<button>` is the right element and a button is the wrong look:
|
|
31
|
+
a calendar day, a time in a request row, a settings tab, a modal backdrop.
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<button type="button" class="mw-btn-plain">14:30</button>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
It clears `appearance`, background, border, padding and margin, inherits font
|
|
38
|
+
and colour, and keeps exactly two things: the pointer and the focus ring. Used
|
|
39
|
+
on its own - `mw-btn` is neither needed nor wanted next to it.
|
|
40
|
+
|
|
41
|
+
Not the same as `mw-btn-link`, which is a button dressed as a link and brings
|
|
42
|
+
its own colour and minimum width.
|
|
25
43
|
|
|
26
44
|
**Destructive actions use `mw-btn-danger`.** `mw-btn-secondary` is a brand
|
|
27
45
|
colour, not a semantic one.
|
|
@@ -54,9 +72,56 @@ Row of equally treated buttons, centered by default.
|
|
|
54
72
|
- Colouring: `mw-button-bar-primary`, `-secondary`, `-outline` style the bare
|
|
55
73
|
`mw-btn` children, so the buttons carry no variant class themselves.
|
|
56
74
|
`mw-button-bar-nav` mixes them for prev/center/next navigation.
|
|
57
|
-
- Alignment: `mw-button-bar-left`, `-right` (
|
|
58
|
-
`mw-button-bar-between` pushes first and last apart.
|
|
75
|
+
- Alignment: `mw-button-bar-left`, `-right`, `-center` (which is also the
|
|
76
|
+
default), `mw-button-bar-between` pushes first and last apart.
|
|
59
77
|
- Sizes: `mw-button-bar-sm`, `mw-button-bar-lg`.
|
|
78
|
+
- Below 576px it stacks and gives every button 96% width. Right for a row of
|
|
79
|
+
independent actions - wrong for a switch, see below.
|
|
80
|
+
|
|
81
|
+
### `mw-actions-note` - one line above a row of actions
|
|
82
|
+
|
|
83
|
+
```html
|
|
84
|
+
<div class="mw-modal-footer">
|
|
85
|
+
<p class="mw-actions-note">This action cannot be undone.</p>
|
|
86
|
+
<button class="mw-btn mw-btn-outline">Cancel</button>
|
|
87
|
+
<button class="mw-btn mw-btn-danger">Delete</button>
|
|
88
|
+
</div>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Right-aligned, muted, one size smaller. For what the buttons apply to, why one
|
|
92
|
+
of them is disabled, or that something cannot be taken back.
|
|
93
|
+
|
|
94
|
+
- Works in `mw-modal-footer`, `mw-card-footer`, `mw-form-actions` and
|
|
95
|
+
`mw-panel-footer`. It is a child of the action row, not a wrapper around it:
|
|
96
|
+
the line takes the full width and pushes the buttons onto the row below.
|
|
97
|
+
- Only rows that actually carry a note start wrapping, so adding one changes
|
|
98
|
+
nothing anywhere else.
|
|
99
|
+
- Inside `mw-form-actions` the alignment variants steer it as well -
|
|
100
|
+
`mw-form-actions-left` makes the note left-aligned too.
|
|
101
|
+
- `mw-form-actions-hint` is the old, form-only name for the same thing. Still
|
|
102
|
+
styled, but use `mw-actions-note`: the hint name is wrong the moment the line
|
|
103
|
+
sits in a modal.
|
|
104
|
+
|
|
105
|
+
### Segmented control
|
|
106
|
+
|
|
107
|
+
Two or three positions of **one** switch: income/expense, offer/quote, a period
|
|
108
|
+
picker.
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<div class="mw-segmented">
|
|
112
|
+
<button type="button" class="mw-segmented-item mw-active">Income</button>
|
|
113
|
+
<button type="button" class="mw-segmented-item">Expense</button>
|
|
114
|
+
</div>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- Stays horizontal at every width and splits the row into equal shares - which
|
|
118
|
+
is the whole point. Stacked in a `mw-button-bar`, two positions of a switch
|
|
119
|
+
look like two buttons you could press both of.
|
|
120
|
+
- Active position: `mw-active` on the item.
|
|
121
|
+
- `mw-segmented-secondary` switches the active fill to the secondary colour,
|
|
122
|
+
`mw-segmented-auto` shrinks the control to its content instead of filling the
|
|
123
|
+
row. `disabled` works on an item.
|
|
124
|
+
- Items reach a 2.5rem minimum height on a coarse pointer.
|
|
60
125
|
|
|
61
126
|
## Cards
|
|
62
127
|
|
|
@@ -169,11 +234,11 @@ Tile sizes: `mw-tile-sm`, `mw-tile-lg`.
|
|
|
169
234
|
```html
|
|
170
235
|
<div class="mw-accordion">
|
|
171
236
|
<div class="mw-accordion-item">
|
|
172
|
-
<div class="mw-accordion-header active">
|
|
237
|
+
<div class="mw-accordion-header mw-active">
|
|
173
238
|
<h3>Question</h3>
|
|
174
239
|
<i class="fas fa-chevron-down mw-accordion-icon"></i>
|
|
175
240
|
</div>
|
|
176
|
-
<div class="mw-accordion-content active">
|
|
241
|
+
<div class="mw-accordion-content mw-active">
|
|
177
242
|
<div class="mw-accordion-content-inner">Answer</div>
|
|
178
243
|
</div>
|
|
179
244
|
</div>
|
|
@@ -189,11 +254,11 @@ via the header state. Content taller than 500px scrolls. Toggling is JS - see
|
|
|
189
254
|
```html
|
|
190
255
|
<div class="mw-tabs">
|
|
191
256
|
<div class="mw-tabs-nav">
|
|
192
|
-
<div class="mw-tabs-nav-item active" data-tab="tab1">Details</div>
|
|
257
|
+
<div class="mw-tabs-nav-item mw-active" data-tab="tab1">Details</div>
|
|
193
258
|
<div class="mw-tabs-nav-item" data-tab="tab2">History</div>
|
|
194
259
|
</div>
|
|
195
260
|
<div class="mw-tabs-content">
|
|
196
|
-
<div class="mw-tabs-panel active" id="tab1">...</div>
|
|
261
|
+
<div class="mw-tabs-panel mw-active" id="tab1">...</div>
|
|
197
262
|
<div class="mw-tabs-panel" id="tab2">...</div>
|
|
198
263
|
</div>
|
|
199
264
|
</div>
|
|
@@ -202,8 +267,15 @@ via the header state. Content taller than 500px scrolls. Toggling is JS - see
|
|
|
202
267
|
- Variants: `mw-tabs-vertical` (nav on the left, horizontal again below `sm`),
|
|
203
268
|
`mw-tabs-pills`.
|
|
204
269
|
- `data-tab` matches the panel `id` - that pairing is only needed for the
|
|
205
|
-
shipped JS. In a SPA, bind `active` yourself and drop the attribute.
|
|
206
|
-
- The nav scrolls horizontally
|
|
270
|
+
shipped JS. In a SPA, bind `mw-active` yourself and drop the attribute.
|
|
271
|
+
- The nav scrolls horizontally instead of wrapping, and says so: a shadow shows
|
|
272
|
+
on whichever side still has tabs behind it and disappears once that end is
|
|
273
|
+
reached. Pure CSS, no scroll listener. The fade colour comes from
|
|
274
|
+
`--mw-scroll-hint-cover`, which is preset to the page background and
|
|
275
|
+
re-pointed to the card background inside `mw-card`, `mw-panel`, `mw-modal`,
|
|
276
|
+
`mw-tile` and `mw-calendar` - override it if the strip sits on some other
|
|
277
|
+
surface.
|
|
278
|
+
- Tab items reach a 2.75rem minimum height on a coarse pointer.
|
|
207
279
|
|
|
208
280
|
## Modal
|
|
209
281
|
|
|
@@ -215,9 +287,10 @@ via the header state. Content taller than 500px scrolls. Toggling is JS - see
|
|
|
215
287
|
<button class="mw-modal-close" type="button">𝗫</button>
|
|
216
288
|
</div>
|
|
217
289
|
<div class="mw-modal-body">
|
|
218
|
-
<p>
|
|
290
|
+
<p>Invoice 2026-0042 will be removed.</p>
|
|
219
291
|
</div>
|
|
220
292
|
<div class="mw-modal-footer">
|
|
293
|
+
<p class="mw-actions-note">This action cannot be undone.</p>
|
|
221
294
|
<button class="mw-btn mw-btn-outline">Cancel</button>
|
|
222
295
|
<button class="mw-btn mw-btn-danger">Delete</button>
|
|
223
296
|
</div>
|
|
@@ -230,11 +303,12 @@ via the header state. Content taller than 500px scrolls. Toggling is JS - see
|
|
|
230
303
|
class is the whole open/close mechanism.
|
|
231
304
|
- Body scroll lock is automatic: the stylesheet uses
|
|
232
305
|
`body:has(.mw-modal-open)`. Nothing to implement.
|
|
233
|
-
- Sizes: `mw-modal-sm` 370px, default
|
|
234
|
-
`mw-modal-xl`
|
|
306
|
+
- Sizes: `mw-modal-sm` 370px, default 520px, `mw-modal-lg` 720px,
|
|
307
|
+
`mw-modal-xl` 960px. Height is capped at 80-92dvh, the body scrolls.
|
|
235
308
|
- `mw-modal-backdrop` is the click-to-close surface; put the close handler on it.
|
|
236
309
|
- `mw-modal-body` takes a `mw-form` directly - the form brings the field gaps,
|
|
237
310
|
the body brings the padding.
|
|
311
|
+
- `mw-actions-note` is the muted line above the buttons - see below.
|
|
238
312
|
|
|
239
313
|
Angular: `<div class="mw-modal-overlay" [class.mw-modal-open]="isOpen()">`.
|
|
240
314
|
|
|
@@ -355,9 +429,17 @@ Shapes: `mw-skeleton-title`, `-text`, `-circle`, `-rect` (+ `-rect-sm`,
|
|
|
355
429
|
| `mw-table-hover` | Stronger row hover |
|
|
356
430
|
| `mw-table-cards` | Below `md` every row becomes a card; keep `<thead>` (hidden via CSS) and give each cell a `data-label` |
|
|
357
431
|
| `mw-table-responsive` | Wrapper, horizontal scroll |
|
|
432
|
+
| `mw-table-responsive-hint` | On top of the wrapper: a soft right edge showing there are more columns. Opt-in - see below |
|
|
358
433
|
| `mw-table-responsive-scroll` | Wrapper with a height cap (`--mw-table-scroll-height`, 400px / 260px below `sm`) and vertical scroll |
|
|
359
434
|
| `mw-table-sticky-head` | Header stays put while the body scrolls - only works inside a height-limited wrapper, i.e. `mw-table-responsive-scroll` |
|
|
360
435
|
|
|
436
|
+
`mw-table-responsive-hint` is opt-in, unlike the automatic hint on a tab bar,
|
|
437
|
+
and works differently for a reason: a table paints its own opaque surface, so
|
|
438
|
+
the gradient trick used on `mw-tabs-nav` would sit _behind_ the rows and never
|
|
439
|
+
show. A mask sits in front and does show - but a mask has no `local` attachment,
|
|
440
|
+
so it cannot fade away once the last column is reached. Put the class on the
|
|
441
|
+
tables you know overflow, leave it off the ones that fit.
|
|
442
|
+
|
|
361
443
|
```html
|
|
362
444
|
<div class="mw-table-responsive-scroll" style="--mw-table-scroll-height: 250px">
|
|
363
445
|
<table class="mw-table mw-table-subtle mw-table-sticky-head">
|
|
@@ -510,6 +592,139 @@ lane it is needed in.
|
|
|
510
592
|
them.
|
|
511
593
|
- `mw-kanban-editing` hides the ticket the composer is currently replacing.
|
|
512
594
|
|
|
595
|
+
## Calendar
|
|
596
|
+
|
|
597
|
+
A card-shaped surface with a header and a seven column grid. A month is six
|
|
598
|
+
rows, a week is one - same classes, same cell size, the only difference is how
|
|
599
|
+
many cells you render. Which page is on screen is application state: the shipped
|
|
600
|
+
JS renders it from `data-calendar`, an Angular app renders the same markup
|
|
601
|
+
itself.
|
|
602
|
+
|
|
603
|
+
```html
|
|
604
|
+
<div
|
|
605
|
+
class="mw-calendar"
|
|
606
|
+
data-calendar="month"
|
|
607
|
+
data-calendar-markers='{"2026-08-19": ["success", "warning"]}'
|
|
608
|
+
>
|
|
609
|
+
<div class="mw-calendar-header">
|
|
610
|
+
<button
|
|
611
|
+
type="button"
|
|
612
|
+
class="mw-btn mw-btn-outline mw-btn-sm mw-calendar-nav"
|
|
613
|
+
data-calendar-nav="-1"
|
|
614
|
+
aria-label="Previous month"
|
|
615
|
+
>
|
|
616
|
+
<i class="fas fa-chevron-left"></i>
|
|
617
|
+
</button>
|
|
618
|
+
|
|
619
|
+
<div class="mw-calendar-title" aria-live="polite">August 2026</div>
|
|
620
|
+
|
|
621
|
+
<button
|
|
622
|
+
type="button"
|
|
623
|
+
class="mw-btn mw-btn-outline mw-btn-sm mw-calendar-nav"
|
|
624
|
+
data-calendar-nav="1"
|
|
625
|
+
aria-label="Next month"
|
|
626
|
+
>
|
|
627
|
+
<i class="fas fa-chevron-right"></i>
|
|
628
|
+
</button>
|
|
629
|
+
</div>
|
|
630
|
+
|
|
631
|
+
<div class="mw-calendar-grid">
|
|
632
|
+
<div class="mw-calendar-weekday" aria-hidden="true">Mon</div>
|
|
633
|
+
...
|
|
634
|
+
<div class="mw-calendar-weekday mw-calendar-weekend" aria-hidden="true">
|
|
635
|
+
Sat
|
|
636
|
+
</div>
|
|
637
|
+
|
|
638
|
+
<button
|
|
639
|
+
type="button"
|
|
640
|
+
class="mw-calendar-day mw-calendar-adjacent"
|
|
641
|
+
aria-label="Monday, 27 July 2026"
|
|
642
|
+
>
|
|
643
|
+
<span class="mw-calendar-date">27</span>
|
|
644
|
+
</button>
|
|
645
|
+
|
|
646
|
+
<button
|
|
647
|
+
type="button"
|
|
648
|
+
class="mw-calendar-day mw-calendar-today"
|
|
649
|
+
aria-current="date"
|
|
650
|
+
aria-pressed="false"
|
|
651
|
+
aria-label="Wednesday, 19 August 2026"
|
|
652
|
+
>
|
|
653
|
+
<span class="mw-calendar-date">19</span>
|
|
654
|
+
<span class="mw-calendar-dots">
|
|
655
|
+
<span class="mw-calendar-dot mw-calendar-dot-success"></span>
|
|
656
|
+
<span class="mw-calendar-dot mw-calendar-dot-warning"></span>
|
|
657
|
+
</span>
|
|
658
|
+
</button>
|
|
659
|
+
...
|
|
660
|
+
</div>
|
|
661
|
+
|
|
662
|
+
<div class="mw-calendar-legend">
|
|
663
|
+
<span class="mw-calendar-legend-item">
|
|
664
|
+
<span class="mw-calendar-dot mw-calendar-dot-success"></span>
|
|
665
|
+
Slots free
|
|
666
|
+
</span>
|
|
667
|
+
...
|
|
668
|
+
</div>
|
|
669
|
+
</div>
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
| Class | Role |
|
|
673
|
+
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
674
|
+
| `mw-calendar` | Surface. Same border, radius and shadow as a card, without the hover lift |
|
|
675
|
+
| `mw-calendar-plain` | Drops the surface - for a calendar already sitting on a card or panel |
|
|
676
|
+
| `mw-calendar-compact` | Date picker density: dots hidden, 29px cells, tighter header and labels |
|
|
677
|
+
| `mw-calendar-header` | Arrow, title, arrow |
|
|
678
|
+
| `mw-calendar-nav` | Sits **on** `mw-btn` - only squares it off around the chevron |
|
|
679
|
+
| `mw-calendar-title` | Takes the space between the arrows, stays optically centred |
|
|
680
|
+
| `mw-calendar-grid` | The seven column grid; weekday labels and day cells are its only children |
|
|
681
|
+
| `mw-calendar-weekday` | Column label. Decorative - the day buttons carry the weekday themselves |
|
|
682
|
+
| `mw-calendar-day` | One day. A `button`, so `:disabled` gives you an unavailable day |
|
|
683
|
+
| `mw-calendar-date` | The number |
|
|
684
|
+
| `mw-calendar-adjacent` | Day of the neighbouring month - muted, still readable |
|
|
685
|
+
| `mw-calendar-weekend` | Saturday/Sunday. Tinted cell; on the label it turns the text secondary |
|
|
686
|
+
| `mw-calendar-today` | Outlined in the secondary colour, bold |
|
|
687
|
+
| `mw-selected` | Filled with the primary colour |
|
|
688
|
+
| `mw-calendar-dots` | Dot row, absolutely placed at the bottom of the cell |
|
|
689
|
+
| `mw-calendar-dot` | 5px dot. Colour comes from `--mw-calendar-dot`; the `-primary`, `-secondary`, `-success`, `-warning`, `-danger`, `-info` classes are presets for it |
|
|
690
|
+
| `mw-calendar-legend` | Rule plus a row of dot/label pairs below the grid |
|
|
691
|
+
| `mw-calendar-legend-item` | One dot/label pair |
|
|
692
|
+
|
|
693
|
+
- **Today is outlined in secondary, the picked day is filled with primary.** Two
|
|
694
|
+
different colours on purpose: an outline and a fill in the same colour read as
|
|
695
|
+
two states of the same thing. Do not give one day both.
|
|
696
|
+
- The dots are positioned absolutely, so a day without any keeps the exact same
|
|
697
|
+
height and its number sits on the same line as every other. Two or three per
|
|
698
|
+
day stay readable, more do not. A dot on a picked day keeps its status colour;
|
|
699
|
+
only the untoned one flips to the accent text colour.
|
|
700
|
+
- **What a dot means belongs to the caller.** The same calendar reads "request
|
|
701
|
+
pending / booked / time offered" in a scheduling app and something entirely
|
|
702
|
+
different on a public booking page, so the colour is a custom property, not a
|
|
703
|
+
fixed set: `style="--mw-calendar-dot: #7a4fd4"` on the dot, or on the cell to
|
|
704
|
+
colour all of its dots. The six tone classes are named presets for exactly
|
|
705
|
+
that property - use them when they fit, ignore them when they do not.
|
|
706
|
+
- **Render the days spilling in from the neighbouring month.** An empty first
|
|
707
|
+
row reads like a broken calendar, not like a short month. `mw-calendar-adjacent`
|
|
708
|
+
is what pushes them back.
|
|
709
|
+
- A month gets six rows even when five would do, so the calendar - and
|
|
710
|
+
everything under it - keeps its height while you page through.
|
|
711
|
+
- Cell height is fixed (38px, 44px on a coarse pointer, 29px compact), width
|
|
712
|
+
follows the container. No aspect ratio: a full-width calendar would otherwise
|
|
713
|
+
grow rows several hundred pixels tall. A calendar therefore wants a column,
|
|
714
|
+
not the full page width - give it one, or cap it.
|
|
715
|
+
- **A week has no class of its own.** Render seven cells instead of forty-two
|
|
716
|
+
and you have one; everything else is identical.
|
|
717
|
+
- `mw-calendar-compact` hides the dots and shortens the cell - the dots are what
|
|
718
|
+
a full cell needs its height for, so the two go together. It costs about a
|
|
719
|
+
third of the calendar height and leaves a plain date picker; drop the legend
|
|
720
|
+
there, it has nothing left to explain. 29px is below a comfortable tap target,
|
|
721
|
+
so keep it for pointer-first surfaces. Its grid gap drops to 2px as well, which
|
|
722
|
+
puts the space back into the cells.
|
|
723
|
+
- Give every day an `aria-label` with the full date - the bare number is not an
|
|
724
|
+
accessible name. The weekday labels are `aria-hidden`, they would only repeat
|
|
725
|
+
it. With a `button` per day, keep one tab stop for the grid (roving
|
|
726
|
+
`tabindex`) instead of 42.
|
|
727
|
+
|
|
513
728
|
## Tags
|
|
514
729
|
|
|
515
730
|
Two forms, picked by count - see also the pitfall list in `SKILL.md`.
|
|
@@ -521,8 +736,14 @@ Two forms, picked by count - see also the pitfall list in `SKILL.md`.
|
|
|
521
736
|
```
|
|
522
737
|
|
|
523
738
|
Variants: `mw-tag-primary`, `-secondary`, `-success`, `-info`, `-warning`,
|
|
524
|
-
`-danger`, `-muted`. Size: `mw-tag-lg`.
|
|
525
|
-
|
|
739
|
+
`-danger`, `-muted`, `-neutral`. Size: `mw-tag-lg`.
|
|
740
|
+
|
|
741
|
+
`mw-tag-muted` and `mw-tag-neutral` are not the same and the difference carries
|
|
742
|
+
meaning. **Muted** means the label steps back - draft, archived, a shortcoming
|
|
743
|
+
("no e-invoice"). **Neutral** is an identifier with no judgement attached and
|
|
744
|
+
keeps the normal text colour - a customer number, a document type, a price on a
|
|
745
|
+
category ("XRechnung" is a property, not a verdict). Both are also available on
|
|
746
|
+
the list container: `mw-tags-muted`, `mw-tags-neutral`.
|
|
526
747
|
|
|
527
748
|
**List** - the container carries the colour for all its items:
|
|
528
749
|
|
|
@@ -670,7 +891,7 @@ Both are chronicles (CV, changelog), not schedulable time axes.
|
|
|
670
891
|
|
|
671
892
|
```html
|
|
672
893
|
<div class="mw-timeline-big">
|
|
673
|
-
<div class="mw-timeline-big-step active">
|
|
894
|
+
<div class="mw-timeline-big-step mw-active">
|
|
674
895
|
<div class="mw-timeline-big-date-container">
|
|
675
896
|
<div class="mw-timeline-big-date-main">01/2023 - today</div>
|
|
676
897
|
<div class="mw-timeline-big-date-sub">(3 years)</div>
|
|
@@ -686,7 +907,7 @@ Both are chronicles (CV, changelog), not schedulable time axes.
|
|
|
686
907
|
</div>
|
|
687
908
|
|
|
688
909
|
<div class="mw-timeline-simple">
|
|
689
|
-
<div class="mw-timeline-simple-step active">
|
|
910
|
+
<div class="mw-timeline-simple-step mw-active">
|
|
690
911
|
<div class="mw-timeline-simple-date">2024-03-22</div>
|
|
691
912
|
<div class="mw-timeline-simple-content">
|
|
692
913
|
<div class="mw-card mw-card-simple">...</div>
|
|
@@ -803,23 +1024,46 @@ The last item takes `mw-breadcrumbs-current` on the `li` and
|
|
|
803
1024
|
|
|
804
1025
|
## Pagination
|
|
805
1026
|
|
|
806
|
-
A titled content frame with prev/next controls - not a page-number list.
|
|
1027
|
+
A titled content frame with prev/next controls - not a page-number list. Same
|
|
1028
|
+
surface and header as `mw-calendar`; only the body differs, and the body is
|
|
1029
|
+
yours.
|
|
807
1030
|
|
|
808
1031
|
```html
|
|
809
1032
|
<div class="mw-pagination">
|
|
810
1033
|
<div class="mw-pagination-header">
|
|
811
|
-
<button
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1034
|
+
<button
|
|
1035
|
+
type="button"
|
|
1036
|
+
class="mw-btn mw-btn-outline mw-btn-sm mw-pagination-nav"
|
|
1037
|
+
aria-label="Previous week"
|
|
1038
|
+
>
|
|
1039
|
+
<i class="fas fa-chevron-left"></i>
|
|
1040
|
+
</button>
|
|
1041
|
+
|
|
1042
|
+
<div class="mw-pagination-title" aria-live="polite">Week 10</div>
|
|
1043
|
+
|
|
1044
|
+
<button
|
|
1045
|
+
type="button"
|
|
1046
|
+
class="mw-btn mw-btn-outline mw-btn-sm mw-pagination-nav"
|
|
1047
|
+
aria-label="Next week"
|
|
1048
|
+
>
|
|
1049
|
+
<i class="fas fa-chevron-right"></i>
|
|
815
1050
|
</button>
|
|
816
1051
|
</div>
|
|
1052
|
+
|
|
817
1053
|
<div class="mw-pagination-content">...</div>
|
|
818
1054
|
</div>
|
|
819
1055
|
```
|
|
820
1056
|
|
|
821
|
-
`mw-pagination-
|
|
822
|
-
|
|
1057
|
+
- `mw-pagination-nav` sits **on** `mw-btn` - it only squares the button off
|
|
1058
|
+
around the chevron. The arrows carry no accessible name of their own, so give
|
|
1059
|
+
each one an `aria-label`; `aria-live="polite"` on the title is what tells a
|
|
1060
|
+
screen reader where you landed.
|
|
1061
|
+
- `mw-pagination-loading` goes on `mw-pagination-content` (not on the
|
|
1062
|
+
container) and dims it while the next page is on its way.
|
|
1063
|
+
- `mw-pagination-slide-left` / `-slide-right` are the directional transitions,
|
|
1064
|
+
also on the content.
|
|
1065
|
+
- The body has a 200px floor so a short page does not collapse the frame. Paging
|
|
1066
|
+
is application state - the framework ships no JavaScript for it.
|
|
823
1067
|
|
|
824
1068
|
## Divider
|
|
825
1069
|
|
|
@@ -867,12 +1111,12 @@ shipped JS, as is the track transform. In a SPA, render the dots and set
|
|
|
867
1111
|
<img class="mw-image-slider-base-image" src="base.jpg" alt="" />
|
|
868
1112
|
</div>
|
|
869
1113
|
<div class="mw-image-slider-overlay">
|
|
870
|
-
<div class="mw-image-slider-overlay-image active" data-index="1">
|
|
1114
|
+
<div class="mw-image-slider-overlay-image mw-active" data-index="1">
|
|
871
1115
|
<img src="a.jpg" alt="" />
|
|
872
1116
|
</div>
|
|
873
1117
|
</div>
|
|
874
1118
|
<div class="mw-image-slider-controls-grid-3">
|
|
875
|
-
<button class="mw-btn mw-btn-primary active" data-index="0">Base</button>
|
|
1119
|
+
<button class="mw-btn mw-btn-primary mw-active" data-index="0">Base</button>
|
|
876
1120
|
</div>
|
|
877
1121
|
</div>
|
|
878
1122
|
```
|
|
@@ -62,7 +62,7 @@ reactive form control.
|
|
|
62
62
|
</div>
|
|
63
63
|
|
|
64
64
|
<div class="mw-form-actions">
|
|
65
|
-
<p class="mw-
|
|
65
|
+
<p class="mw-actions-note">Changes are saved immediately.</p>
|
|
66
66
|
<button type="button" class="mw-btn mw-btn-outline">Cancel</button>
|
|
67
67
|
<button type="submit" class="mw-btn mw-btn-primary">Save</button>
|
|
68
68
|
</div>
|
|
@@ -77,9 +77,13 @@ reactive form control.
|
|
|
77
77
|
`mw-form-group-title` is the heading hook inside it - put it on the `<h3>` /
|
|
78
78
|
`<h4>`, otherwise the heading keeps its full document-level size.
|
|
79
79
|
- `mw-form-actions` is a right-aligned wrapping button row.
|
|
80
|
-
`mw-
|
|
81
|
-
|
|
82
|
-
`mw-form-actions-
|
|
80
|
+
`mw-actions-note` is a full-width note above the buttons and works the same
|
|
81
|
+
way in a modal, card or panel footer (`references/components.md`). The
|
|
82
|
+
alignment variants steer it along: `mw-form-actions-left`,
|
|
83
|
+
`mw-form-actions-center`, `mw-form-actions-full-width` (stacked, buttons at
|
|
84
|
+
100% - login forms).
|
|
85
|
+
`mw-form-actions-hint` is the old name for the note and still styled, but new
|
|
86
|
+
markup should use `mw-actions-note`.
|
|
83
87
|
- Put multi-column layouts inside a group with `mw-grid-2` etc. and let a field
|
|
84
88
|
span everything with `style="grid-column: 1 / -1"`.
|
|
85
89
|
|
|
@@ -26,32 +26,44 @@ classes are the entire contract.
|
|
|
26
26
|
## Behaviour inventory
|
|
27
27
|
|
|
28
28
|
| Behaviour | What the shipped JS does | What to do instead |
|
|
29
|
-
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
-
| Accordion | Toggles `active` on `mw-accordion-header` and the following `mw-accordion-content`
|
|
31
|
-
| Tabs | `data-tab` → panel `id`; sets `active` on nav item and panel
|
|
29
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
30
|
+
| Accordion | Toggles `mw-active` on `mw-accordion-header` and the following `mw-accordion-content` | `[class.mw-active]="isOpen()"` on both elements |
|
|
31
|
+
| Tabs | `data-tab` → panel `id`; sets `mw-active` on nav item and panel | Track the selected index/key, bind `mw-active` on both; drop `data-tab` |
|
|
32
32
|
| Modal | Click on `mw-modal-close` removes `mw-modal-open` from the overlay | `[class.mw-modal-open]="isOpen()"`; backdrop click closes. Opening is not in the script at all (the showcase has its own `openModal`) |
|
|
33
33
|
| Mobile nav | Toggles `open` on `mw-menu-btn` and `mw-navbar`, closes on anchor click | One signal, bound to both; reset it on navigation end |
|
|
34
|
-
| Scroll spy | Sets `active` on `mw-navbar-link` from the scroll position
|
|
35
|
-
| Theme toggle | `localStorage['mw-theme']`, toggles `mw-theme-light` on `<body>` and `active` on the toggle
|
|
34
|
+
| Scroll spy | Sets `mw-active` on `mw-navbar-link` from the scroll position | Router-based: `routerLinkActive="mw-active"` |
|
|
35
|
+
| Theme toggle | `localStorage['mw-theme']`, toggles `mw-theme-light` on `<body>` and `mw-active` on the toggle | A theme service - see `examples/angular-services.md` |
|
|
36
36
|
| Progress bar | `IntersectionObserver` sets `width` from `data-value` | Bind `[style.width.%]="value()"` on `mw-progress-fill` |
|
|
37
37
|
| Slider | On `input`, sets `--value` (track fill) and `data-value` (badge text) | Bind `[style.--value.%]` and `[attr.data-value]` |
|
|
38
38
|
| Alerts | Close button adds `mw-alert-closed` (`display: none`) | Remove the alert from the list/signal |
|
|
39
39
|
| Checkbox lists | Adds `mw-selected` to the `li`, emits a `checkboxToggle` event, exposes `window.toggleCheckbox` | `[class.mw-selected]="item.checked"` |
|
|
40
40
|
| Gallery | Generates the dots, moves the track, swipe handling, writes `mw-gallery-desc` | Render dots in the template, bind the track transform and `mw-active` on the current dot |
|
|
41
|
-
| Image slider | Toggles `active` on the overlay image and the control button with the matching `data-index`
|
|
41
|
+
| 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 |
|
|
42
42
|
| Kanban board | Counts the tickets per lane, moves a card between lanes (`data-kanban-move`), 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 |
|
|
43
|
+
| 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 |
|
|
43
44
|
| 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 |
|
|
44
45
|
| Header login button | Swaps the FontAwesome lock icon | Bind the icon class |
|
|
45
46
|
| Color swatches | Showcase-only (prints computed hex values) | Not needed |
|
|
46
47
|
|
|
48
|
+
## A note on the state class
|
|
49
|
+
|
|
50
|
+
Since 4.0.0 the script writes `mw-active` and clears both `mw-active` and the
|
|
51
|
+
deprecated bare `active` when it switches a state off. That is deliberate: HTML
|
|
52
|
+
written against an older version marks the first tab with `active`, and if
|
|
53
|
+
switching away only removed `mw-active`, that first tab would stay lit next to
|
|
54
|
+
the newly chosen one. In your own components bind `mw-active` and forget the
|
|
55
|
+
other spelling exists.
|
|
56
|
+
|
|
47
57
|
## What works without any JavaScript
|
|
48
58
|
|
|
49
59
|
Pure CSS, nothing to wire up: hover and focus states, the card lift, tooltips
|
|
50
60
|
(`data-tooltip`), `mw-rating` (via `data-rating`), the responsive table card
|
|
51
61
|
view (`data-label`), all grids and utilities, the body scroll lock while a modal
|
|
52
62
|
is open (`body:has(.mw-modal-open)`), toast entry animations, the sticky table
|
|
53
|
-
header, the
|
|
54
|
-
lane
|
|
63
|
+
header, the scroll hint on a tab bar (four gradients, no scroll listener), the
|
|
64
|
+
kanban empty-lane placeholder (hidden via `:has()` as soon as the lane holds a
|
|
65
|
+
ticket), touch target sizing on a coarse pointer, `prefers-reduced-motion`
|
|
66
|
+
handling.
|
|
55
67
|
|
|
56
68
|
## When you do keep the shipped JS
|
|
57
69
|
|
|
@@ -26,7 +26,11 @@
|
|
|
26
26
|
- `mw-main` is a full-height flex column - the footer stays at the bottom on
|
|
27
27
|
short pages.
|
|
28
28
|
- `mw-container` is `min(1200px, 89%)`, horizontally centered. Nest it inside
|
|
29
|
-
every full-bleed band (header, section, footer), never around them.
|
|
29
|
+
every full-bleed band (header, section, footer), never around them. Below
|
|
30
|
+
576px it switches to a fixed 1rem gutter instead of the percentage - 89% of a
|
|
31
|
+
375px screen leaves 41px of nothing on a card that has to fit an amount, a
|
|
32
|
+
status and two labels into one row. Both widths are tokens,
|
|
33
|
+
`--mw-container-width` and `--mw-container-width-sm`.
|
|
30
34
|
- `mw-content` (`flex: 1` + top padding) is the alternative to `mw-section` when
|
|
31
35
|
a page has one single content area. `mw-content-centered` centers it
|
|
32
36
|
vertically over the full viewport - login pages, error pages.
|
|
@@ -59,7 +63,7 @@ its children are styled through descendant selectors:
|
|
|
59
63
|
<nav class="mw-navbar mw-navbar-medium">
|
|
60
64
|
<ul class="mw-navbar-list">
|
|
61
65
|
<li class="mw-navbar-item">
|
|
62
|
-
<a href="#start" class="mw-navbar-link active">Start</a>
|
|
66
|
+
<a href="#start" class="mw-navbar-link mw-active">Start</a>
|
|
63
67
|
</li>
|
|
64
68
|
<li class="mw-navbar-item">
|
|
65
69
|
<a href="#docs" class="mw-navbar-link">Docs</a>
|
|
@@ -153,7 +157,7 @@ viewport, so keep long tooltips off the outermost elements.
|
|
|
153
157
|
|
|
154
158
|
| Class | Use |
|
|
155
159
|
| ----------------------------------- | -------------------------------------------------------------------------- |
|
|
156
|
-
| `mw-section` | Vertical rhythm
|
|
160
|
+
| `mw-section` | Vertical rhythm for a page band - `--mw-section-padding-block`, 1.75rem |
|
|
157
161
|
| `mw-section-alternate` | Diagonal pattern background; combine with `mw-section` |
|
|
158
162
|
| `mw-section-title` | Centered `3xl` heading with a decorative primary underline - landing pages |
|
|
159
163
|
| `mw-section-subtitle` | Centered `2xl` heading with a thin secondary underline |
|
|
@@ -277,6 +281,47 @@ automatically.
|
|
|
277
281
|
**Display** - `mw-d-flex`, `mw-d-inline-flex`, `mw-d-block`, `mw-d-inline`,
|
|
278
282
|
`mw-d-inline-block`, `mw-d-grid`, `mw-d-none`, `mw-d-contents`.
|
|
279
283
|
|
|
284
|
+
**Numbers** - `mw-text-numeric` is fixed-width digits and nothing else, for a
|
|
285
|
+
clock, a counter or an ID that must not jitter while it changes.
|
|
286
|
+
`mw-text-currency` adds right alignment and `nowrap` on top, which is what a
|
|
287
|
+
money column in a table wants. Do not reach for `mw-text-currency` just to get
|
|
288
|
+
the digits - that was the old behaviour of `mw-text-numeric` and the reason
|
|
289
|
+
people wrote `font-variant-numeric` out by hand.
|
|
290
|
+
|
|
291
|
+
### `mw-row-split` - the row with two ends
|
|
292
|
+
|
|
293
|
+
The most common layout in any application: what it is on the left, the value or
|
|
294
|
+
the action on the right, wrapping to two lines when it runs out of room. Card
|
|
295
|
+
header, section header, key figure, week row.
|
|
296
|
+
|
|
297
|
+
```html
|
|
298
|
+
<div class="mw-row-split">
|
|
299
|
+
<strong>Invoice 2026-0042</strong>
|
|
300
|
+
<span class="mw-text-currency">1,204.50</span>
|
|
301
|
+
</div>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Five declarations you would otherwise write again in every component -
|
|
305
|
+
including the `flex-wrap` everyone forgets, which is what keeps the left half
|
|
306
|
+
from being squashed on a phone.
|
|
307
|
+
|
|
308
|
+
`mw-row-split` aligns on the baseline, which is right for text against text. Add
|
|
309
|
+
`mw-row-split-center` when the two sides differ in height - a heading beside a
|
|
310
|
+
button, a label beside an icon.
|
|
311
|
+
|
|
312
|
+
### `mw-sr-only` - text for screen readers only
|
|
313
|
+
|
|
314
|
+
```html
|
|
315
|
+
<button type="button" class="mw-btn mw-btn-outline">
|
|
316
|
+
<i class="fas fa-trash"></i>
|
|
317
|
+
<span class="mw-sr-only">Delete invoice 2026-0042</span>
|
|
318
|
+
</button>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Wherever an icon carries the whole message. Not `display: none` and not
|
|
322
|
+
`visibility: hidden` - both drop the element out of the accessibility tree,
|
|
323
|
+
which is precisely what must not happen.
|
|
324
|
+
|
|
280
325
|
There are no responsive display variants. Show/hide per breakpoint is the
|
|
281
326
|
application's job (media query in your own stylesheet, or `@if` in the
|
|
282
327
|
template).
|