maverick-wave 4.9.4 → 4.11.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 +112 -12
- package/.claude/skills/maverick-wave/references/components.md +152 -4
- 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 +6 -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 +71 -1
- package/src/partials/accordions-container.html +5 -0
- 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/_mixins.scss +176 -0
- package/src/scss/abstracts/_variables.scss +193 -5
- package/src/scss/base/_base.scss +103 -2
- package/src/scss/base/_reset.scss +43 -1
- package/src/scss/base/_typography.scss +47 -0
- package/src/scss/components/_accordions.scss +11 -1
- 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,38 @@ 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` = 90/150/240/400/700ms,
|
|
166
|
+
`--mw-ease-out` (things arriving - the default), `--mw-ease-in-out` (A to B and
|
|
167
|
+
back), `--mw-ease-spring` (a pop). Two ready-made transitions:
|
|
168
|
+
`var(--mw-transition)` for hover and focus states, `var(--mw-transition-fast)`
|
|
169
|
+
for anything that should feel instant under the pointer. Both list their
|
|
170
|
+
properties explicitly rather than saying `all`.
|
|
171
|
+
|
|
172
|
+
**Control sizes** `--mw-control-height-sm|base|lg` = 2 / 2.25 / 2.5rem (an even
|
|
173
|
+
32 / 36 / 40px step) and
|
|
174
|
+
`--mw-control-font-sm|base|lg` = 0.8 / 0.9 / 1rem, shared by `mw-input`,
|
|
175
|
+
`mw-select`, `mw-textarea` and `mw-btn`. A field and the button beside it are
|
|
176
|
+
the same height by construction. Buttons run one font step above the fields.
|
|
177
|
+
|
|
178
|
+
**Focus** `--mw-focus-ring-width` 2px, `--mw-focus-ring-offset` 2px,
|
|
179
|
+
`--mw-focus-ring-color`, plus `--mw-focus-halo-size` 3px /
|
|
180
|
+
`--mw-focus-halo-opacity` 28% for the soft ring a form field gets instead of a
|
|
181
|
+
hard outline. SCSS: `@include focus-ring`, `focus-ring-inset`, `field-focus`.
|
|
182
|
+
|
|
158
183
|
## Component index
|
|
159
184
|
|
|
160
185
|
Everything below is documented in `references/components.md` unless marked otherwise.
|
|
161
186
|
|
|
162
187
|
**Actions** `mw-btn` (+ `primary`, `secondary`, `danger`, `success`, `outline`,
|
|
163
|
-
`link`, `link-muted`, `plain`, `
|
|
188
|
+
`ghost`, `ghost-danger`, `link`, `link-muted`, `plain`, `icon`, `block`, `sm`,
|
|
189
|
+
`lg`) · `mw-btn-mini` · `mw-button-bar`
|
|
164
190
|
(+ `left`, `right`, `center`, `between`) · `mw-segmented` · `mw-actions-note`
|
|
165
191
|
|
|
166
192
|
**Containers** `mw-card` (+ `simple`, `lg`, `xl`, `stack`, badge, ribbon,
|
|
@@ -171,7 +197,8 @@ feature frame) ·
|
|
|
171
197
|
**Data & status** `mw-table` (+ `subtle`, `sticky-head`, `cards`, `compact`,
|
|
172
198
|
`hover`, responsive wrappers) · `mw-kanban` (+ `plain`, `compact`) ·
|
|
173
199
|
`mw-calendar` (+ `compact`, `plain`) · `mw-tag` /
|
|
174
|
-
`mw-tags` · `mw-
|
|
200
|
+
`mw-tags` · `mw-badge` (+ `-dot`, `-status`, `-anchor`, `-float`, `-pulse`) ·
|
|
201
|
+
`mw-info` / `mw-info-mini` / `mw-info-counter` · `mw-progress-bar` ·
|
|
175
202
|
`mw-rating` · `mw-meta-header` · `mw-stepper` · `mw-timeline-big` /
|
|
176
203
|
`mw-timeline-simple`
|
|
177
204
|
|
|
@@ -179,11 +206,13 @@ feature frame) ·
|
|
|
179
206
|
`mw-spinner-border` / `mw-spinner-dots` / `mw-spinner-dual-ring` · `mw-skeleton`
|
|
180
207
|
|
|
181
208
|
**Navigation** `mw-header` + `mw-navbar` · `mw-breadcrumbs` · `mw-pagination` ·
|
|
182
|
-
`mw-
|
|
209
|
+
`mw-dropdown` (+ `-menu`, `-item`, `-item-danger`, `-divider`, `-label`,
|
|
210
|
+
`-caret`, `-end`, `-up`) · `mw-section-nav` (`references/layout.md`)
|
|
183
211
|
|
|
184
212
|
**Media & content** `mw-avatar` (+ `initials`, `group`) · `mw-gallery` ·
|
|
185
213
|
`mw-image-slider` · `mw-blog-post` · `mw-code-block` / `mw-terminal` ·
|
|
186
|
-
`mw-techstack-bucket` · `mw-coming-soon` · `mw-divider` · `mw-
|
|
214
|
+
`mw-techstack-bucket` · `mw-coming-soon` · `mw-divider` · `mw-kbd` ·
|
|
215
|
+
`mw-list` family
|
|
187
216
|
|
|
188
217
|
**Forms** (`references/forms.md`) `mw-field` · `mw-input` · `mw-select` ·
|
|
189
218
|
`mw-textarea` · `mw-checkbox` · `mw-radio` · `mw-toggle` · `mw-slider` ·
|
|
@@ -194,9 +223,14 @@ feature frame) ·
|
|
|
194
223
|
`mw-section` · `mw-page-header` · `mw-grid-*` · `mw-row-split` · `mw-hero` ·
|
|
195
224
|
`mw-footer`
|
|
196
225
|
|
|
197
|
-
**Utilities** (`references/layout.md`) `mw-sr-only`
|
|
198
|
-
(+ `center`) · `mw-text-numeric` /
|
|
199
|
-
text
|
|
226
|
+
**Utilities** (`references/layout.md`) `mw-sr-only` / `mw-sr-only-focusable` /
|
|
227
|
+
`mw-skip-link` · `mw-row-split` (+ `center`) · `mw-text-numeric` /
|
|
228
|
+
`mw-text-currency` · `mw-text-truncate` / `mw-text-clamp-2..5` /
|
|
229
|
+
`mw-text-break` / `mw-text-nowrap` · `mw-text-balance` / `mw-text-pretty` /
|
|
230
|
+
`mw-text-eyebrow` / `mw-text-measure` · `mw-elevation-0..5` ·
|
|
231
|
+
`mw-aspect-square|video|wide|portrait|photo` · `mw-d-{sm,md,lg,xl}-*` /
|
|
232
|
+
`mw-hide-mobile` / `mw-hide-desktop` · `mw-overflow-*` / `mw-snap-x` ·
|
|
233
|
+
spacing, flex, display, text
|
|
200
234
|
|
|
201
235
|
## Pitfalls
|
|
202
236
|
|
|
@@ -238,11 +272,13 @@ text
|
|
|
238
272
|
Safari 16.4+, Firefox 128+. Both carry the derived tones, so older browsers
|
|
239
273
|
get no colours at all, not merely worse ones.
|
|
240
274
|
11. **Touch targets grow on their own.** On `pointer: coarse` or below 768px,
|
|
241
|
-
`mw-btn
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
275
|
+
`mw-btn` gets a 2.75rem minimum height, `mw-btn-sm`, `mw-input-sm`,
|
|
276
|
+
`mw-select-sm` and `mw-textarea-sm` 2.5rem, a tab 2.75rem, a calendar day
|
|
277
|
+
44px, and list rows / menu items / pager pages / accordion headers 2.75rem.
|
|
278
|
+
`mw-btn-mini` keeps its 18px circle - it sits in tag rows and table cells
|
|
279
|
+
where a bigger one would shift the layout - and grows its _hit area_ to 28px
|
|
280
|
+
via a pseudo-element. Nothing to switch on, and no reason to write the media
|
|
281
|
+
query again in an app.
|
|
246
282
|
12. **`mw-empty-state` has a `-desc`, not a `-text`.** The parts are
|
|
247
283
|
`mw-empty-state-icon`, `-title`, `-desc`, plus the size variant
|
|
248
284
|
`mw-empty-state-sm`. Invented names fail silently, as always.
|
|
@@ -259,3 +295,67 @@ text
|
|
|
259
295
|
that host - it then generates no box and the children take the item role
|
|
260
296
|
back. Angular: `host: { class: 'mw-d-contents' }`. `mw-header` handles this
|
|
261
297
|
case on its own, the others do not.
|
|
298
|
+
15. **A badge is not a tag.** `mw-tag` names something - a topic, a state - and
|
|
299
|
+
sits in a row of its own kind, with a tinted surface. `mw-badge` carries a
|
|
300
|
+
_count_ or a _status_ and usually sits **on** something, filled rather than
|
|
301
|
+
tinted. Number on a bell: badge. "Draft" next to a title: tag.
|
|
302
|
+
16. **A dropdown is a `<details>`, not a div.** `<details class="mw-dropdown">`
|
|
303
|
+
with a `<summary>` trigger - that is where the keyboard handling, the focus
|
|
304
|
+
and the open state come from, and it works without script. The framework JS
|
|
305
|
+
only adds Escape and click-outside. Writing your own div-plus-click loses all
|
|
306
|
+
of it.
|
|
307
|
+
17. **An open dropdown is clipped by anything that hides its overflow.** The menu
|
|
308
|
+
is absolutely positioned. The framework's own containers - panel, card, tile,
|
|
309
|
+
modal body, responsive table - lift the clip while a menu is open. On your
|
|
310
|
+
own container it is one line:
|
|
311
|
+
`:has(.mw-dropdown[open]) { overflow: visible }`.
|
|
312
|
+
18. **Never write `box-shadow` by hand.** Use `var(--mw-elevation-1..5)` or the
|
|
313
|
+
`mw-elevation-*` class. A hand-rolled shadow is the wrong colour in one of
|
|
314
|
+
the two themes - the dark theme's shadow is a light rim over a dark contact
|
|
315
|
+
layer, not a black blur.
|
|
316
|
+
19. **Never write a duration or an easing curve by hand** either. Use
|
|
317
|
+
`var(--mw-transition)` for a hover or focus state,
|
|
318
|
+
`var(--mw-transition-fast)` for something that should feel instant, and
|
|
319
|
+
`var(--mw-duration-*)` with `var(--mw-ease-*)` for anything else. That is
|
|
320
|
+
also what makes `prefers-reduced-motion` work - it turns the duration tokens
|
|
321
|
+
down, so anything built on them is covered for free.
|
|
322
|
+
20. **A hover effect that _moves_ something needs the `hover` mixin.** On touch
|
|
323
|
+
`:hover` latches after a tap and stays on, so a lifted card stays lifted,
|
|
324
|
+
visibly out of line with its row. `@include hover { transform: ... }` -
|
|
325
|
+
colour changes are fine unguarded, movement is not. The framework's own
|
|
326
|
+
components already do this.
|
|
327
|
+
21. **Below 576px a modal is a bottom sheet.** Full width, anchored to the bottom
|
|
328
|
+
edge, rounded on the top two corners, with a grab handle and full-width
|
|
329
|
+
actions in the footer. Nothing to switch on - do not fight it with your own
|
|
330
|
+
media query, and do not put a fixed height on `mw-modal`.
|
|
331
|
+
22. **Press states exist on every control**, because a finger never hovers.
|
|
332
|
+
`mw-btn` and friends dip 1px and invert their highlight, `mw-btn-mini` and
|
|
333
|
+
`mw-modal-close` scale down. If you build your own control, give it an
|
|
334
|
+
`:active` - on touch it is the only feedback there is.
|
|
335
|
+
23. **Never set a height on a form control.** `mw-input`, `mw-select`,
|
|
336
|
+
`mw-textarea` and `mw-btn` all take their minimum height from the control
|
|
337
|
+
scale, so a field and the button next to it line up on their own. Pick the
|
|
338
|
+
size step (`-sm` / nothing / `-lg`) and leave the height alone - a hand-set
|
|
339
|
+
one puts that control back out of line with everything around it.
|
|
340
|
+
24. **Put the size modifier on the input group, not inside it.**
|
|
341
|
+
`mw-input-group-sm` and `-lg` size the prefix, the suffix _and_ the field.
|
|
342
|
+
Adding `mw-input-sm` inside as well is redundant, and mixing the two steps
|
|
343
|
+
is what makes a group look broken.
|
|
344
|
+
25. **Loops keep their own timing, entrances go on the scale.** A spinner, a
|
|
345
|
+
skeleton shimmer and a pulse ring are ambient - running them at 300ms would
|
|
346
|
+
be frantic, and they are the one place a hand-written duration is right.
|
|
347
|
+
Anything that plays once - a panel appearing, a card sliding in, a drawer -
|
|
348
|
+
uses `var(--mw-duration-*)`.
|
|
349
|
+
26. **`transition: all` is out.** `var(--mw-transition)` lists paint-only
|
|
350
|
+
properties on purpose. The one exception in the framework is the header
|
|
351
|
+
burger, which morphs by animating `top` and `bottom`, and it says so in a
|
|
352
|
+
comment. If your component really does need to animate a size, name that
|
|
353
|
+
property - do not reach for `all`.
|
|
354
|
+
27. **A control is a `<button>`, never a styled `<div>`.** `mw-tabs-nav-item`,
|
|
355
|
+
`mw-theme-toggle` and `mw-gallery-dot` are all written for one, and all
|
|
356
|
+
three shipped as divs and spans before 4.11 that no keyboard could reach.
|
|
357
|
+
Each class clears what a `<button>` brings with it, so
|
|
358
|
+
`<button type="button" class="mw-tabs-nav-item" data-tab="...">` is the
|
|
359
|
+
whole markup. If you build your own clickable thing: the element decides
|
|
360
|
+
whether anyone without a mouse can use it, the class only decides how it
|
|
361
|
+
looks.
|
|
@@ -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
|
|
@@ -809,6 +849,114 @@ Container variants: `mw-tags-primary`, `-secondary`, `-success`, `-info`,
|
|
|
809
849
|
`-warning`, `-danger`, `-muted`, size `mw-tags-lg`. `mw-tags-remove` only
|
|
810
850
|
positions the button - its look comes from `mw-btn-mini` plus a colour variant.
|
|
811
851
|
|
|
852
|
+
## Badges
|
|
853
|
+
|
|
854
|
+
A count or a status, not a label. See the pitfall list in `SKILL.md` for the
|
|
855
|
+
badge-versus-tag rule: a tag names something and sits in a row of its own kind,
|
|
856
|
+
a badge carries a number or a state and usually sits _on_ something.
|
|
857
|
+
|
|
858
|
+
```html
|
|
859
|
+
<span class="mw-badge mw-badge-danger">99+</span>
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
Colours: `mw-badge-primary` (the default), `-secondary`, `-success`, `-warning`,
|
|
863
|
+
`-danger`, `-info`, `-muted`, plus `mw-badge-outline`. Sizes: `mw-badge-sm`,
|
|
864
|
+
`mw-badge-lg`.
|
|
865
|
+
|
|
866
|
+
Round on one character, a pill from two on - a `min-width` the padding grows
|
|
867
|
+
past. The digits are `tabular-nums`, so a counter ticking 9 to 10 does not shift
|
|
868
|
+
what sits beside it.
|
|
869
|
+
|
|
870
|
+
**On an icon or a button:**
|
|
871
|
+
|
|
872
|
+
```html
|
|
873
|
+
<span class="mw-badge-anchor">
|
|
874
|
+
<button class="mw-btn mw-btn-outline">
|
|
875
|
+
<i class="fas fa-inbox"></i> Inbox
|
|
876
|
+
</button>
|
|
877
|
+
<span class="mw-badge mw-badge-danger mw-badge-float">5</span>
|
|
878
|
+
</span>
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
`mw-badge-anchor` on the thing being badged, `mw-badge-float` on the badge. It
|
|
882
|
+
hangs half off the corner and carries a ring in the surface colour, so it reads
|
|
883
|
+
as a separate object rather than a blob on the icon. The ring follows the
|
|
884
|
+
surface automatically inside a card, panel, modal, tile or the header.
|
|
885
|
+
|
|
886
|
+
**Status:**
|
|
887
|
+
|
|
888
|
+
```html
|
|
889
|
+
<span class="mw-badge-status mw-badge-status-success">
|
|
890
|
+
<span class="mw-badge-dot"></span> Online
|
|
891
|
+
</span>
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
`mw-badge-status-{primary|secondary|success|warning|danger|info|muted}` colours
|
|
895
|
+
the dot inside it. A bare `mw-badge-dot-{colour}` works on its own too. Add
|
|
896
|
+
`mw-badge-pulse` for something actually running - a job, an open connection: a
|
|
897
|
+
ring grows out and fades while the dot underneath stays put, so a column of them
|
|
898
|
+
in a table stays readable. Not for decoration.
|
|
899
|
+
|
|
900
|
+
## Dropdown
|
|
901
|
+
|
|
902
|
+
Built on `<details>` / `<summary>`. That is where the keyboard handling, the
|
|
903
|
+
focus behaviour and the open state a screen reader can see come from, and it
|
|
904
|
+
works with JavaScript switched off. The framework script only adds Escape and
|
|
905
|
+
click-outside.
|
|
906
|
+
|
|
907
|
+
```html
|
|
908
|
+
<details class="mw-dropdown">
|
|
909
|
+
<summary class="mw-btn mw-btn-primary">
|
|
910
|
+
Actions <i class="fas fa-chevron-down mw-dropdown-caret"></i>
|
|
911
|
+
</summary>
|
|
912
|
+
<div class="mw-dropdown-menu">
|
|
913
|
+
<div class="mw-dropdown-label">Manage</div>
|
|
914
|
+
<button class="mw-dropdown-item"><i class="fas fa-pen"></i> Edit</button>
|
|
915
|
+
<button class="mw-dropdown-item mw-active">Duplicate</button>
|
|
916
|
+
<button class="mw-dropdown-item" disabled>Move</button>
|
|
917
|
+
<hr class="mw-dropdown-divider" />
|
|
918
|
+
<button class="mw-dropdown-item mw-dropdown-item-danger">
|
|
919
|
+
<i class="fas fa-trash"></i> Delete
|
|
920
|
+
</button>
|
|
921
|
+
</div>
|
|
922
|
+
</details>
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
Parts: `mw-dropdown-menu`, `-item`, `-item-danger`, `-divider`, `-label`,
|
|
926
|
+
`-caret` (rotates with the open state on its own). Alignment:
|
|
927
|
+
`mw-dropdown-end` anchors the menu to the trigger's right edge,
|
|
928
|
+
`mw-dropdown-up` opens it upward.
|
|
929
|
+
|
|
930
|
+
`mw-active` (or `aria-checked="true"`) marks the current choice, for a menu that
|
|
931
|
+
picks rather than acts. Icons inside items keep one column, so labels line up
|
|
932
|
+
whether or not every item has one.
|
|
933
|
+
|
|
934
|
+
The menu is absolutely positioned and is clipped by any ancestor that hides its
|
|
935
|
+
overflow. The framework's own containers lift that clip while a menu is open; on
|
|
936
|
+
your own it is `:has(.mw-dropdown[open]) { overflow: visible }`.
|
|
937
|
+
|
|
938
|
+
On a coarse pointer the rows grow to 2.75rem and the menu takes at least the
|
|
939
|
+
trigger's full width.
|
|
940
|
+
|
|
941
|
+
## Keyboard keys
|
|
942
|
+
|
|
943
|
+
`<kbd>` is styled directly, so a shortcut in prose needs no class:
|
|
944
|
+
|
|
945
|
+
```html
|
|
946
|
+
<p>
|
|
947
|
+
Press
|
|
948
|
+
<span class="mw-kbd-group"
|
|
949
|
+
><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd></span
|
|
950
|
+
>
|
|
951
|
+
to open the palette, <kbd>Esc</kbd> to close.
|
|
952
|
+
</p>
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
`mw-kbd` is the same look for a `<span>` when the markup is not yours to change.
|
|
956
|
+
`mw-kbd-group` wraps a combination so the pluses are spaced and the whole thing
|
|
957
|
+
cannot wrap apart. `mw-kbd-pressed` shows a key mid-press - the raised bottom
|
|
958
|
+
edge drops and the key sits 1px lower.
|
|
959
|
+
|
|
812
960
|
## Info badges & counters
|
|
813
961
|
|
|
814
962
|
```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` | `[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
|
+
| 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
|
|
|
@@ -117,7 +117,7 @@ same height as the login and burger buttons.
|
|
|
117
117
|
palette. All of them derive from `--mw-primary-color`, not from a theme
|
|
118
118
|
colour: the bar is dark in both themes and a light-only project must not have
|
|
119
119
|
to configure dark-theme values to change it. The bar itself is the primary
|
|
120
|
-
darkened toward black, `$header-surface` (
|
|
120
|
+
darkened toward black, `$header-surface` (14%) - see `theming.md`.
|
|
121
121
|
|
|
122
122
|
**Localhost indicator.** Put `mw-localhost-indicator-activated` on the header
|
|
123
123
|
and the shipped JS prepends a pulsing bar when the host is localhost/127.0.0.1/
|
|
@@ -238,8 +238,8 @@ mode with the background image from `--mw-hero-background`.
|
|
|
238
238
|
`mw-home-content-fade` fades the content in.
|
|
239
239
|
|
|
240
240
|
The image is treated per theme through `--mw-hero-image-filter` - a filter, not
|
|
241
|
-
a tinted overlay: `$hero-filter-dark` (`brightness(0.
|
|
242
|
-
`$hero-filter-light` (`brightness(1.
|
|
241
|
+
a tinted overlay: `$hero-filter-dark` (`brightness(0.7)`) against
|
|
242
|
+
`$hero-filter-light` (`brightness(1.1)`). A translucent layer can
|
|
243
243
|
only darken toward its own colour, so on an already dark photo both themes look
|
|
244
244
|
the same until the alpha is high enough that the tint colour is what you see.
|
|
245
245
|
Moving the image's brightness reads as a difference even on a dark photo, and
|
|
@@ -314,6 +314,41 @@ automatically.
|
|
|
314
314
|
**Display** - `mw-d-flex`, `mw-d-inline-flex`, `mw-d-block`, `mw-d-inline`,
|
|
315
315
|
`mw-d-inline-block`, `mw-d-grid`, `mw-d-none`, `mw-d-contents`.
|
|
316
316
|
|
|
317
|
+
**Responsive display** - `mw-d-{sm|md|lg|xl}-{none|block|flex|inline-flex|grid|inline-block}`.
|
|
318
|
+
Mobile-first and min-width, like everything else here: `mw-d-md-flex` means
|
|
319
|
+
"flex from the md breakpoint up", and the unprefixed class beside it is what
|
|
320
|
+
applies below that. The pair `mw-d-none mw-d-md-flex` is hidden on a phone and a
|
|
321
|
+
row from a tablet on. `mw-hide-mobile` (gone below md) and `mw-hide-desktop`
|
|
322
|
+
(gone from md up) spell out the two everyone actually reaches for.
|
|
323
|
+
|
|
324
|
+
**Text overflow** - `mw-text-truncate` is one line ending in an ellipsis and
|
|
325
|
+
carries `min-width: 0` with it, which is the reason truncation "does not work"
|
|
326
|
+
nine times out of ten inside a flex row. `mw-text-clamp-2` through `-5` is that
|
|
327
|
+
many lines ending in one. `mw-text-break` is for a string with nothing to break
|
|
328
|
+
at - a URL, a hash, an API key - and is what keeps a phone page from scrolling
|
|
329
|
+
sideways. `mw-text-nowrap` is the opposite.
|
|
330
|
+
|
|
331
|
+
**Wrapping and measure** - `mw-text-balance` evens out the lines of a short
|
|
332
|
+
block so a headline never leaves one word alone (headings get it already);
|
|
333
|
+
`mw-text-pretty` only prevents the orphan and is the one for body copy
|
|
334
|
+
(paragraphs get it already). `mw-text-measure` caps a column at 68 characters -
|
|
335
|
+
past roughly 75 the eye loses the start of the next line on the way back.
|
|
336
|
+
`mw-text-eyebrow` is the small spaced upper-case kicker above a heading.
|
|
337
|
+
`mw-text-uppercase` carries the letter-spacing capitals need to stay legible
|
|
338
|
+
with it; `mw-text-capitalize` is the plain transform.
|
|
339
|
+
|
|
340
|
+
**Elevation** - `mw-elevation-0` through `-5`, the same ramp every component
|
|
341
|
+
uses. See the scale table in `SKILL.md`. Never hand-roll a `box-shadow`.
|
|
342
|
+
|
|
343
|
+
**Aspect ratio** - `mw-aspect-square|video|wide|portrait|photo`. Reserves the
|
|
344
|
+
box before the image inside it has loaded, so the page does not reflow when the
|
|
345
|
+
picture arrives. The child fills the box and crops rather than stretching.
|
|
346
|
+
|
|
347
|
+
**Overflow and scrolling** - `mw-overflow-auto`, `mw-overflow-x-auto` (which
|
|
348
|
+
also contains the overscroll, so a sideways swipe on a wide table does not walk
|
|
349
|
+
the whole page), `mw-overflow-hidden`, and `mw-snap-x` to make a horizontal
|
|
350
|
+
strip come to rest on an item instead of halfway between two.
|
|
351
|
+
|
|
317
352
|
**Numbers** - `mw-text-numeric` is fixed-width digits and nothing else, for a
|
|
318
353
|
clock, a counter or an ID that must not jitter while it changes.
|
|
319
354
|
`mw-text-currency` adds right alignment and `nowrap` on top, which is what a
|