maverick-wave 5.30.0 → 5.32.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/README.md +2 -2
- package/maverick-wave.min.css +2 -2
- package/package.json +8 -2
- package/src/scss/abstracts/_variables.scss +2 -0
- package/src/scss/components/_modals.scss +23 -9
- package/src/scss/components/_testimonial.scss +2 -0
- package/src/scss/layout/_home.scss +46 -0
- package/src/scss/layout/_main.scss +20 -0
- package/src/scss/layout/_parallax.scss +2 -2
- package/src/scss/main-lean.scss +8 -3
- package/.claude/settings.local.json +0 -43
- package/.claude/skills/mw-maverick-wave/SKILL.md +0 -532
- package/.claude/skills/mw-maverick-wave/examples/angular-form.md +0 -279
- package/.claude/skills/mw-maverick-wave/examples/angular-list-page.md +0 -309
- package/.claude/skills/mw-maverick-wave/examples/angular-services.md +0 -538
- package/.claude/skills/mw-maverick-wave/examples/static-landing-page.md +0 -530
- package/.claude/skills/mw-maverick-wave/references/components.md +0 -2170
- package/.claude/skills/mw-maverick-wave/references/forms.md +0 -358
- package/.claude/skills/mw-maverick-wave/references/javascript.md +0 -164
- package/.claude/skills/mw-maverick-wave/references/layout.md +0 -764
- package/.claude/skills/mw-maverick-wave/references/theming.md +0 -530
- package/.github/workflows/ci.yml +0 -41
- package/.idea/codeStyles/Project.xml +0 -59
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/maverick-wave.iml +0 -12
- package/.idea/modules.xml +0 -8
- package/.idea/prettier.xml +0 -6
- package/.idea/vcs.xml +0 -6
- package/.prettierignore +0 -8
- package/.prettierrc.json +0 -7
- package/CHANGELOG.md +0 -956
- package/CLAUDE.md +0 -60
- package/gulpfile.js +0 -167
- package/index.html +0 -1730
- package/release.sh +0 -31
- package/scripts/verify.js +0 -252
- package/src/assets/favicon/apple-touch-icon.png +0 -0
- package/src/assets/favicon/favicon-96x96.png +0 -0
- package/src/assets/favicon/favicon.svg +0 -1
- package/src/assets/favicon/site.webmanifest +0 -37
- package/src/assets/favicon/web-app-manifest-192x192.png +0 -0
- package/src/assets/favicon/web-app-manifest-512x512.png +0 -0
- package/src/assets/favicon.ico +0 -0
- package/src/assets/header-logo.svg +0 -24
- package/src/assets/images/gallery-city.svg +0 -49
- package/src/assets/images/gallery-desert.svg +0 -25
- package/src/assets/images/gallery-forest.svg +0 -46
- package/src/assets/images/gallery-lake.svg +0 -26
- package/src/assets/images/gallery-mountain.svg +0 -19
- package/src/assets/images/gallery-ocean.svg +0 -26
- package/src/assets/images/gallery-oldtown.svg +0 -41
- package/src/assets/images/gallery-snow.svg +0 -39
- package/src/assets/images/photo-balloon.svg +0 -35
- package/src/assets/images/photo-forest-path.svg +0 -31
- package/src/assets/images/photo-lighthouse.svg +0 -42
- package/src/assets/images/photo-palm.svg +0 -27
- package/src/assets/images/photo-peak.svg +0 -27
- package/src/assets/images/photo-tower.svg +0 -34
- package/src/assets/images/photo-waterfall.svg +0 -32
- package/src/assets/images/slider-field.svg +0 -124
- package/src/assets/images/slider-forest.svg +0 -33
- package/src/assets/images/slider-lake.svg +0 -35
- package/src/assets/images/slider-mountains.svg +0 -29
- package/src/assets/images/slider-trees.svg +0 -41
- package/src/assets/images/story-aurora.svg +0 -36
- package/src/assets/images/story-beach.svg +0 -34
- package/src/assets/images/story-stars.svg +0 -35
- package/src/assets/images/tile-analytics.svg +0 -22
- package/src/assets/images/tile-cloud.svg +0 -19
- package/src/assets/images/tile-ecommerce.svg +0 -14
- package/src/assets/images/tile-enterprise.svg +0 -22
- package/src/assets/images/tile-marketing.svg +0 -16
- package/src/assets/media/demo-chime.wav +0 -0
- package/src/assets/media/story-sunrise.jpg +0 -0
- package/src/assets/media/story-sunrise.mp4 +0 -0
- package/src/partials/accordions-container.html +0 -179
- package/src/partials/alerts-container.html +0 -151
- package/src/partials/announcement-container.html +0 -67
- package/src/partials/avatars-container.html +0 -229
- package/src/partials/badges-container.html +0 -114
- package/src/partials/blog-posts-container.html +0 -158
- package/src/partials/breadcrumbs-container.html +0 -126
- package/src/partials/button-bar-container.html +0 -175
- package/src/partials/buttons-container.html +0 -272
- package/src/partials/calendar-container.html +0 -203
- package/src/partials/cards-container.html +0 -727
- package/src/partials/code-container.html +0 -194
- package/src/partials/colors-container.html +0 -263
- package/src/partials/coming-soon-container.html +0 -10
- package/src/partials/contact-container.html +0 -85
- package/src/partials/content-slider-container.html +0 -36
- package/src/partials/divider-container.html +0 -70
- package/src/partials/documentation-container.html +0 -204
- package/src/partials/dropdown-container.html +0 -119
- package/src/partials/empty-state-container.html +0 -79
- package/src/partials/feed-container.html +0 -506
- package/src/partials/footer-container.html +0 -113
- package/src/partials/form-container.html +0 -262
- package/src/partials/form-elements-container.html +0 -616
- package/src/partials/form-field-container.html +0 -166
- package/src/partials/gallery-container.html +0 -62
- package/src/partials/get-started-container.html +0 -423
- package/src/partials/grid-container.html +0 -191
- package/src/partials/header-container.html +0 -69
- package/src/partials/header-utilities-container.html +0 -331
- package/src/partials/highlights-container.html +0 -71
- package/src/partials/history-container.html +0 -67
- package/src/partials/home-container.html +0 -31
- package/src/partials/html-lists-container.html +0 -63
- package/src/partials/info-container.html +0 -104
- package/src/partials/input-group-container.html +0 -299
- package/src/partials/item-lists-container.html +0 -368
- package/src/partials/kanban-container.html +0 -459
- package/src/partials/kbd-container.html +0 -63
- package/src/partials/leader-row-container.html +0 -139
- package/src/partials/login-container.html +0 -65
- package/src/partials/lunch-menu-container.html +0 -341
- package/src/partials/media-container.html +0 -27
- package/src/partials/meta-info-container.html +0 -21
- package/src/partials/modals-container.html +0 -199
- package/src/partials/mosaic-container.html +0 -173
- package/src/partials/page-header-container.html +0 -78
- package/src/partials/pagination-container.html +0 -64
- package/src/partials/palette-container.html +0 -480
- package/src/partials/panels-container.html +0 -126
- package/src/partials/parallax-container.html +0 -147
- package/src/partials/portrait-gallery-container.html +0 -175
- package/src/partials/preview-container.html +0 -136
- package/src/partials/pricing-container.html +0 -523
- package/src/partials/progress-container.html +0 -230
- package/src/partials/prose-container.html +0 -43
- package/src/partials/ratings-container.html +0 -104
- package/src/partials/section-head-container.html +0 -77
- package/src/partials/segmented-container.html +0 -66
- package/src/partials/skeleton-container.html +0 -87
- package/src/partials/spinners-container.html +0 -96
- package/src/partials/stepper-container.html +0 -111
- package/src/partials/stories-container.html +0 -182
- package/src/partials/tables-container.html +0 -309
- package/src/partials/tabs-container.html +0 -290
- package/src/partials/tags-container.html +0 -242
- package/src/partials/techstack-bucket-container.html +0 -187
- package/src/partials/testimonials-container.html +0 -88
- package/src/partials/tiles-container.html +0 -247
- package/src/partials/timelines-container.html +0 -196
- package/src/partials/typography-container.html +0 -74
- package/src/partials/utilities-container.html +0 -553
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
# Forms
|
|
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` | 1.875rem | 0.8rem |
|
|
10
|
-
| base | 2.125rem | 0.9rem |
|
|
11
|
-
| `-lg` | 2.375rem | 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
|
-
|
|
29
|
-
## The field pattern
|
|
30
|
-
|
|
31
|
-
`mw-field` groups label, control, hint and error into one unit. It is the
|
|
32
|
-
recommended wrapper for every labelled control and the natural fit for a
|
|
33
|
-
reactive form control.
|
|
34
|
-
|
|
35
|
-
```html
|
|
36
|
-
<div class="mw-field">
|
|
37
|
-
<label class="mw-field-label mw-required" for="email">Email</label>
|
|
38
|
-
<input id="email" type="email" class="mw-input" />
|
|
39
|
-
<span class="mw-field-hint">Used for login and notifications.</span>
|
|
40
|
-
<span class="mw-field-error">
|
|
41
|
-
<i class="fas fa-exclamation-circle"></i> Please enter a valid email
|
|
42
|
-
address.
|
|
43
|
-
</span>
|
|
44
|
-
</div>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
- `mw-required` on the label appends a red asterisk (`data-required="true"`
|
|
48
|
-
works too). A label sitting directly in front of a control with the `required`
|
|
49
|
-
attribute gets the asterisk without either class - useful for template-driven
|
|
50
|
-
and plain HTML forms, where the marker then cannot drift out of step with the
|
|
51
|
-
validation.
|
|
52
|
-
- `mw-field-hint` is the small muted helper line, `mw-field-error` the small red
|
|
53
|
-
one. Render only one of them at a time.
|
|
54
|
-
- `mw-field-has-error` on the **wrapper** turns the border of the contained
|
|
55
|
-
`mw-input` / `mw-select` / `mw-textarea` red and adds a soft red halo.
|
|
56
|
-
- `mw-form-element-error` does the same for a single control that has no field
|
|
57
|
-
wrapper - it also works on `mw-checkbox-group`, `mw-radio-group` and
|
|
58
|
-
`mw-slider-container`. On the two groups it adds the inset a field brings with
|
|
59
|
-
it, so the rows do not sit flush against the border.
|
|
60
|
-
- Native validation is styled too: a control that fails `required`, `type` or
|
|
61
|
-
`pattern` gets the same red border and halo through `:user-invalid`, and the
|
|
62
|
-
`mw-field-hint` beside it turns red. `:user-invalid` and not `:invalid`, so an
|
|
63
|
-
untouched empty field is not red on page load. Nothing has to be bound for
|
|
64
|
-
this - which is exactly why the classes still exist for the case below, where
|
|
65
|
-
the validator lives in the component rather than on the element.
|
|
66
|
-
- `mw-textarea` grows with its content (`field-sizing`) between 3 lines and
|
|
67
|
-
60dvh, where the browser supports it.
|
|
68
|
-
|
|
69
|
-
> **The framework does not style Angular's `ng-invalid` / `ng-touched` classes.**
|
|
70
|
-
> Bind the framework classes to the control state yourself:
|
|
71
|
-
>
|
|
72
|
-
> ```html
|
|
73
|
-
> <div
|
|
74
|
-
> class="mw-field"
|
|
75
|
-
> [class.mw-field-has-error]="email.invalid && email.touched"
|
|
76
|
-
> >
|
|
77
|
-
> <label class="mw-field-label mw-required" for="email">Email</label>
|
|
78
|
-
> <input id="email" type="email" class="mw-input" formControlName="email" />
|
|
79
|
-
> @if (email.hasError('required') && email.touched) {
|
|
80
|
-
> <span class="mw-field-error">
|
|
81
|
-
> <i class="fas fa-exclamation-circle"></i> Email is required.
|
|
82
|
-
> </span>
|
|
83
|
-
> }
|
|
84
|
-
> </div>
|
|
85
|
-
> ```
|
|
86
|
-
>
|
|
87
|
-
> The same applies to any other framework - React: `className={...}`, Vue:
|
|
88
|
-
> `:class`. Nothing reacts to validation on its own.
|
|
89
|
-
|
|
90
|
-
## Form layout
|
|
91
|
-
|
|
92
|
-
```html
|
|
93
|
-
<form class="mw-form">
|
|
94
|
-
<div class="mw-form-group">
|
|
95
|
-
<h4 class="mw-form-group-title">Personal information</h4>
|
|
96
|
-
<div class="mw-grid-2">
|
|
97
|
-
<div class="mw-field">...</div>
|
|
98
|
-
<div class="mw-field">...</div>
|
|
99
|
-
</div>
|
|
100
|
-
</div>
|
|
101
|
-
|
|
102
|
-
<div class="mw-form-actions">
|
|
103
|
-
<p class="mw-actions-note">Changes are saved immediately.</p>
|
|
104
|
-
<button type="button" class="mw-btn mw-btn-outline">Cancel</button>
|
|
105
|
-
<button type="submit" class="mw-btn mw-btn-primary">Save</button>
|
|
106
|
-
</div>
|
|
107
|
-
</form>
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
- `mw-form` is a flex column with a gap and **no padding** - safe to put
|
|
111
|
-
directly on `mw-modal-body` or `mw-panel-body`. It also works on a `<div>`
|
|
112
|
-
when there is no real form element.
|
|
113
|
-
- `mw-form-inline` lays the children out in a row.
|
|
114
|
-
- `mw-form-group` is the bordered block for a titled group of fields.
|
|
115
|
-
`mw-form-group-title` is the heading hook inside it - put it on the `<h3>` /
|
|
116
|
-
`<h4>`, otherwise the heading keeps its full document-level size.
|
|
117
|
-
- `mw-form-actions` is a right-aligned wrapping button row.
|
|
118
|
-
`mw-actions-note` is a full-width note above the buttons and works the same
|
|
119
|
-
way in a modal, card or panel footer (`references/components.md`). The
|
|
120
|
-
alignment variants steer it along: `mw-form-actions-left`,
|
|
121
|
-
`mw-form-actions-center`, `mw-form-actions-full-width` (stacked, buttons at
|
|
122
|
-
100% - login forms).
|
|
123
|
-
`mw-form-actions-hint` is the old name for the note and still styled, but new
|
|
124
|
-
markup should use `mw-actions-note`.
|
|
125
|
-
- Put multi-column layouts inside a group with `mw-grid-2` etc. and let a field
|
|
126
|
-
span everything with `style="grid-column: 1 / -1"`.
|
|
127
|
-
|
|
128
|
-
## Text inputs
|
|
129
|
-
|
|
130
|
-
```html
|
|
131
|
-
<input type="text" class="mw-input" />
|
|
132
|
-
<input type="text" class="mw-input mw-input-sm" />
|
|
133
|
-
<input type="text" class="mw-input mw-input-lg" />
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
- Full width by default, `2px` border, focus ring in the primary colour.
|
|
137
|
-
- `readonly` renders on a muted surface, `disabled` additionally as
|
|
138
|
-
`not-allowed`. Both are attribute driven - there is no readonly/disabled
|
|
139
|
-
class.
|
|
140
|
-
- Date/time work as normal inputs (`type="date" | "time" | "datetime-local"`);
|
|
141
|
-
the native picker indicator is styled.
|
|
142
|
-
|
|
143
|
-
### Numbers and money
|
|
144
|
-
|
|
145
|
-
```html
|
|
146
|
-
<div class="mw-field">
|
|
147
|
-
<label class="mw-field-label" for="budget">Budget</label>
|
|
148
|
-
<div class="mw-input-group">
|
|
149
|
-
<span class="mw-input-group-prefix"><i class="fas fa-euro-sign"></i></span>
|
|
150
|
-
<input
|
|
151
|
-
id="budget"
|
|
152
|
-
type="text"
|
|
153
|
-
inputmode="decimal"
|
|
154
|
-
class="mw-input mw-input-numeric"
|
|
155
|
-
placeholder="0,00"
|
|
156
|
-
/>
|
|
157
|
-
<span class="mw-input-group-suffix">EUR</span>
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
`mw-input-numeric` is the entry counterpart to the `mw-text-numeric` utility:
|
|
163
|
-
right aligned, tabular lining figures, native spinners suppressed. A value looks
|
|
164
|
-
identical while being typed and once it is rendered into a table.
|
|
165
|
-
|
|
166
|
-
> **Use `type="text"` + `inputmode="decimal"`, not `type="number"`.** A focused
|
|
167
|
-
> number input changes its value when the page is scrolled, and in most locales
|
|
168
|
-
> it rejects a comma as decimal separator - the user sees `1234,50` but `.value`
|
|
169
|
-
> comes back empty. `inputmode="decimal"` still brings up the numeric keypad on
|
|
170
|
-
> mobile. Parsing the comma stays the application's job.
|
|
171
|
-
|
|
172
|
-
## Input group
|
|
173
|
-
|
|
174
|
-
Prefix, suffix and buttons glued to the control:
|
|
175
|
-
|
|
176
|
-
```html
|
|
177
|
-
<div class="mw-input-group">
|
|
178
|
-
<span class="mw-input-group-prefix"><i class="fas fa-search"></i></span>
|
|
179
|
-
<input type="text" class="mw-input" placeholder="Search…" />
|
|
180
|
-
<button type="button" class="mw-btn mw-btn-primary">
|
|
181
|
-
<i class="fas fa-search"></i>
|
|
182
|
-
</button>
|
|
183
|
-
</div>
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
Sizes: `mw-input-group-sm`, `mw-input-group-lg` - combine them with the matching
|
|
187
|
-
`mw-input-sm` / `mw-input-lg` on the control. A prefix or suffix takes an icon,
|
|
188
|
-
a symbol (`@`, `https://`) or a short unit.
|
|
189
|
-
|
|
190
|
-
## Select
|
|
191
|
-
|
|
192
|
-
```html
|
|
193
|
-
<select class="mw-select">
|
|
194
|
-
<option value="" disabled selected>Choose…</option>
|
|
195
|
-
<option>Option A</option>
|
|
196
|
-
</select>
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
Sizes: `mw-select-sm`, `mw-select-lg`.
|
|
200
|
-
|
|
201
|
-
## Textarea
|
|
202
|
-
|
|
203
|
-
```html
|
|
204
|
-
<textarea class="mw-textarea" rows="4"></textarea>
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
Sizes: `mw-textarea-sm`, `mw-textarea-lg`. Resizing is off by default; enable it
|
|
208
|
-
with `mw-textarea-resizable`, `mw-textarea-resizable-vertical` or
|
|
209
|
-
`mw-textarea-resizable-horizontal`.
|
|
210
|
-
|
|
211
|
-
## Prefilled values
|
|
212
|
-
|
|
213
|
-
`mw-prefilled` marks a control whose value did not come from the user in this
|
|
214
|
-
session - loaded from an existing record, restored from a draft, filled with a
|
|
215
|
-
default. It draws a small triangle into the top left corner of the control.
|
|
216
|
-
|
|
217
|
-
```html
|
|
218
|
-
<input type="text" class="mw-input mw-prefilled" value="Max Mustermann" />
|
|
219
|
-
<select class="mw-select mw-select-sm mw-prefilled">
|
|
220
|
-
...
|
|
221
|
-
</select>
|
|
222
|
-
<textarea class="mw-textarea mw-prefilled" rows="3"></textarea>
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
- Works on `mw-input`, `mw-select` and `mw-textarea`, and goes on the control
|
|
226
|
-
itself, not on the `mw-field` wrapper - so it also works in an input group or
|
|
227
|
-
on a standalone control.
|
|
228
|
-
- The triangle follows the size modifier (`mw-input-sm`, `mw-select-lg`, ...)
|
|
229
|
-
and stays visible on `readonly` and `disabled` controls.
|
|
230
|
-
- Colour is `--mw-info-color`, deliberately not primary or danger: it is an
|
|
231
|
-
information about the value, not a state or an error.
|
|
232
|
-
- Decoration only. Screen readers do not see it, so put the same information in
|
|
233
|
-
an `mw-field-hint` and reference it with `aria-describedby`.
|
|
234
|
-
- It is drawn as a background layer, not a pseudo element (`input` and `select`
|
|
235
|
-
never render `::before`/`::after`). A rule that sets the `background`
|
|
236
|
-
shorthand on the same control wipes it - use `background-color` there.
|
|
237
|
-
|
|
238
|
-
## Checkbox
|
|
239
|
-
|
|
240
|
-
The native input is hidden; `mw-checkbox-box` is the visible control, so the
|
|
241
|
-
order of the three children matters.
|
|
242
|
-
|
|
243
|
-
```html
|
|
244
|
-
<div class="mw-checkbox-group mw-checkbox-group-inline">
|
|
245
|
-
<label class="mw-checkbox mw-checkbox-success">
|
|
246
|
-
<input type="checkbox" />
|
|
247
|
-
<div class="mw-checkbox-box"></div>
|
|
248
|
-
<div class="mw-checkbox-label">Send a copy</div>
|
|
249
|
-
</label>
|
|
250
|
-
</div>
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
- Sizes: `mw-checkbox-sm`, `mw-checkbox-lg`
|
|
254
|
-
- Colours: `mw-checkbox-primary`, `-secondary`, `-success`, `-warning`,
|
|
255
|
-
`-danger`, `-info`
|
|
256
|
-
- Disabled: `mw-checkbox-disabled` on the label **plus** the `disabled`
|
|
257
|
-
attribute on the input
|
|
258
|
-
- Group: `mw-checkbox-group` (column), `mw-checkbox-group-inline` (row)
|
|
259
|
-
- With a heading and a description use `mw-checkbox-content` containing
|
|
260
|
-
`mw-checkbox-header` + `mw-checkbox-label` - see the checkbox item list in
|
|
261
|
-
`references/components.md`
|
|
262
|
-
|
|
263
|
-
## Radio
|
|
264
|
-
|
|
265
|
-
Same structure, with `mw-radio-button` as the visible control:
|
|
266
|
-
|
|
267
|
-
```html
|
|
268
|
-
<div class="mw-radio-group">
|
|
269
|
-
<label class="mw-radio">
|
|
270
|
-
<input type="radio" name="plan" value="free" />
|
|
271
|
-
<div class="mw-radio-button"></div>
|
|
272
|
-
<div class="mw-radio-label">Free</div>
|
|
273
|
-
</label>
|
|
274
|
-
</div>
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
Sizes `mw-radio-sm`, `-lg`; colours `mw-radio-primary`, `-secondary`,
|
|
278
|
-
`-success`, `-warning`, `-danger`, `-info`; `mw-radio-disabled`; groups
|
|
279
|
-
`mw-radio-group`, `mw-radio-group-inline`.
|
|
280
|
-
|
|
281
|
-
## Toggle
|
|
282
|
-
|
|
283
|
-
```html
|
|
284
|
-
<div class="mw-toggle-group">
|
|
285
|
-
<label class="mw-toggle mw-toggle-success">
|
|
286
|
-
<input type="checkbox" checked />
|
|
287
|
-
<div class="mw-toggle-track"></div>
|
|
288
|
-
<span class="mw-toggle-label">Notifications</span>
|
|
289
|
-
</label>
|
|
290
|
-
</div>
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
Sizes `mw-toggle-sm`, `-lg`; colours `mw-toggle-primary`, `-secondary`,
|
|
294
|
-
`-success`, `-warning`, `-danger` (no `info` variant); `mw-toggle-disabled` plus
|
|
295
|
-
the `disabled` attribute; groups `mw-toggle-group`, `mw-toggle-group-inline`.
|
|
296
|
-
|
|
297
|
-
## Slider
|
|
298
|
-
|
|
299
|
-
```html
|
|
300
|
-
<label>
|
|
301
|
-
<span>Experience</span>
|
|
302
|
-
<div class="mw-slider-container">
|
|
303
|
-
<div class="mw-slider-value mw-slider-numeric" data-value="3"></div>
|
|
304
|
-
<input
|
|
305
|
-
type="range"
|
|
306
|
-
class="mw-slider mw-slider-primary"
|
|
307
|
-
min="0"
|
|
308
|
-
max="10"
|
|
309
|
-
value="3"
|
|
310
|
-
/>
|
|
311
|
-
</div>
|
|
312
|
-
</label>
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
- The badge prints `data-value`: `mw-slider-numeric` appends `/10`,
|
|
316
|
-
`mw-slider-percent` appends `%`.
|
|
317
|
-
- The filled part of the track comes from the custom property `--value` on the
|
|
318
|
-
input (`style="--value: 30%"`).
|
|
319
|
-
- Both `data-value` and `--value` are set by the shipped JS on `input`. In a SPA
|
|
320
|
-
bind them yourself - two bindings, see `examples/angular-form.md`.
|
|
321
|
-
- Colours: `mw-slider-primary`, `-secondary`, `-success`, `-warning`, `-danger`,
|
|
322
|
-
`-info`; sizes `mw-slider-sm`, `-lg`.
|
|
323
|
-
|
|
324
|
-
## Login card
|
|
325
|
-
|
|
326
|
-
A centered, self-contained card for sign-in screens.
|
|
327
|
-
|
|
328
|
-
```html
|
|
329
|
-
<div class="mw-login">
|
|
330
|
-
<div class="mw-login-logo"><img src="logo.svg" alt="" /></div>
|
|
331
|
-
<div class="mw-login-message">Internal area - valid account required.</div>
|
|
332
|
-
<div class="mw-login-message mw-login-message-error">Wrong credentials.</div>
|
|
333
|
-
|
|
334
|
-
<form class="mw-form">
|
|
335
|
-
<div class="mw-grid-1">
|
|
336
|
-
<div class="mw-field">
|
|
337
|
-
<label class="mw-field-label mw-required" for="login-email"
|
|
338
|
-
>Email</label
|
|
339
|
-
>
|
|
340
|
-
<div class="mw-input-group">
|
|
341
|
-
<span class="mw-input-group-prefix"
|
|
342
|
-
><i class="fas fa-envelope"></i
|
|
343
|
-
></span>
|
|
344
|
-
<input id="login-email" type="email" class="mw-input" required />
|
|
345
|
-
</div>
|
|
346
|
-
</div>
|
|
347
|
-
</div>
|
|
348
|
-
<div class="mw-form-actions mw-form-actions-full-width">
|
|
349
|
-
<button type="submit" class="mw-btn mw-btn-primary">
|
|
350
|
-
<i class="fas fa-sign-in-alt"></i> Sign in
|
|
351
|
-
</button>
|
|
352
|
-
</div>
|
|
353
|
-
</form>
|
|
354
|
-
</div>
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
`mw-login-error` on the card flashes a red border after a failed attempt,
|
|
358
|
-
`mw-login-message-info` / `mw-login-message-error` colour the message line.
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
# JavaScript & SPA integration
|
|
2
|
-
|
|
3
|
-
## What `maverick-wave.min.js` is
|
|
4
|
-
|
|
5
|
-
One vanilla IIFE, no dependencies, ~24 kB. It queries the DOM on
|
|
6
|
-
`DOMContentLoaded` and attaches listeners. No `MutationObserver`, no exported
|
|
7
|
-
module - it is built for a server-rendered or static page.
|
|
8
|
-
|
|
9
|
-
Markup that arrives later gets wired through the one public entry point:
|
|
10
|
-
|
|
11
|
-
```js
|
|
12
|
-
window.MaverickWave.init(subtree); // or init() for the whole document
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Call it after an htmx swap, after filling a modal from a fetch, or after a view
|
|
16
|
-
transition replaced the body. Every element that already carries listeners is
|
|
17
|
-
skipped, so calling it twice over the same markup does nothing - the components
|
|
18
|
-
are safe to re-run, the document- and window-level behaviour is wired once and
|
|
19
|
-
never again.
|
|
20
|
-
|
|
21
|
-
What that does **not** make it is a SPA library - see below.
|
|
22
|
-
|
|
23
|
-
## Why a SPA must not load it
|
|
24
|
-
|
|
25
|
-
- Anything rendered after bootstrap needs `MaverickWave.init()` by hand, for
|
|
26
|
-
every subtree, on every render - in a routed application that is all of it.
|
|
27
|
-
- It writes straight into the DOM (class toggles, generated elements, inline
|
|
28
|
-
styles). In Angular that happens outside change detection; in a zoneless app
|
|
29
|
-
the framework never learns about it, and on the next re-render your bindings
|
|
30
|
-
win and the mutation is gone.
|
|
31
|
-
- It reads and writes `localStorage` for the theme, competing with whatever
|
|
32
|
-
service you build for the same job.
|
|
33
|
-
|
|
34
|
-
So: no `"scripts"` entry in `angular.json`, no `import 'maverick-wave.min.js'`
|
|
35
|
-
in a Vite entry point. Load the **CSS only** and rebuild the handful of
|
|
36
|
-
behaviours in components. Each one is a few lines - the framework's state
|
|
37
|
-
classes are the entire contract.
|
|
38
|
-
|
|
39
|
-
## The theme has to be applied before the first paint
|
|
40
|
-
|
|
41
|
-
`main.js` sits at the end of the body, so a stored choice that differs from the
|
|
42
|
-
operating system reaches the page one frame too late - the reader sees the other
|
|
43
|
-
theme flash. The fix is five lines in the `<head>`, inline and synchronous,
|
|
44
|
-
before the stylesheet has painted anything:
|
|
45
|
-
|
|
46
|
-
```html
|
|
47
|
-
<script>
|
|
48
|
-
(function () {
|
|
49
|
-
var stored = localStorage.getItem('mw-theme');
|
|
50
|
-
if (stored === 'light' || stored === 'dark') {
|
|
51
|
-
document.documentElement.classList.add('mw-theme-' + stored);
|
|
52
|
-
}
|
|
53
|
-
})();
|
|
54
|
-
</script>
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
That is also why the toggle writes the class on `<html>` and not on `<body>`:
|
|
58
|
-
in the head there is no body yet. An external file would not help - it paints
|
|
59
|
-
first. With nothing stored, nothing happens here and the page follows the OS on
|
|
60
|
-
its own through `light-dark()`.
|
|
61
|
-
|
|
62
|
-
## Behaviour inventory
|
|
63
|
-
|
|
64
|
-
| Behaviour | What the shipped JS does | What to do instead |
|
|
65
|
-
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
66
|
-
| 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 |
|
|
67
|
-
| Tabs | `data-tab` → panel `id`; sets `mw-active` on nav item and panel, and wires the whole tablist: `role`, `aria-selected`, `aria-controls`, `aria-labelledby`, a roving `tabindex` and arrow/Home/End keys | Track the selected index/key, bind `mw-active` on both; drop `data-tab` |
|
|
68
|
-
| Modal | `data-mw-modal="<id>"` on a trigger opens that modal; a click on `mw-modal-close` closes the `<dialog>` it sits in, or clears `mw-modal-open` on an overlay div | A `<dialog>` needs a directive calling `showModal()` - `[open]` only opens it non-modally. See `examples/angular-services.md` |
|
|
69
|
-
| Mobile nav | Toggles `open` on `mw-menu-btn` and `mw-navbar` plus `mw-nav-open` on `<body>`, writes `aria-expanded` when the button is a `<button>`, closes on anchor click and on Escape (focus returns to the button) | One signal, bound to all three - the body class carries the scrim, the scroll lock and the pinning of the bar; reset it on navigation end |
|
|
70
|
-
| Scroll spy | Sets `mw-active` on `mw-navbar-link` from the scroll position | Router-based: `routerLinkActive="mw-active"` |
|
|
71
|
-
| Anchor scrolling | Intercepts `a[href^="#"]` and runs its own eased scroll - duration scales with distance, capped at 1.4s, cancelled by wheel or touch. Lands on `scroll-padding-top`, moves focus to the target, writes the hash with `replaceState`, and measures a sticky target unpinned | The router; for in-page anchors `scrollIntoView({ behavior: 'smooth' })` or your own animation |
|
|
72
|
-
| Theme toggle | `localStorage['mw-theme']`, toggles `mw-theme-light` / `mw-theme-dark` on `<html>` and `mw-active` on the toggle, wrapped in `mw-theme-switching` on `<html>` so the flip starts no transitions. With nothing stored it takes `prefers-color-scheme` and keeps following it | A theme service - see `examples/angular-services.md` |
|
|
73
|
-
| Progress bar | `IntersectionObserver` sets `width` from `data-value` | Bind `[style.width.%]="value()"` on `mw-progress-fill` |
|
|
74
|
-
| Slider | On `input`, sets `--value` (track fill) and `data-value` (badge text) | Bind `[style.--value.%]` and `[attr.data-value]` |
|
|
75
|
-
| Alerts | Close button adds `mw-alert-closing` (fade out), then `mw-alert-closed` (`display: none`) after `--mw-duration-base` | Remove the alert from the list/signal |
|
|
76
|
-
| Checkbox lists | Adds `mw-selected` to the `li`, emits a `checkboxToggle` event, exposes `window.mwToggleCheckbox` | `[class.mw-selected]="item.checked"` |
|
|
77
|
-
| Gallery | Generates the dots, moves the track, swipe handling (single finger, dominant axis, passive listeners), writes `mw-gallery-desc` | Render dots in the template, bind the track transform and `mw-active` on the current dot |
|
|
78
|
-
| 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 |
|
|
79
|
-
| Mosaic | Sets `mw-mosaic-wide` / `mw-mosaic-tall` on a tile from the photo's proportions - its `width`/`height` attributes, else the natural size - unless a shape class is already there | Bind the shape class from the image size you already know |
|
|
80
|
-
| Lightbox | Delegated: an image link inside `mw-mosaic` or `[data-mw-lightbox]` opens a `<dialog class="mw-lightbox">` built once - slides in reading order, images loaded one either side of the current, swipe, arrows and keys, focus back on the tile of the last photo | A component around a `<dialog>`: `showModal()`, a scroll-snap track, the index read from `scrollLeft` |
|
|
81
|
-
| Portrait gallery | Adds arrows and dots and keeps them in step with the scroll position; sets `mw-fit-contain` and a blurred `mw-fit-backdrop` on a slide whose photo is more than 1.4× off its shape, checked again by a `ResizeObserver` | Render arrows and dots, `scrollBy` one slide on click; bind `mw-fit-contain` from the image ratio |
|
|
82
|
-
| Story reel | Playback is CSS. Adds the `mw-story-reel-prev` / `-next` tap zones and arrow keys (seeking through `getAnimations()`), starts a video when its frame comes up and stops it with the story, closes the popover at the end, focuses the close button on open | Keep the CSS playback and the popover; add stepping only if you need it, the same way |
|
|
83
|
-
| 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 |
|
|
84
|
-
| 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 |
|
|
85
|
-
| Viewport check | On a local hostname, warns on the console when the viewport meta has no `viewport-fit=cover` - without it every `env(safe-area-inset-*)` resolves to 0 | Nothing - check the meta tag once |
|
|
86
|
-
| 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 |
|
|
87
|
-
| Header login button | Swaps the FontAwesome lock icon | Bind the icon class |
|
|
88
|
-
| Color swatches | Showcase-only (prints computed hex values); exposes `window.mwRefreshColorSwatches` to read them again after a root colour changed | Not needed |
|
|
89
|
-
| 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 |
|
|
90
|
-
| Language switcher | Keeps the trigger's flag and code in step with the chosen item, moves `mw-active` and `aria-current`, and fires `mw-language-change` (`detail: { lang, name }`) on the switcher | Bind the trigger from your locale signal and switch the language in your own i18n service; the menu itself is a `<details>` and needs nothing |
|
|
91
|
-
| Scroll reveal | Older browsers only: an `IntersectionObserver` adds `mw-reveal-hidden` to what is still below the fold and swaps it for `mw-reveal-run` on entry, with an `animation-delay` per grid column | A directive per element - see `examples/angular-services.md` |
|
|
92
|
-
| Header reveal | Older browsers only: toggles `mw-header-away` and `mw-announcement-away` past 270px of scroll, and adds the transition class one frame later so the bar does not slide away on load | The same two classes bound to a scroll signal, behind the same guard |
|
|
93
|
-
| Parallax | Older browsers only: writes `--mw-parallax-progress` (0 to 1) on every `mw-parallax-media` from a `requestAnimationFrame` loop | The same, reading every layer's rect before writing to any of them, and measuring against `document.documentElement.clientHeight` - `innerHeight` grows by up to 100px as the URL bar slides away and steps every layer mid-scroll |
|
|
94
|
-
|
|
95
|
-
## Scroll-driven animations
|
|
96
|
-
|
|
97
|
-
Four things ride the browser's own scroll timeline: `mw-reveal`,
|
|
98
|
-
`mw-header-reveal`, `mw-parallax` and the `mw-progress-fill` scrub. Chrome and
|
|
99
|
-
Edge have had timelines since 115, Firefox since 158 and Safari since 26, and
|
|
100
|
-
there they need no script at all. Older versions get the shipped JS instead.
|
|
101
|
-
Each one checks `CSS.supports('animation-timeline', ...)` first and does nothing
|
|
102
|
-
where the browser has it.
|
|
103
|
-
|
|
104
|
-
Without the script an older browser loses the motion and nothing else: cards
|
|
105
|
-
stand in place, the picture holds still. The exception is `mw-header-reveal`,
|
|
106
|
-
where the bar then sits over the hero from the first paint - a layout
|
|
107
|
-
difference, not a missing effect.
|
|
108
|
-
|
|
109
|
-
## Modals and progress bars
|
|
110
|
-
|
|
111
|
-
`mwOpenModal(id)` / `mwCloseModal(id)` are on `window` and handle both modal
|
|
112
|
-
shapes - a `<dialog class="mw-modal">` and the older `mw-modal-overlay` div.
|
|
113
|
-
Close buttons and `data-mw-modal="<id>"` triggers are delegated from the
|
|
114
|
-
document, so markup rendered later still works and a static page needs no code
|
|
115
|
-
of its own. In a SPA, call `showModal()` and `close()` on the element instead.
|
|
116
|
-
|
|
117
|
-
`mw-progress-fill` takes its target width from `data-value="75"` or an inline
|
|
118
|
-
`style="width: 75%"`. Where scroll-driven animations are supported the bar fills
|
|
119
|
-
with the scroll position and the script only hands the value over; elsewhere it
|
|
120
|
-
falls back to setting the width once the bar comes into view.
|
|
121
|
-
|
|
122
|
-
## A note on the state class
|
|
123
|
-
|
|
124
|
-
Since 4.0.0 the script writes `mw-active` and clears both `mw-active` and the
|
|
125
|
-
deprecated bare `active` when it switches a state off. That is deliberate: HTML
|
|
126
|
-
written against an older version marks the first tab with `active`, and if
|
|
127
|
-
switching away only removed `mw-active`, that first tab would stay lit next to
|
|
128
|
-
the newly chosen one. In your own components bind `mw-active` and forget the
|
|
129
|
-
other spelling exists.
|
|
130
|
-
|
|
131
|
-
## What works without any JavaScript
|
|
132
|
-
|
|
133
|
-
Pure CSS, nothing to wire up: hover, focus and press states, the card lift,
|
|
134
|
-
tooltips (`data-tooltip`), `mw-rating` (via `data-rating`), the responsive table
|
|
135
|
-
card view (`data-label`), all grids and utilities, the body scroll lock while a
|
|
136
|
-
modal is open (from the `<dialog>` element, or `body:has(.mw-modal-open)` for an
|
|
137
|
-
overlay div), the modal turning into a bottom sheet below 576px, toast entry animations, the photo feed and its post cards (`popover`), story
|
|
138
|
-
playback with its progress bar and pause, the sticky table header, the scroll
|
|
139
|
-
hint on a tab bar (four gradients, no scroll listener), the kanban empty-lane
|
|
140
|
-
placeholder (hidden via `:has()` as soon as the lane holds a ticket), touch
|
|
141
|
-
target sizing on a coarse pointer, `prefers-reduced-motion` handling.
|
|
142
|
-
|
|
143
|
-
`mw-dropdown` is _almost_ in this list: opening, closing, the keyboard and the
|
|
144
|
-
open state are all the `<details>` element, so it works with no script at all.
|
|
145
|
-
The only two things the shipped JS adds are closing on Escape and closing on a
|
|
146
|
-
click somewhere else - worth rebuilding in a SPA, but a menu without them is
|
|
147
|
-
still a working menu, not a broken one.
|
|
148
|
-
|
|
149
|
-
## When you do keep the shipped JS
|
|
150
|
-
|
|
151
|
-
For a static page, a landing page or a server-rendered site (Thymeleaf, Twig,
|
|
152
|
-
Jekyll, plain HTML) it is exactly right - load it at the end of `<body>` and
|
|
153
|
-
write no JavaScript at all. A modal opens from its trigger:
|
|
154
|
-
|
|
155
|
-
```html
|
|
156
|
-
<button class="mw-btn mw-btn-primary" data-mw-modal="demo">Book a demo</button>
|
|
157
|
-
|
|
158
|
-
<dialog id="demo" class="mw-modal" closedby="any">
|
|
159
|
-
…
|
|
160
|
-
<button class="mw-modal-close mw-btn mw-btn-outline">Cancel</button>
|
|
161
|
-
</dialog>
|
|
162
|
-
|
|
163
|
-
<script src="maverick-wave.min.js"></script>
|
|
164
|
-
```
|