maverick-wave 3.10.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 +62 -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 +416 -27
- package/.claude/skills/maverick-wave/references/forms.md +8 -4
- package/.claude/skills/maverick-wave/references/javascript.md +32 -18
- package/.claude/skills/maverick-wave/references/layout.md +48 -3
- package/.claude/skills/maverick-wave/references/theming.md +34 -24
- package/CHANGELOG.md +56 -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 +6 -2
- package/src/scss/base/_base.scss +21 -0
- package/src/scss/components/_accordions.scss +2 -0
- package/src/scss/components/_alerts.scss +5 -19
- 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/_kanban.scss +2 -6
- 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
|
|
|
@@ -96,8 +161,9 @@ Row of equally treated buttons, centered by default.
|
|
|
96
161
|
`-secondary`, `-success`, `-warning`, `-danger`, `-info`.
|
|
97
162
|
- A card does not clip its content, so a tooltip or dropdown inside it can reach
|
|
98
163
|
outside. The exception is `mw-card-ribbon`: that banner has to be cut off at
|
|
99
|
-
the edge, so a card
|
|
100
|
-
everything else too.
|
|
164
|
+
the edge, so a card with one as its **direct child** switches to
|
|
165
|
+
`overflow: hidden` and clips everything else too. A ribbon deeper inside - on
|
|
166
|
+
a card nested in another card - does not make the outer one clip.
|
|
101
167
|
|
|
102
168
|
```html
|
|
103
169
|
<div class="mw-card">
|
|
@@ -168,11 +234,11 @@ Tile sizes: `mw-tile-sm`, `mw-tile-lg`.
|
|
|
168
234
|
```html
|
|
169
235
|
<div class="mw-accordion">
|
|
170
236
|
<div class="mw-accordion-item">
|
|
171
|
-
<div class="mw-accordion-header active">
|
|
237
|
+
<div class="mw-accordion-header mw-active">
|
|
172
238
|
<h3>Question</h3>
|
|
173
239
|
<i class="fas fa-chevron-down mw-accordion-icon"></i>
|
|
174
240
|
</div>
|
|
175
|
-
<div class="mw-accordion-content active">
|
|
241
|
+
<div class="mw-accordion-content mw-active">
|
|
176
242
|
<div class="mw-accordion-content-inner">Answer</div>
|
|
177
243
|
</div>
|
|
178
244
|
</div>
|
|
@@ -188,11 +254,11 @@ via the header state. Content taller than 500px scrolls. Toggling is JS - see
|
|
|
188
254
|
```html
|
|
189
255
|
<div class="mw-tabs">
|
|
190
256
|
<div class="mw-tabs-nav">
|
|
191
|
-
<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>
|
|
192
258
|
<div class="mw-tabs-nav-item" data-tab="tab2">History</div>
|
|
193
259
|
</div>
|
|
194
260
|
<div class="mw-tabs-content">
|
|
195
|
-
<div class="mw-tabs-panel active" id="tab1">...</div>
|
|
261
|
+
<div class="mw-tabs-panel mw-active" id="tab1">...</div>
|
|
196
262
|
<div class="mw-tabs-panel" id="tab2">...</div>
|
|
197
263
|
</div>
|
|
198
264
|
</div>
|
|
@@ -201,8 +267,15 @@ via the header state. Content taller than 500px scrolls. Toggling is JS - see
|
|
|
201
267
|
- Variants: `mw-tabs-vertical` (nav on the left, horizontal again below `sm`),
|
|
202
268
|
`mw-tabs-pills`.
|
|
203
269
|
- `data-tab` matches the panel `id` - that pairing is only needed for the
|
|
204
|
-
shipped JS. In a SPA, bind `active` yourself and drop the attribute.
|
|
205
|
-
- 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.
|
|
206
279
|
|
|
207
280
|
## Modal
|
|
208
281
|
|
|
@@ -214,9 +287,10 @@ via the header state. Content taller than 500px scrolls. Toggling is JS - see
|
|
|
214
287
|
<button class="mw-modal-close" type="button">𝗫</button>
|
|
215
288
|
</div>
|
|
216
289
|
<div class="mw-modal-body">
|
|
217
|
-
<p>
|
|
290
|
+
<p>Invoice 2026-0042 will be removed.</p>
|
|
218
291
|
</div>
|
|
219
292
|
<div class="mw-modal-footer">
|
|
293
|
+
<p class="mw-actions-note">This action cannot be undone.</p>
|
|
220
294
|
<button class="mw-btn mw-btn-outline">Cancel</button>
|
|
221
295
|
<button class="mw-btn mw-btn-danger">Delete</button>
|
|
222
296
|
</div>
|
|
@@ -229,11 +303,12 @@ via the header state. Content taller than 500px scrolls. Toggling is JS - see
|
|
|
229
303
|
class is the whole open/close mechanism.
|
|
230
304
|
- Body scroll lock is automatic: the stylesheet uses
|
|
231
305
|
`body:has(.mw-modal-open)`. Nothing to implement.
|
|
232
|
-
- Sizes: `mw-modal-sm` 370px, default
|
|
233
|
-
`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.
|
|
234
308
|
- `mw-modal-backdrop` is the click-to-close surface; put the close handler on it.
|
|
235
309
|
- `mw-modal-body` takes a `mw-form` directly - the form brings the field gaps,
|
|
236
310
|
the body brings the padding.
|
|
311
|
+
- `mw-actions-note` is the muted line above the buttons - see below.
|
|
237
312
|
|
|
238
313
|
Angular: `<div class="mw-modal-overlay" [class.mw-modal-open]="isOpen()">`.
|
|
239
314
|
|
|
@@ -354,9 +429,17 @@ Shapes: `mw-skeleton-title`, `-text`, `-circle`, `-rect` (+ `-rect-sm`,
|
|
|
354
429
|
| `mw-table-hover` | Stronger row hover |
|
|
355
430
|
| `mw-table-cards` | Below `md` every row becomes a card; keep `<thead>` (hidden via CSS) and give each cell a `data-label` |
|
|
356
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 |
|
|
357
433
|
| `mw-table-responsive-scroll` | Wrapper with a height cap (`--mw-table-scroll-height`, 400px / 260px below `sm`) and vertical scroll |
|
|
358
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` |
|
|
359
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
|
+
|
|
360
443
|
```html
|
|
361
444
|
<div class="mw-table-responsive-scroll" style="--mw-table-scroll-height: 250px">
|
|
362
445
|
<table class="mw-table mw-table-subtle mw-table-sticky-head">
|
|
@@ -365,6 +448,283 @@ Shapes: `mw-skeleton-title`, `-text`, `-circle`, `-rect` (+ `-rect-sm`,
|
|
|
365
448
|
</div>
|
|
366
449
|
```
|
|
367
450
|
|
|
451
|
+
## Kanban
|
|
452
|
+
|
|
453
|
+
A board is a grid of equally wide lanes; a ticket is a plain `mw-card` with
|
|
454
|
+
`mw-kanban-card` on top. Everything except the counters and the composer is CSS.
|
|
455
|
+
|
|
456
|
+
```html
|
|
457
|
+
<div class="mw-kanban" style="--mw-kanban-column-min-height: 390px">
|
|
458
|
+
<div class="mw-kanban-column">
|
|
459
|
+
<div class="mw-kanban-column-header">
|
|
460
|
+
<h5 class="mw-kanban-title">In Progress</h5>
|
|
461
|
+
<span class="mw-kanban-count">2</span>
|
|
462
|
+
<button
|
|
463
|
+
type="button"
|
|
464
|
+
class="mw-btn mw-btn-secondary mw-btn-sm mw-kanban-add"
|
|
465
|
+
aria-label="Add ticket to In Progress"
|
|
466
|
+
>
|
|
467
|
+
<i class="fas fa-plus"></i>
|
|
468
|
+
</button>
|
|
469
|
+
</div>
|
|
470
|
+
|
|
471
|
+
<div class="mw-kanban-column-body">
|
|
472
|
+
<article class="mw-card mw-kanban-card">
|
|
473
|
+
<div class="mw-card-ribbon mw-card-addon-danger">High</div>
|
|
474
|
+
<p class="mw-kanban-card-title">Sticky table header jitters</p>
|
|
475
|
+
<p class="mw-kanban-card-text">
|
|
476
|
+
On Safari the header shifts by a pixel.
|
|
477
|
+
</p>
|
|
478
|
+
<div class="mw-kanban-card-footer">
|
|
479
|
+
<span class="mw-kanban-card-id">MW-102</span>
|
|
480
|
+
<div class="mw-kanban-card-actions">
|
|
481
|
+
<div class="mw-avatar mw-avatar-xs mw-avatar-initials">jd</div>
|
|
482
|
+
<button
|
|
483
|
+
type="button"
|
|
484
|
+
class="mw-kanban-action"
|
|
485
|
+
aria-label="Move MW-102 one lane left"
|
|
486
|
+
>
|
|
487
|
+
<i class="fas fa-chevron-left"></i>
|
|
488
|
+
</button>
|
|
489
|
+
<button
|
|
490
|
+
type="button"
|
|
491
|
+
class="mw-kanban-action"
|
|
492
|
+
aria-label="Move MW-102 one lane right"
|
|
493
|
+
>
|
|
494
|
+
<i class="fas fa-chevron-right"></i>
|
|
495
|
+
</button>
|
|
496
|
+
<button
|
|
497
|
+
type="button"
|
|
498
|
+
class="mw-kanban-action"
|
|
499
|
+
aria-label="Edit MW-102"
|
|
500
|
+
>
|
|
501
|
+
<i class="fas fa-pen"></i>
|
|
502
|
+
</button>
|
|
503
|
+
<button
|
|
504
|
+
type="button"
|
|
505
|
+
class="mw-kanban-action mw-kanban-action-danger"
|
|
506
|
+
aria-label="Delete MW-102"
|
|
507
|
+
>
|
|
508
|
+
<i class="fas fa-trash"></i>
|
|
509
|
+
</button>
|
|
510
|
+
</div>
|
|
511
|
+
</div>
|
|
512
|
+
</article>
|
|
513
|
+
|
|
514
|
+
<div class="mw-kanban-empty">No tickets</div>
|
|
515
|
+
</div>
|
|
516
|
+
</div>
|
|
517
|
+
...
|
|
518
|
+
</div>
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
| Class | Role |
|
|
522
|
+
| ------------------------- | ---------------------------------------------------------------------------------------- |
|
|
523
|
+
| `mw-kanban` | Board. Grid, one column per lane, same width for all of them |
|
|
524
|
+
| `mw-kanban-plain` | Board without its own surface or padding - for a board that already sits on a panel |
|
|
525
|
+
| `mw-kanban-compact` | Tighter padding, description clamped to 2 lines instead of 4, lane floor 90px |
|
|
526
|
+
| `mw-kanban-column` | Lane: dashed border, flex column |
|
|
527
|
+
| `mw-kanban-column-header` | Title + counter + add button in one row |
|
|
528
|
+
| `mw-kanban-title` | Lane title, uppercase, truncates |
|
|
529
|
+
| `mw-kanban-count` | Ticket counter pill |
|
|
530
|
+
| `mw-kanban-add` | Sits **on** `mw-btn` - only trims it to the header line height |
|
|
531
|
+
| `mw-kanban-column-body` | Ticket stack; fills the lane so the empty state stays centred |
|
|
532
|
+
| `mw-kanban-card` | Ticket. Needs `mw-card` next to it |
|
|
533
|
+
| `mw-kanban-card-title` | Ticket title |
|
|
534
|
+
| `mw-kanban-card-text` | Description, clamped to 4 lines (2 on a compact board) |
|
|
535
|
+
| `mw-kanban-card-footer` | Rule + key on the left, avatar and actions on the right |
|
|
536
|
+
| `mw-kanban-card-id` | Ticket key, monospaced so equal-length keys line up across cards |
|
|
537
|
+
| `mw-kanban-card-actions` | Right-hand group; resets the avatar margin |
|
|
538
|
+
| `mw-kanban-action` | 26px square icon button (32px below `md`), `mw-kanban-action-danger` turns the hover red |
|
|
539
|
+
| `mw-kanban-empty` | Placeholder; hides itself as soon as the lane holds a ticket or an open composer |
|
|
540
|
+
| `mw-kanban-card-in` | One-shot entry animation for a freshly created ticket |
|
|
541
|
+
|
|
542
|
+
- **Priority is the regular `mw-card-ribbon`** and brings its own colour through
|
|
543
|
+
`mw-card-addon-danger|warning|info`. It has to be a **direct child** of the
|
|
544
|
+
card - that is what reserves the space next to the title. No ribbon means no
|
|
545
|
+
priority; there is no separate priority class.
|
|
546
|
+
- Lanes are as tall as the fullest one, with `--mw-kanban-column-min-height` as
|
|
547
|
+
the floor (120px, 90px compact). Raise it per board so a board that starts out
|
|
548
|
+
empty still reads as a board.
|
|
549
|
+
- Board surface and lane border are `--mw-kanban-background` and
|
|
550
|
+
`--mw-kanban-lane-border` - set them on the board, or drop the surface with
|
|
551
|
+
`mw-kanban-plain`.
|
|
552
|
+
- Responsive: below `lg` the lanes reflow into two columns, below `sm` into one.
|
|
553
|
+
- The actions are ordinary buttons - which of them exist is your decision. Give
|
|
554
|
+
every one an `aria-label` naming the ticket; the icon alone has no accessible
|
|
555
|
+
name.
|
|
556
|
+
|
|
557
|
+
### Composer
|
|
558
|
+
|
|
559
|
+
The inline form for creating and editing a ticket. One per board, moved into the
|
|
560
|
+
lane it is needed in.
|
|
561
|
+
|
|
562
|
+
```html
|
|
563
|
+
<form class="mw-kanban-composer mw-active">
|
|
564
|
+
<input type="text" class="mw-input mw-input-sm mw-kanban-composer-title" />
|
|
565
|
+
<textarea class="mw-textarea mw-kanban-composer-text" rows="2"></textarea>
|
|
566
|
+
<div class="mw-kanban-composer-row">
|
|
567
|
+
<select class="mw-select mw-select-sm mw-kanban-composer-priority">
|
|
568
|
+
...
|
|
569
|
+
</select>
|
|
570
|
+
<select class="mw-select mw-select-sm mw-kanban-composer-assignee">
|
|
571
|
+
...
|
|
572
|
+
</select>
|
|
573
|
+
</div>
|
|
574
|
+
<div class="mw-kanban-composer-actions">
|
|
575
|
+
<button type="button" class="mw-btn mw-btn-outline mw-btn-sm">
|
|
576
|
+
Cancel
|
|
577
|
+
</button>
|
|
578
|
+
<button type="submit" class="mw-btn mw-btn-primary mw-btn-sm">Save</button>
|
|
579
|
+
</div>
|
|
580
|
+
</form>
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
- **`mw-kanban-composer` is `display: none` until it also carries `mw-active`.**
|
|
584
|
+
Rendering it conditionally is not enough - the class has to be there too, or
|
|
585
|
+
the form stays invisible.
|
|
586
|
+
- `mw-kanban-composer-row` puts priority and assignee side by side and stacks
|
|
587
|
+
them once the lane gets too narrow; `mw-kanban-composer-actions` is the button
|
|
588
|
+
row and gives its buttons the same 80px floor as a modal footer.
|
|
589
|
+
- The `-title`, `-text`, `-priority`, `-assignee` and `-cancel` classes carry no
|
|
590
|
+
styling of their own beyond a min-height on the textarea - they are the hooks
|
|
591
|
+
the shipped JS queries. In a SPA you bind the controls yourself and can drop
|
|
592
|
+
them.
|
|
593
|
+
- `mw-kanban-editing` hides the ticket the composer is currently replacing.
|
|
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
|
+
|
|
368
728
|
## Tags
|
|
369
729
|
|
|
370
730
|
Two forms, picked by count - see also the pitfall list in `SKILL.md`.
|
|
@@ -376,8 +736,14 @@ Two forms, picked by count - see also the pitfall list in `SKILL.md`.
|
|
|
376
736
|
```
|
|
377
737
|
|
|
378
738
|
Variants: `mw-tag-primary`, `-secondary`, `-success`, `-info`, `-warning`,
|
|
379
|
-
`-danger`, `-muted`. Size: `mw-tag-lg`.
|
|
380
|
-
|
|
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`.
|
|
381
747
|
|
|
382
748
|
**List** - the container carries the colour for all its items:
|
|
383
749
|
|
|
@@ -525,7 +891,7 @@ Both are chronicles (CV, changelog), not schedulable time axes.
|
|
|
525
891
|
|
|
526
892
|
```html
|
|
527
893
|
<div class="mw-timeline-big">
|
|
528
|
-
<div class="mw-timeline-big-step active">
|
|
894
|
+
<div class="mw-timeline-big-step mw-active">
|
|
529
895
|
<div class="mw-timeline-big-date-container">
|
|
530
896
|
<div class="mw-timeline-big-date-main">01/2023 - today</div>
|
|
531
897
|
<div class="mw-timeline-big-date-sub">(3 years)</div>
|
|
@@ -541,7 +907,7 @@ Both are chronicles (CV, changelog), not schedulable time axes.
|
|
|
541
907
|
</div>
|
|
542
908
|
|
|
543
909
|
<div class="mw-timeline-simple">
|
|
544
|
-
<div class="mw-timeline-simple-step active">
|
|
910
|
+
<div class="mw-timeline-simple-step mw-active">
|
|
545
911
|
<div class="mw-timeline-simple-date">2024-03-22</div>
|
|
546
912
|
<div class="mw-timeline-simple-content">
|
|
547
913
|
<div class="mw-card mw-card-simple">...</div>
|
|
@@ -658,23 +1024,46 @@ The last item takes `mw-breadcrumbs-current` on the `li` and
|
|
|
658
1024
|
|
|
659
1025
|
## Pagination
|
|
660
1026
|
|
|
661
|
-
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.
|
|
662
1030
|
|
|
663
1031
|
```html
|
|
664
1032
|
<div class="mw-pagination">
|
|
665
1033
|
<div class="mw-pagination-header">
|
|
666
|
-
<button
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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>
|
|
670
1050
|
</button>
|
|
671
1051
|
</div>
|
|
1052
|
+
|
|
672
1053
|
<div class="mw-pagination-content">...</div>
|
|
673
1054
|
</div>
|
|
674
1055
|
```
|
|
675
1056
|
|
|
676
|
-
`mw-pagination-
|
|
677
|
-
|
|
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.
|
|
678
1067
|
|
|
679
1068
|
## Divider
|
|
680
1069
|
|
|
@@ -722,12 +1111,12 @@ shipped JS, as is the track transform. In a SPA, render the dots and set
|
|
|
722
1111
|
<img class="mw-image-slider-base-image" src="base.jpg" alt="" />
|
|
723
1112
|
</div>
|
|
724
1113
|
<div class="mw-image-slider-overlay">
|
|
725
|
-
<div class="mw-image-slider-overlay-image active" data-index="1">
|
|
1114
|
+
<div class="mw-image-slider-overlay-image mw-active" data-index="1">
|
|
726
1115
|
<img src="a.jpg" alt="" />
|
|
727
1116
|
</div>
|
|
728
1117
|
</div>
|
|
729
1118
|
<div class="mw-image-slider-controls-grid-3">
|
|
730
|
-
<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>
|
|
731
1120
|
</div>
|
|
732
1121
|
</div>
|
|
733
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
|
|