maverick-wave 4.10.0 → 4.12.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 +114 -12
- package/.claude/skills/maverick-wave/examples/angular-services.md +8 -4
- package/.claude/skills/maverick-wave/examples/static-landing-page.md +18 -8
- package/.claude/skills/maverick-wave/references/components.md +168 -9
- package/.claude/skills/maverick-wave/references/forms.md +26 -0
- package/.claude/skills/maverick-wave/references/javascript.md +34 -27
- package/.claude/skills/maverick-wave/references/layout.md +38 -3
- package/.claude/skills/maverick-wave/references/theming.md +33 -7
- package/CHANGELOG.md +12 -0
- package/README.md +79 -4
- package/index.html +16 -0
- package/maverick-wave.min.css +3 -3
- package/maverick-wave.min.js +1 -1
- package/package.json +2 -2
- package/src/js/main.js +79 -1
- package/src/partials/accordions-container.html +41 -16
- package/src/partials/alerts-container.html +5 -0
- package/src/partials/avatars-container.html +5 -0
- package/src/partials/badges-container.html +114 -0
- package/src/partials/blog-posts-container.html +5 -0
- package/src/partials/breadcrumbs-container.html +1 -1
- package/src/partials/button-bar-container.html +5 -0
- package/src/partials/buttons-container.html +84 -0
- package/src/partials/calendar-container.html +1 -1
- package/src/partials/cards-container.html +5 -0
- package/src/partials/code-container.html +6 -0
- package/src/partials/colors-container.html +1 -1
- package/src/partials/coming-soon-container.html +4 -0
- package/src/partials/divider-container.html +4 -0
- package/src/partials/dropdown-container.html +119 -0
- package/src/partials/empty-state-container.html +5 -0
- package/src/partials/form-container.html +5 -0
- package/src/partials/form-elements-container.html +5 -0
- package/src/partials/form-field-container.html +5 -0
- package/src/partials/gallery-container.html +5 -0
- package/src/partials/get-started-container.html +16 -4
- package/src/partials/header-container.html +6 -2
- package/src/partials/header-utilities-container.html +8 -2
- package/src/partials/html-lists-container.html +5 -0
- package/src/partials/info-container.html +4 -0
- package/src/partials/input-group-container.html +5 -0
- package/src/partials/item-lists-container.html +5 -0
- package/src/partials/kanban-container.html +1 -1
- package/src/partials/kbd-container.html +63 -0
- package/src/partials/meta-info-container.html +5 -0
- package/src/partials/pagination-container.html +1 -1
- package/src/partials/panels-container.html +4 -0
- package/src/partials/progress-container.html +4 -0
- package/src/partials/ratings-container.html +1 -1
- package/src/partials/skeleton-container.html +5 -0
- package/src/partials/spinners-container.html +5 -1
- package/src/partials/stepper-container.html +4 -0
- package/src/partials/tables-container.html +7 -0
- package/src/partials/tabs-container.html +35 -13
- package/src/partials/tags-container.html +5 -0
- package/src/partials/tiles-container.html +1 -1
- package/src/partials/typography-container.html +5 -0
- package/src/partials/utilities-container.html +153 -0
- package/src/scss/abstracts/_functions.scss +18 -0
- package/src/scss/abstracts/_mixins.scss +176 -0
- package/src/scss/abstracts/_variables.scss +192 -4
- package/src/scss/base/_base.scss +103 -2
- package/src/scss/base/_reset.scss +38 -3
- package/src/scss/base/_typography.scss +47 -0
- package/src/scss/components/_accordions.scss +37 -7
- package/src/scss/components/_alerts.scss +3 -3
- package/src/scss/components/_avatars.scss +27 -6
- package/src/scss/components/_badge.scss +157 -0
- package/src/scss/components/_blog-post.scss +10 -7
- package/src/scss/components/_breadcrumbs.scss +11 -2
- package/src/scss/components/_button-bar.scss +9 -2
- package/src/scss/components/_buttons.scss +133 -17
- package/src/scss/components/_calendar.scss +13 -2
- package/src/scss/components/_cards.scss +12 -5
- package/src/scss/components/_code.scss +15 -0
- package/src/scss/components/_coming-soon.scss +5 -2
- package/src/scss/components/_content-slider.scss +1 -1
- package/src/scss/components/_dropdown.scss +219 -0
- package/src/scss/components/_gallery.scss +17 -8
- package/src/scss/components/_index.scss +3 -0
- package/src/scss/components/_info.scss +14 -8
- package/src/scss/components/_kanban.scss +40 -6
- package/src/scss/components/_kbd.scss +43 -0
- package/src/scss/components/_lists.scss +26 -23
- package/src/scss/components/_modals.scss +71 -6
- package/src/scss/components/_pagination.scss +4 -3
- package/src/scss/components/_panels.scss +13 -14
- package/src/scss/components/_progress.scss +1 -1
- package/src/scss/components/_segmented.scss +8 -4
- package/src/scss/components/_stepper.scss +3 -3
- package/src/scss/components/_tables.scss +5 -8
- package/src/scss/components/_tabs.scss +22 -3
- package/src/scss/components/_tags.scss +1 -2
- package/src/scss/components/_techstack-bucket.scss +8 -5
- package/src/scss/components/_theme-toggle.scss +16 -8
- package/src/scss/components/_tiles.scss +14 -4
- package/src/scss/components/_timelines.scss +9 -6
- package/src/scss/components/_toasts.scss +1 -1
- package/src/scss/form-elements/_checkbox.scss +2 -1
- package/src/scss/form-elements/_form.scss +2 -2
- package/src/scss/form-elements/_input-group.scss +30 -1
- package/src/scss/form-elements/_input.scss +10 -10
- package/src/scss/form-elements/_login.scss +1 -1
- package/src/scss/form-elements/_radio.scss +4 -3
- package/src/scss/form-elements/_select.scss +9 -10
- package/src/scss/form-elements/_slider.scss +11 -11
- package/src/scss/form-elements/_textarea.scss +5 -8
- package/src/scss/form-elements/_toggle.scss +6 -5
- package/src/scss/layout/_footer.scss +14 -7
- package/src/scss/layout/_header.scss +18 -17
- package/src/scss/layout/_home.scss +1 -1
- package/src/scss/layout/_main.scss +6 -6
- package/src/scss/layout/_section.scss +7 -4
- package/src/scss/utilities/_accessibility.scss +38 -0
- package/src/scss/utilities/_aspect.scss +27 -0
- package/src/scss/utilities/_display.scss +52 -0
- package/src/scss/utilities/_elevation.scss +16 -0
- package/src/scss/utilities/_index.scss +2 -0
- package/src/scss/utilities/_text.scss +63 -0
- package/src/scss/utilities/_touch-targets.scss +63 -0
|
@@ -155,12 +155,39 @@ 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
|
+
**Elevation** (`mw-elevation-0` … `-5`, and `var(--mw-elevation-N)` inside SCSS).
|
|
159
|
+
Two shadows per level - a tight contact layer plus a wide ambient one:
|
|
160
|
+
`1` resting (inputs, tags), `2` raised (cards, panels at rest), `3` floating
|
|
161
|
+
(a card under the pointer), `4` overlay (dropdown, popover, drawer), `5` modal.
|
|
162
|
+
`0` is explicitly flat. Never write a `box-shadow` by hand - the twelve one-off
|
|
163
|
+
values that used to exist are exactly what this replaced.
|
|
164
|
+
|
|
165
|
+
**Motion** `--mw-duration-instant|fast|base|slow|slower` = 110/180/300/520/900ms
|
|
166
|
+
plus `--mw-duration-zoom` (650ms, for a large surface actually travelling),
|
|
167
|
+
`--mw-ease-out` (things arriving - the default), `--mw-ease-in-out` (A to B and
|
|
168
|
+
back), `--mw-ease-spring` (a pop). Two ready-made transitions:
|
|
169
|
+
`var(--mw-transition)` for hover and focus states, `var(--mw-transition-fast)`
|
|
170
|
+
for anything that should feel instant under the pointer. Both list their
|
|
171
|
+
properties explicitly rather than saying `all`.
|
|
172
|
+
|
|
173
|
+
**Control sizes** `--mw-control-height-sm|base|lg` = 2 / 2.25 / 2.5rem (an even
|
|
174
|
+
32 / 36 / 40px step) and
|
|
175
|
+
`--mw-control-font-sm|base|lg` = 0.8 / 0.9 / 1rem, shared by `mw-input`,
|
|
176
|
+
`mw-select`, `mw-textarea` and `mw-btn`. A field and the button beside it are
|
|
177
|
+
the same height by construction. Buttons run one font step above the fields.
|
|
178
|
+
|
|
179
|
+
**Focus** `--mw-focus-ring-width` 2px, `--mw-focus-ring-offset` 2px,
|
|
180
|
+
`--mw-focus-ring-color`, plus `--mw-focus-halo-size` 3px /
|
|
181
|
+
`--mw-focus-halo-opacity` 28% for the soft ring a form field gets instead of a
|
|
182
|
+
hard outline. SCSS: `@include focus-ring`, `focus-ring-inset`, `field-focus`.
|
|
183
|
+
|
|
158
184
|
## Component index
|
|
159
185
|
|
|
160
186
|
Everything below is documented in `references/components.md` unless marked otherwise.
|
|
161
187
|
|
|
162
188
|
**Actions** `mw-btn` (+ `primary`, `secondary`, `danger`, `success`, `outline`,
|
|
163
|
-
`link`, `link-muted`, `plain`, `
|
|
189
|
+
`ghost`, `ghost-danger`, `link`, `link-muted`, `plain`, `icon`, `block`, `sm`,
|
|
190
|
+
`lg`) · `mw-btn-mini` · `mw-button-bar`
|
|
164
191
|
(+ `left`, `right`, `center`, `between`) · `mw-segmented` · `mw-actions-note`
|
|
165
192
|
|
|
166
193
|
**Containers** `mw-card` (+ `simple`, `lg`, `xl`, `stack`, badge, ribbon,
|
|
@@ -171,7 +198,8 @@ feature frame) ·
|
|
|
171
198
|
**Data & status** `mw-table` (+ `subtle`, `sticky-head`, `cards`, `compact`,
|
|
172
199
|
`hover`, responsive wrappers) · `mw-kanban` (+ `plain`, `compact`) ·
|
|
173
200
|
`mw-calendar` (+ `compact`, `plain`) · `mw-tag` /
|
|
174
|
-
`mw-tags` · `mw-
|
|
201
|
+
`mw-tags` · `mw-badge` (+ `-dot`, `-status`, `-anchor`, `-float`, `-pulse`) ·
|
|
202
|
+
`mw-info` / `mw-info-mini` / `mw-info-counter` · `mw-progress-bar` ·
|
|
175
203
|
`mw-rating` · `mw-meta-header` · `mw-stepper` · `mw-timeline-big` /
|
|
176
204
|
`mw-timeline-simple`
|
|
177
205
|
|
|
@@ -179,11 +207,13 @@ feature frame) ·
|
|
|
179
207
|
`mw-spinner-border` / `mw-spinner-dots` / `mw-spinner-dual-ring` · `mw-skeleton`
|
|
180
208
|
|
|
181
209
|
**Navigation** `mw-header` + `mw-navbar` · `mw-breadcrumbs` · `mw-pagination` ·
|
|
182
|
-
`mw-
|
|
210
|
+
`mw-dropdown` (+ `-menu`, `-item`, `-item-danger`, `-divider`, `-label`,
|
|
211
|
+
`-caret`, `-end`, `-up`) · `mw-section-nav` (`references/layout.md`)
|
|
183
212
|
|
|
184
213
|
**Media & content** `mw-avatar` (+ `initials`, `group`) · `mw-gallery` ·
|
|
185
214
|
`mw-image-slider` · `mw-blog-post` · `mw-code-block` / `mw-terminal` ·
|
|
186
|
-
`mw-techstack-bucket` · `mw-coming-soon` · `mw-divider` · `mw-
|
|
215
|
+
`mw-techstack-bucket` · `mw-coming-soon` · `mw-divider` · `mw-kbd` ·
|
|
216
|
+
`mw-list` family
|
|
187
217
|
|
|
188
218
|
**Forms** (`references/forms.md`) `mw-field` · `mw-input` · `mw-select` ·
|
|
189
219
|
`mw-textarea` · `mw-checkbox` · `mw-radio` · `mw-toggle` · `mw-slider` ·
|
|
@@ -194,9 +224,14 @@ feature frame) ·
|
|
|
194
224
|
`mw-section` · `mw-page-header` · `mw-grid-*` · `mw-row-split` · `mw-hero` ·
|
|
195
225
|
`mw-footer`
|
|
196
226
|
|
|
197
|
-
**Utilities** (`references/layout.md`) `mw-sr-only`
|
|
198
|
-
(+ `center`) · `mw-text-numeric` /
|
|
199
|
-
text
|
|
227
|
+
**Utilities** (`references/layout.md`) `mw-sr-only` / `mw-sr-only-focusable` /
|
|
228
|
+
`mw-skip-link` · `mw-row-split` (+ `center`) · `mw-text-numeric` /
|
|
229
|
+
`mw-text-currency` · `mw-text-truncate` / `mw-text-clamp-2..5` /
|
|
230
|
+
`mw-text-break` / `mw-text-nowrap` · `mw-text-balance` / `mw-text-pretty` /
|
|
231
|
+
`mw-text-eyebrow` / `mw-text-measure` · `mw-elevation-0..5` ·
|
|
232
|
+
`mw-aspect-square|video|wide|portrait|photo` · `mw-d-{sm,md,lg,xl}-*` /
|
|
233
|
+
`mw-hide-mobile` / `mw-hide-desktop` · `mw-overflow-*` / `mw-snap-x` ·
|
|
234
|
+
spacing, flex, display, text
|
|
200
235
|
|
|
201
236
|
## Pitfalls
|
|
202
237
|
|
|
@@ -238,11 +273,13 @@ text
|
|
|
238
273
|
Safari 16.4+, Firefox 128+. Both carry the derived tones, so older browsers
|
|
239
274
|
get no colours at all, not merely worse ones.
|
|
240
275
|
11. **Touch targets grow on their own.** On `pointer: coarse` or below 768px,
|
|
241
|
-
`mw-btn
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
276
|
+
`mw-btn` gets a 2.75rem minimum height, `mw-btn-sm`, `mw-input-sm`,
|
|
277
|
+
`mw-select-sm` and `mw-textarea-sm` 2.5rem, a tab 2.75rem, a calendar day
|
|
278
|
+
44px, and list rows / menu items / pager pages / accordion headers 2.75rem.
|
|
279
|
+
`mw-btn-mini` keeps its 18px circle - it sits in tag rows and table cells
|
|
280
|
+
where a bigger one would shift the layout - and grows its _hit area_ to 28px
|
|
281
|
+
via a pseudo-element. Nothing to switch on, and no reason to write the media
|
|
282
|
+
query again in an app.
|
|
246
283
|
12. **`mw-empty-state` has a `-desc`, not a `-text`.** The parts are
|
|
247
284
|
`mw-empty-state-icon`, `-title`, `-desc`, plus the size variant
|
|
248
285
|
`mw-empty-state-sm`. Invented names fail silently, as always.
|
|
@@ -259,3 +296,68 @@ text
|
|
|
259
296
|
that host - it then generates no box and the children take the item role
|
|
260
297
|
back. Angular: `host: { class: 'mw-d-contents' }`. `mw-header` handles this
|
|
261
298
|
case on its own, the others do not.
|
|
299
|
+
15. **A badge is not a tag.** `mw-tag` names something - a topic, a state - and
|
|
300
|
+
sits in a row of its own kind, with a tinted surface. `mw-badge` carries a
|
|
301
|
+
_count_ or a _status_ and usually sits **on** something, filled rather than
|
|
302
|
+
tinted. Number on a bell: badge. "Draft" next to a title: tag.
|
|
303
|
+
16. **A dropdown is a `<details>`, not a div.** `<details class="mw-dropdown">`
|
|
304
|
+
with a `<summary>` trigger - that is where the keyboard handling, the focus
|
|
305
|
+
and the open state come from, and it works without script. The framework JS
|
|
306
|
+
only adds Escape and click-outside. Writing your own div-plus-click loses all
|
|
307
|
+
of it.
|
|
308
|
+
17. **An open dropdown is clipped by anything that hides its overflow.** The menu
|
|
309
|
+
is absolutely positioned. The framework's own containers - panel, card, tile,
|
|
310
|
+
modal body, responsive table - lift the clip while a menu is open. On your
|
|
311
|
+
own container it is one line:
|
|
312
|
+
`:has(.mw-dropdown[open]) { overflow: visible }`.
|
|
313
|
+
18. **Never write `box-shadow` by hand.** Use `var(--mw-elevation-1..5)` or the
|
|
314
|
+
`mw-elevation-*` class. A hand-rolled shadow is the wrong colour in one of
|
|
315
|
+
the two themes - the dark theme's shadow is a light rim over a dark contact
|
|
316
|
+
layer, not a black blur.
|
|
317
|
+
19. **Never write a duration or an easing curve by hand** either. Use
|
|
318
|
+
`var(--mw-transition)` for a hover or focus state,
|
|
319
|
+
`var(--mw-transition-fast)` for something that should feel instant, and
|
|
320
|
+
`var(--mw-duration-*)` with `var(--mw-ease-*)` for anything else. That is
|
|
321
|
+
also what makes `prefers-reduced-motion` work - it turns the duration tokens
|
|
322
|
+
down, so anything built on them is covered for free.
|
|
323
|
+
20. **A hover effect that _moves_ something needs the `hover` mixin.** On touch
|
|
324
|
+
`:hover` latches after a tap and stays on, so a lifted card stays lifted,
|
|
325
|
+
visibly out of line with its row. `@include hover { transform: ... }` -
|
|
326
|
+
colour changes are fine unguarded, movement is not. The framework's own
|
|
327
|
+
components already do this.
|
|
328
|
+
21. **Below 576px a modal is a bottom sheet.** Full width, anchored to the bottom
|
|
329
|
+
edge, rounded on the top two corners, with a grab handle and full-width
|
|
330
|
+
actions in the footer. Nothing to switch on - do not fight it with your own
|
|
331
|
+
media query, and do not put a fixed height on `mw-modal`.
|
|
332
|
+
22. **Press states exist on every control**, because a finger never hovers.
|
|
333
|
+
`mw-btn` and friends dip 1px and invert their highlight, `mw-btn-mini` and
|
|
334
|
+
`mw-modal-close` scale down. If you build your own control, give it an
|
|
335
|
+
`:active` - on touch it is the only feedback there is.
|
|
336
|
+
23. **Never set a height on a form control.** `mw-input`, `mw-select`,
|
|
337
|
+
`mw-textarea` and `mw-btn` all take their minimum height from the control
|
|
338
|
+
scale, so a field and the button next to it line up on their own. Pick the
|
|
339
|
+
size step (`-sm` / nothing / `-lg`) and leave the height alone - a hand-set
|
|
340
|
+
one puts that control back out of line with everything around it.
|
|
341
|
+
24. **Put the size modifier on the input group, not inside it.**
|
|
342
|
+
`mw-input-group-sm` and `-lg` size the prefix, the suffix _and_ the field.
|
|
343
|
+
Adding `mw-input-sm` inside as well is redundant, and mixing the two steps
|
|
344
|
+
is what makes a group look broken.
|
|
345
|
+
25. **Loops keep their own timing, entrances go on the scale.** A spinner, a
|
|
346
|
+
skeleton shimmer and a pulse ring are ambient - running them at 300ms would
|
|
347
|
+
be frantic, and they are the one place a hand-written duration is right.
|
|
348
|
+
Anything that plays once - a panel appearing, a card sliding in, a drawer -
|
|
349
|
+
uses `var(--mw-duration-*)`.
|
|
350
|
+
26. **`transition: all` is out.** `var(--mw-transition)` lists paint-only
|
|
351
|
+
properties on purpose. The one exception in the framework is the header
|
|
352
|
+
burger, which morphs by animating `top` and `bottom`, and it says so in a
|
|
353
|
+
comment. If your component really does need to animate a size, name that
|
|
354
|
+
property - do not reach for `all`.
|
|
355
|
+
27. **A control is a `<button>`, never a styled `<div>`.** `mw-tabs-nav-item`,
|
|
356
|
+
`mw-theme-toggle`, `mw-gallery-dot` and `mw-accordion-header` are all
|
|
357
|
+
written for one, and all four shipped as divs and spans - the first three
|
|
358
|
+
until 4.11, the accordion header until 4.12 - that no keyboard could reach.
|
|
359
|
+
Each class clears what a `<button>` brings with it, so
|
|
360
|
+
`<button type="button" class="mw-tabs-nav-item" data-tab="...">` is the
|
|
361
|
+
whole markup. If you build your own clickable thing: the element decides
|
|
362
|
+
whether anyone without a mouse can use it, the class only decides how it
|
|
363
|
+
looks.
|
|
@@ -194,7 +194,7 @@ export class ConfirmDialogComponent {
|
|
|
194
194
|
|
|
195
195
|
## Accordion
|
|
196
196
|
|
|
197
|
-
`active` on the header **and** the content
|
|
197
|
+
`mw-active` on the header **and** the content.
|
|
198
198
|
|
|
199
199
|
```ts
|
|
200
200
|
@Component({
|
|
@@ -203,16 +203,20 @@ export class ConfirmDialogComponent {
|
|
|
203
203
|
<div class="mw-accordion">
|
|
204
204
|
@for (item of items(); track item.id; let i = $index) {
|
|
205
205
|
<div class="mw-accordion-item">
|
|
206
|
-
<
|
|
206
|
+
<button
|
|
207
|
+
type="button"
|
|
207
208
|
class="mw-accordion-header"
|
|
208
209
|
[class.mw-active]="openIndex() === i"
|
|
210
|
+
[attr.aria-expanded]="openIndex() === i"
|
|
211
|
+
[attr.aria-controls]="item.id"
|
|
209
212
|
(click)="toggle(i)"
|
|
210
213
|
>
|
|
211
|
-
<
|
|
214
|
+
<span>{{ item.question }}</span>
|
|
212
215
|
<i class="fas fa-chevron-down mw-accordion-icon"></i>
|
|
213
|
-
</
|
|
216
|
+
</button>
|
|
214
217
|
<div
|
|
215
218
|
class="mw-accordion-content"
|
|
219
|
+
[id]="item.id"
|
|
216
220
|
[class.mw-active]="openIndex() === i"
|
|
217
221
|
>
|
|
218
222
|
<div class="mw-accordion-content-inner">{{ item.answer }}</div>
|
|
@@ -234,11 +234,16 @@ wired up by hand.
|
|
|
234
234
|
|
|
235
235
|
<div class="mw-accordion">
|
|
236
236
|
<div class="mw-accordion-item">
|
|
237
|
-
<
|
|
238
|
-
|
|
237
|
+
<button
|
|
238
|
+
type="button"
|
|
239
|
+
class="mw-accordion-header mw-active"
|
|
240
|
+
aria-expanded="true"
|
|
241
|
+
aria-controls="faq-storage"
|
|
242
|
+
>
|
|
243
|
+
<span>Where is the data stored?</span>
|
|
239
244
|
<i class="fas fa-chevron-down mw-accordion-icon"></i>
|
|
240
|
-
</
|
|
241
|
-
<div class="mw-accordion-content mw-active">
|
|
245
|
+
</button>
|
|
246
|
+
<div class="mw-accordion-content mw-active" id="faq-storage">
|
|
242
247
|
<div class="mw-accordion-content-inner">
|
|
243
248
|
<p>In Frankfurt, in our own data centre.</p>
|
|
244
249
|
</div>
|
|
@@ -246,11 +251,16 @@ wired up by hand.
|
|
|
246
251
|
</div>
|
|
247
252
|
|
|
248
253
|
<div class="mw-accordion-item">
|
|
249
|
-
<
|
|
250
|
-
|
|
254
|
+
<button
|
|
255
|
+
type="button"
|
|
256
|
+
class="mw-accordion-header"
|
|
257
|
+
aria-expanded="false"
|
|
258
|
+
aria-controls="faq-cancel"
|
|
259
|
+
>
|
|
260
|
+
<span>Can I cancel monthly?</span>
|
|
251
261
|
<i class="fas fa-chevron-down mw-accordion-icon"></i>
|
|
252
|
-
</
|
|
253
|
-
<div class="mw-accordion-content">
|
|
262
|
+
</button>
|
|
263
|
+
<div class="mw-accordion-content" id="faq-cancel">
|
|
254
264
|
<div class="mw-accordion-content-inner">
|
|
255
265
|
<p>Yes, at the end of every billing period.</p>
|
|
256
266
|
</div>
|
|
@@ -15,15 +15,55 @@ exhaustive: what is not named does not exist.
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
- Variants: `mw-btn-primary`, `mw-btn-secondary`, `mw-btn-danger`,
|
|
18
|
-
`mw-btn-success`, `mw-btn-outline`, `mw-btn-
|
|
18
|
+
`mw-btn-success`, `mw-btn-outline`, `mw-btn-ghost`, `mw-btn-ghost-danger`,
|
|
19
|
+
`mw-btn-link`, `mw-btn-link-muted`
|
|
20
|
+
- Shapes: `mw-btn-icon` (square, for an icon-only button), `mw-btn-block`
|
|
21
|
+
(full width)
|
|
19
22
|
- Sizes: `mw-btn-sm`, `mw-btn-lg`
|
|
20
23
|
- `mw-btn` is `inline-flex` with a gap - icons need no wrapper and no extra class
|
|
21
24
|
- `disabled` gets `opacity: .6` and `not-allowed`; there is no disabled class
|
|
22
25
|
- `mw-active` gives the pressed/selected look on the coloured variants
|
|
26
|
+
- Every variant dips 1px on `:active`, and the solid ones invert their top
|
|
27
|
+
highlight into an inner shadow. On touch that press is the only feedback there
|
|
28
|
+
is, so do not override it away
|
|
23
29
|
- `mw-btn-link` still has the button padding - `mw-p-0` makes it read as inline
|
|
24
30
|
text
|
|
25
|
-
- On a coarse pointer or below 768px `mw-btn
|
|
26
|
-
|
|
31
|
+
- On a coarse pointer or below 768px `mw-btn` grows to a 2.75rem minimum height
|
|
32
|
+
and `mw-btn-sm` to 2.5rem, on their own
|
|
33
|
+
|
|
34
|
+
### Three emphasis levels
|
|
35
|
+
|
|
36
|
+
`mw-btn-primary` (or any solid colour) **fills**, `mw-btn-outline` **draws a
|
|
37
|
+
line**, `mw-btn-ghost` does **neither** until you point at it. Ghost is what a
|
|
38
|
+
toolbar, a card's own actions or a row of icon buttons wants - five outlined
|
|
39
|
+
boxes in a row fight the content they sit on.
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<button type="button" class="mw-btn mw-btn-ghost">Rename</button>
|
|
43
|
+
<button type="button" class="mw-btn mw-btn-ghost mw-btn-ghost-danger">
|
|
44
|
+
Remove
|
|
45
|
+
</button>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Ghost is not `mw-btn-plain`: it keeps the button's shape, padding and hit area
|
|
49
|
+
and only leaves them unpainted at rest. `mw-btn-plain` has no look at all.
|
|
50
|
+
|
|
51
|
+
### Icon-only buttons
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<button
|
|
55
|
+
type="button"
|
|
56
|
+
class="mw-btn mw-btn-ghost mw-btn-icon"
|
|
57
|
+
aria-label="Duplicate"
|
|
58
|
+
>
|
|
59
|
+
<i class="fas fa-copy"></i>
|
|
60
|
+
</button>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`mw-btn-icon` makes the button square and follows the size modifier next to it
|
|
64
|
+
(`mw-btn-sm` / `mw-btn-lg`), growing to 2.75rem on a coarse pointer. It always
|
|
65
|
+
needs an `aria-label` - there is no text to read out. Combine it with any
|
|
66
|
+
variant; ghost is usually the right one.
|
|
27
67
|
|
|
28
68
|
### `mw-btn-plain` - a button with no button in it
|
|
29
69
|
|
|
@@ -75,7 +115,7 @@ Row of equally treated buttons, centered by default.
|
|
|
75
115
|
- Alignment: `mw-button-bar-left`, `-right`, `-center` (which is also the
|
|
76
116
|
default), `mw-button-bar-between` pushes first and last apart.
|
|
77
117
|
- Sizes: `mw-button-bar-sm`, `mw-button-bar-lg`.
|
|
78
|
-
- Below 576px it stacks and gives every button
|
|
118
|
+
- Below 576px it stacks and gives every button the full width. Right for a row of
|
|
79
119
|
independent actions - wrong for a switch, see below.
|
|
80
120
|
|
|
81
121
|
### `mw-actions-note` - one line above a row of actions
|
|
@@ -277,21 +317,32 @@ Tile sizes: `mw-tile-sm`, `mw-tile-lg`.
|
|
|
277
317
|
```html
|
|
278
318
|
<div class="mw-accordion">
|
|
279
319
|
<div class="mw-accordion-item">
|
|
280
|
-
<
|
|
281
|
-
|
|
320
|
+
<button
|
|
321
|
+
type="button"
|
|
322
|
+
class="mw-accordion-header mw-active"
|
|
323
|
+
aria-expanded="true"
|
|
324
|
+
aria-controls="faq-1"
|
|
325
|
+
>
|
|
326
|
+
<span>Question</span>
|
|
282
327
|
<i class="fas fa-chevron-down mw-accordion-icon"></i>
|
|
283
|
-
</
|
|
284
|
-
<div class="mw-accordion-content mw-active">
|
|
328
|
+
</button>
|
|
329
|
+
<div class="mw-accordion-content mw-active" id="faq-1">
|
|
285
330
|
<div class="mw-accordion-content-inner">Answer</div>
|
|
286
331
|
</div>
|
|
287
332
|
</div>
|
|
288
333
|
</div>
|
|
289
334
|
```
|
|
290
335
|
|
|
336
|
+
The header is a `<button>` - it is the control that opens the panel, and a
|
|
337
|
+
`<div>` is not focusable. A heading is not allowed inside a button, so the
|
|
338
|
+
question is a `<span>` and the header carries the type itself; a nested `h3`
|
|
339
|
+
still renders the same, for markup written before 4.12.
|
|
340
|
+
|
|
291
341
|
Open state = `mw-active` on header **and** content (bare `active` still works
|
|
292
342
|
but is deprecated). The icon rotates via the **header** state - putting the
|
|
293
343
|
class on the icon instead does nothing. Content taller than 500px scrolls.
|
|
294
|
-
Toggling is JS - see `references/javascript.md
|
|
344
|
+
Toggling is JS - see `references/javascript.md`; the shipped script keeps
|
|
345
|
+
`aria-expanded` in step when the header is a button.
|
|
295
346
|
|
|
296
347
|
## Tabs
|
|
297
348
|
|
|
@@ -809,6 +860,114 @@ Container variants: `mw-tags-primary`, `-secondary`, `-success`, `-info`,
|
|
|
809
860
|
`-warning`, `-danger`, `-muted`, size `mw-tags-lg`. `mw-tags-remove` only
|
|
810
861
|
positions the button - its look comes from `mw-btn-mini` plus a colour variant.
|
|
811
862
|
|
|
863
|
+
## Badges
|
|
864
|
+
|
|
865
|
+
A count or a status, not a label. See the pitfall list in `SKILL.md` for the
|
|
866
|
+
badge-versus-tag rule: a tag names something and sits in a row of its own kind,
|
|
867
|
+
a badge carries a number or a state and usually sits _on_ something.
|
|
868
|
+
|
|
869
|
+
```html
|
|
870
|
+
<span class="mw-badge mw-badge-danger">99+</span>
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
Colours: `mw-badge-primary` (the default), `-secondary`, `-success`, `-warning`,
|
|
874
|
+
`-danger`, `-info`, `-muted`, plus `mw-badge-outline`. Sizes: `mw-badge-sm`,
|
|
875
|
+
`mw-badge-lg`.
|
|
876
|
+
|
|
877
|
+
Round on one character, a pill from two on - a `min-width` the padding grows
|
|
878
|
+
past. The digits are `tabular-nums`, so a counter ticking 9 to 10 does not shift
|
|
879
|
+
what sits beside it.
|
|
880
|
+
|
|
881
|
+
**On an icon or a button:**
|
|
882
|
+
|
|
883
|
+
```html
|
|
884
|
+
<span class="mw-badge-anchor">
|
|
885
|
+
<button class="mw-btn mw-btn-outline">
|
|
886
|
+
<i class="fas fa-inbox"></i> Inbox
|
|
887
|
+
</button>
|
|
888
|
+
<span class="mw-badge mw-badge-danger mw-badge-float">5</span>
|
|
889
|
+
</span>
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
`mw-badge-anchor` on the thing being badged, `mw-badge-float` on the badge. It
|
|
893
|
+
hangs half off the corner and carries a ring in the surface colour, so it reads
|
|
894
|
+
as a separate object rather than a blob on the icon. The ring follows the
|
|
895
|
+
surface automatically inside a card, panel, modal, tile or the header.
|
|
896
|
+
|
|
897
|
+
**Status:**
|
|
898
|
+
|
|
899
|
+
```html
|
|
900
|
+
<span class="mw-badge-status mw-badge-status-success">
|
|
901
|
+
<span class="mw-badge-dot"></span> Online
|
|
902
|
+
</span>
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
`mw-badge-status-{primary|secondary|success|warning|danger|info|muted}` colours
|
|
906
|
+
the dot inside it. A bare `mw-badge-dot-{colour}` works on its own too. Add
|
|
907
|
+
`mw-badge-pulse` for something actually running - a job, an open connection: a
|
|
908
|
+
ring grows out and fades while the dot underneath stays put, so a column of them
|
|
909
|
+
in a table stays readable. Not for decoration.
|
|
910
|
+
|
|
911
|
+
## Dropdown
|
|
912
|
+
|
|
913
|
+
Built on `<details>` / `<summary>`. That is where the keyboard handling, the
|
|
914
|
+
focus behaviour and the open state a screen reader can see come from, and it
|
|
915
|
+
works with JavaScript switched off. The framework script only adds Escape and
|
|
916
|
+
click-outside.
|
|
917
|
+
|
|
918
|
+
```html
|
|
919
|
+
<details class="mw-dropdown">
|
|
920
|
+
<summary class="mw-btn mw-btn-primary">
|
|
921
|
+
Actions <i class="fas fa-chevron-down mw-dropdown-caret"></i>
|
|
922
|
+
</summary>
|
|
923
|
+
<div class="mw-dropdown-menu">
|
|
924
|
+
<div class="mw-dropdown-label">Manage</div>
|
|
925
|
+
<button class="mw-dropdown-item"><i class="fas fa-pen"></i> Edit</button>
|
|
926
|
+
<button class="mw-dropdown-item mw-active">Duplicate</button>
|
|
927
|
+
<button class="mw-dropdown-item" disabled>Move</button>
|
|
928
|
+
<hr class="mw-dropdown-divider" />
|
|
929
|
+
<button class="mw-dropdown-item mw-dropdown-item-danger">
|
|
930
|
+
<i class="fas fa-trash"></i> Delete
|
|
931
|
+
</button>
|
|
932
|
+
</div>
|
|
933
|
+
</details>
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
Parts: `mw-dropdown-menu`, `-item`, `-item-danger`, `-divider`, `-label`,
|
|
937
|
+
`-caret` (rotates with the open state on its own). Alignment:
|
|
938
|
+
`mw-dropdown-end` anchors the menu to the trigger's right edge,
|
|
939
|
+
`mw-dropdown-up` opens it upward.
|
|
940
|
+
|
|
941
|
+
`mw-active` (or `aria-checked="true"`) marks the current choice, for a menu that
|
|
942
|
+
picks rather than acts. Icons inside items keep one column, so labels line up
|
|
943
|
+
whether or not every item has one.
|
|
944
|
+
|
|
945
|
+
The menu is absolutely positioned and is clipped by any ancestor that hides its
|
|
946
|
+
overflow. The framework's own containers lift that clip while a menu is open; on
|
|
947
|
+
your own it is `:has(.mw-dropdown[open]) { overflow: visible }`.
|
|
948
|
+
|
|
949
|
+
On a coarse pointer the rows grow to 2.75rem and the menu takes at least the
|
|
950
|
+
trigger's full width.
|
|
951
|
+
|
|
952
|
+
## Keyboard keys
|
|
953
|
+
|
|
954
|
+
`<kbd>` is styled directly, so a shortcut in prose needs no class:
|
|
955
|
+
|
|
956
|
+
```html
|
|
957
|
+
<p>
|
|
958
|
+
Press
|
|
959
|
+
<span class="mw-kbd-group"
|
|
960
|
+
><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd></span
|
|
961
|
+
>
|
|
962
|
+
to open the palette, <kbd>Esc</kbd> to close.
|
|
963
|
+
</p>
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
`mw-kbd` is the same look for a `<span>` when the markup is not yours to change.
|
|
967
|
+
`mw-kbd-group` wraps a combination so the pluses are spaced and the whole thing
|
|
968
|
+
cannot wrap apart. `mw-kbd-pressed` shows a key mid-press - the raised bottom
|
|
969
|
+
edge drops and the key sits 1px lower.
|
|
970
|
+
|
|
812
971
|
## Info badges & counters
|
|
813
972
|
|
|
814
973
|
```html
|
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Forms
|
|
2
2
|
|
|
3
|
+
## Control sizes
|
|
4
|
+
|
|
5
|
+
`mw-input`, `mw-select`, `mw-textarea` and `mw-btn` share one scale:
|
|
6
|
+
|
|
7
|
+
| step | height | font |
|
|
8
|
+
| ----- | ------- | ------ |
|
|
9
|
+
| `-sm` | 2rem | 0.8rem |
|
|
10
|
+
| base | 2.25rem | 0.9rem |
|
|
11
|
+
| `-lg` | 2.5rem | 1rem |
|
|
12
|
+
|
|
13
|
+
A field and the button beside it are therefore the same height by construction -
|
|
14
|
+
before 4.11.0 each control worked its own height out and an input, a select and
|
|
15
|
+
a button in one row measured 32.2, 34.4 and 37.2 pixels. Buttons sit one font
|
|
16
|
+
step above the fields: a button carries a label, a field carries what the user
|
|
17
|
+
typed.
|
|
18
|
+
|
|
19
|
+
Never set a height on a control yourself. Pick the step and leave it alone.
|
|
20
|
+
|
|
21
|
+
The minimum only governs while it is the larger of the two numbers. Block
|
|
22
|
+
padding above it wins, and the control silently leaves the scale - which is
|
|
23
|
+
exactly how a button once ended up 1.2px taller than the field beside it. So if
|
|
24
|
+
you do override `padding-block` on a control, keep
|
|
25
|
+
`2 x padding + line-height + border` under the height its step allows.
|
|
26
|
+
On a coarse pointer or below 768px every control grows to 2.75rem on its own.
|
|
27
|
+
Retune the whole scale through `--mw-control-height*` / `--mw-control-font*`.
|
|
28
|
+
|
|
3
29
|
## The field pattern
|
|
4
30
|
|
|
5
31
|
`mw-field` groups label, control, hint and error into one unit. It is the
|
|
@@ -25,25 +25,26 @@ classes are the entire contract.
|
|
|
25
25
|
|
|
26
26
|
## Behaviour inventory
|
|
27
27
|
|
|
28
|
-
| Behaviour | What the shipped JS does
|
|
29
|
-
| ------------------- |
|
|
30
|
-
| Accordion | Toggles `mw-active` on `mw-accordion-header` and the following `mw-accordion-content`
|
|
31
|
-
| Tabs | `data-tab` → panel `id`; sets `mw-active` on nav item and panel
|
|
32
|
-
| Modal | Click on `mw-modal-close` removes `mw-modal-open` from the overlay
|
|
33
|
-
| Mobile nav | Toggles `open` on `mw-menu-btn` and `mw-navbar`, closes on anchor click
|
|
34
|
-
| Scroll spy | Sets `mw-active` on `mw-navbar-link` from the scroll position
|
|
35
|
-
| Theme toggle | `localStorage['mw-theme']`, toggles `mw-theme-light` on `<body>` and `mw-active` on the toggle
|
|
36
|
-
| Progress bar | `IntersectionObserver` sets `width` from `data-value`
|
|
37
|
-
| Slider | On `input`, sets `--value` (track fill) and `data-value` (badge text)
|
|
38
|
-
| Alerts | Close button adds `mw-alert-closed` (`display: none`)
|
|
39
|
-
| Checkbox lists | Adds `mw-selected` to the `li`, emits a `checkboxToggle` event, exposes `window.toggleCheckbox`
|
|
40
|
-
| Gallery | Generates the dots, moves the track, swipe handling, writes `mw-gallery-desc`
|
|
41
|
-
| Image slider | Toggles `mw-active` on the overlay image and the control button with the matching `data-index`
|
|
42
|
-
| Kanban board | Counts the tickets per lane, moves a card between lanes (`data-kanban-move`)
|
|
43
|
-
| Calendar | Renders the month or week grid from `data-calendar="month
|
|
44
|
-
| Localhost indicator | On a local hostname, prepends `mw-localhost-indicator-pulse` to the header when it carries `mw-localhost-indicator-activated`
|
|
45
|
-
| Header login button | Swaps the FontAwesome lock icon
|
|
46
|
-
| Color swatches | Showcase-only (prints computed hex values)
|
|
28
|
+
| Behaviour | What the shipped JS does | What to do instead |
|
|
29
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
30
|
+
| Accordion | Toggles `mw-active` on `mw-accordion-header` and the following `mw-accordion-content`, and writes `aria-expanded` when the header is a `<button>` | `[class.mw-active]="isOpen()"` and `[attr.aria-expanded]="isOpen()"` on the header, `mw-active` on the panel |
|
|
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
|
+
| 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
|
+
| 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 `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
|
+
| Progress bar | `IntersectionObserver` sets `width` from `data-value` | Bind `[style.width.%]="value()"` on `mw-progress-fill` |
|
|
37
|
+
| Slider | On `input`, sets `--value` (track fill) and `data-value` (badge text) | Bind `[style.--value.%]` and `[attr.data-value]` |
|
|
38
|
+
| Alerts | Close button adds `mw-alert-closed` (`display: none`) | Remove the alert from the list/signal |
|
|
39
|
+
| Checkbox lists | Adds `mw-selected` to the `li`, emits a `checkboxToggle` event, exposes `window.toggleCheckbox` | `[class.mw-selected]="item.checked"` |
|
|
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 `mw-active` on the overlay image and the control button with the matching `data-index` | Bind `mw-active` from the selected index |
|
|
42
|
+
| Kanban board | Counts the tickets per lane, moves a card between lanes (`data-kanban-move`) and marks the arrival with `mw-kanban-card-moved-forward` / `-back`, clones `mw-kanban-card-template` on save, derives the next key from `data-kanban-prefix`, toggles `mw-active` on the composer | Keep the tickets in a signal/store and render the lanes from it; `mw-active` on the composer, `mw-kanban-editing` on the ticket it replaces. Neither the `<template>` nor the `mw-kanban-composer-*` hook classes are needed |
|
|
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 |
|
|
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 |
|
|
45
|
+
| Header login button | Swaps the FontAwesome lock icon | Bind the icon class |
|
|
46
|
+
| Color swatches | Showcase-only (prints computed hex values) | Not needed |
|
|
47
|
+
| Dropdown | Delegated to the document: closes the open `mw-dropdown` on Escape, on a click elsewhere and on a click on a `mw-dropdown-item`, and returns focus to the `summary` | The `<details>` does the opening, the keyboard and the state on its own. Rebuild only the two behaviours markup cannot express - or bind `[attr.open]` and keep them in the component |
|
|
47
48
|
|
|
48
49
|
## A note on the state class
|
|
49
50
|
|
|
@@ -56,14 +57,20 @@ other spelling exists.
|
|
|
56
57
|
|
|
57
58
|
## What works without any JavaScript
|
|
58
59
|
|
|
59
|
-
Pure CSS, nothing to wire up: hover and
|
|
60
|
-
(`data-tooltip`), `mw-rating` (via `data-rating`), the responsive table
|
|
61
|
-
view (`data-label`), all grids and utilities, the body scroll lock while a
|
|
62
|
-
is open (`body:has(.mw-modal-open)`),
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
handling.
|
|
60
|
+
Pure CSS, nothing to wire up: hover, focus and press states, the card lift,
|
|
61
|
+
tooltips (`data-tooltip`), `mw-rating` (via `data-rating`), the responsive table
|
|
62
|
+
card view (`data-label`), all grids and utilities, the body scroll lock while a
|
|
63
|
+
modal is open (`body:has(.mw-modal-open)`), the modal turning into a bottom
|
|
64
|
+
sheet below 576px, toast entry animations, the sticky table header, the scroll
|
|
65
|
+
hint on a tab bar (four gradients, no scroll listener), the kanban empty-lane
|
|
66
|
+
placeholder (hidden via `:has()` as soon as the lane holds a ticket), touch
|
|
67
|
+
target sizing on a coarse pointer, `prefers-reduced-motion` handling.
|
|
68
|
+
|
|
69
|
+
`mw-dropdown` is _almost_ in this list: opening, closing, the keyboard and the
|
|
70
|
+
open state are all the `<details>` element, so it works with no script at all.
|
|
71
|
+
The only two things the shipped JS adds are closing on Escape and closing on a
|
|
72
|
+
click somewhere else - worth rebuilding in a SPA, but a menu without them is
|
|
73
|
+
still a working menu, not a broken one.
|
|
67
74
|
|
|
68
75
|
## When you do keep the shipped JS
|
|
69
76
|
|