maverick-wave 5.16.0 → 5.19.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/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "maverick-wave",
3
- "version": "5.16.0",
3
+ "version": "5.19.0",
4
4
  "config": {
5
- "version_short": "5.16"
5
+ "version_short": "5.19"
6
6
  },
7
7
  "description": "A lightweight, modern CSS framework for building responsive websites with elegance and speed.",
8
8
  "main": "src/js/main.js",
package/release.sh CHANGED
@@ -4,6 +4,11 @@ echo "## extract version from package.json"
4
4
  VERSION=$(npm pkg get version --workspaces=false | tr -d \")
5
5
  echo "## checkout release branch and grab changes from main"
6
6
  git checkout release && git merge main --no-commit
7
+ echo "## point the README CDN snippets at this version"
8
+ # The skills pin a major range and need no bump; the README is the place the
9
+ # exact-version advice belongs, and it is the one that kept going stale - 5.0.0
10
+ # shipped with six snippets still on 4.28.0.
11
+ sed -i '' -E "s|maverick-wave@[0-9]+\.[0-9]+\.[0-9]+|maverick-wave@${VERSION}|g" README.md
7
12
  echo "## make a new release"
8
13
  npm run build && npm run prepack
9
14
  echo "## add all changes and push it"
package/scripts/verify.js CHANGED
@@ -155,22 +155,35 @@ for (const f of ['maverick-wave.min.css', 'maverick-wave.min.js']) {
155
155
  }
156
156
 
157
157
  // --- 5: pinned CDN version -------------------------------------------------
158
- // Every jsDelivr snippet in the docs pins a version, which is the right advice
159
- // and the reason it goes stale: 5.0.0 shipped with six snippets still on 4.28.0.
158
+ // Two rules, because the two places are read at different times. The README is
159
+ // where someone decides how to pull the framework in, so it names the exact
160
+ // version - release.sh rewrites it, and this catches a bump that went around the
161
+ // script. The skills are installed outside this repo and never get that rewrite,
162
+ // so they carry a major range; an exact pin there is stale the day the next
163
+ // version ships and nobody notices, which is how 5.0.0 went out with six
164
+ // snippets still on 4.28.0.
160
165
 
161
166
  const { version } = require(path.join(ROOT, 'package.json'));
167
+ const EXACT_PIN = /maverick-wave@([0-9]+\.[0-9]+\.[0-9]+)/g;
162
168
 
163
- for (const p of [path.join(ROOT, 'README.md'), ...DOCS]) {
169
+ const readme = read(path.join(ROOT, 'README.md'));
170
+ if (readme) {
171
+ for (const v of new Set(
172
+ all(readme, EXACT_PIN).filter((v) => v !== version)
173
+ )) {
174
+ errors.push(
175
+ `stale CDN pin in README.md: maverick-wave@${v} - package.json is ${version}`
176
+ );
177
+ }
178
+ }
179
+
180
+ const major = version.split('.')[0];
181
+ for (const p of DOCS) {
164
182
  const md = read(p);
165
183
  if (!md) continue;
166
- const stale = new Set(
167
- all(md, /maverick-wave@([0-9]+\.[0-9]+\.[0-9]+)/g).filter(
168
- (v) => v !== version
169
- )
170
- );
171
- for (const v of stale) {
184
+ for (const v of new Set(all(md, EXACT_PIN))) {
172
185
  errors.push(
173
- `stale CDN pin in ${path.relative(ROOT, p)}: maverick-wave@${v} - package.json is ${version}`
186
+ `exact CDN pin in ${path.relative(ROOT, p)}: maverick-wave@${v} - a skill carries @${major}, so it never goes stale`
174
187
  );
175
188
  }
176
189
  }
@@ -276,9 +276,11 @@ body {
276
276
  <code>DOMContentLoaded</code> and writes straight into the DOM - in a
277
277
  SPA everything rendered afterwards stays uninitialized, and the class
278
278
  toggles happen behind change detection. Rebuild the behaviors in
279
- components; the state classes (<code>active</code>, <code>open</code>,
280
- <code>mw-modal-open</code>, <code>mw-selected</code>) are the whole
281
- contract.
279
+ components; the state classes (<code>mw-active</code>,
280
+ <code>open</code>, <code>mw-selected</code>) are the whole contract. A
281
+ modal is the exception - write it as a
282
+ <code>&lt;dialog class="mw-modal"&gt;</code> and the element brings
283
+ Escape, the focus trap and the scroll lock with it.
282
284
  </p>
283
285
  <p>
284
286
  Alternatively, use the CDN link directly in
@@ -31,7 +31,7 @@
31
31
  <!-- Theme Toggle -->
32
32
  <button
33
33
  type="button"
34
- class="mw-theme-toggle mw-ml-5"
34
+ class="mw-theme-toggle mw-header-keep mw-ml-5"
35
35
  aria-label="Toggle light and dark theme"
36
36
  >
37
37
  <div class="mw-theme-toggle-slider">
@@ -53,10 +53,20 @@
53
53
  <i class="fas fa-palette"></i>
54
54
  </button>
55
55
 
56
+ <!-- Login Button - mw-header-keep, so it is there before the bar is -->
57
+ <button
58
+ type="button"
59
+ class="mw-login-btn mw-header-keep"
60
+ id="login-button"
61
+ aria-label="Log in"
62
+ >
63
+ <i class="fas fa-lock"></i>
64
+ </button>
65
+
56
66
  <!-- Burger Menu Button -->
57
67
  <button
58
68
  type="button"
59
- class="mw-menu-btn"
69
+ class="mw-menu-btn mw-header-keep"
60
70
  aria-label="Menu"
61
71
  aria-expanded="false"
62
72
  aria-controls="mw-main-nav"
@@ -1,22 +1,25 @@
1
1
  <h3 class="mw-section-subtitle">Simple Modals</h3>
2
2
  <p class="mw-text-muted mw-mb-6">
3
- JavaScript-controlled modals - perfect for e.g. Angular apps using class
4
- toggling.
3
+ A <code>&lt;dialog&gt;</code> carrying the same <code>mw-modal</code> classes.
4
+ Escape, the focus trap, the inert page behind it and the body scroll lock come
5
+ from the element - only opening it needs a line of script. The older
6
+ <code>mw-modal-overlay</code> div is still styled for markup that predates
7
+ this.
5
8
  </p>
6
9
 
7
10
  <!-- Size Triggers -->
8
11
  <h4 class="mw-mb-3">Modal Sizes</h4>
9
12
  <div class="mw-grid-4 mw-mb-8">
10
- <button onclick="openModal('modal-sm')" class="mw-btn mw-btn-outline">
13
+ <button data-mw-modal="modal-sm" class="mw-btn mw-btn-outline">
11
14
  Small Modal
12
15
  </button>
13
- <button onclick="openModal('modal-default')" class="mw-btn mw-btn-outline">
16
+ <button data-mw-modal="modal-default" class="mw-btn mw-btn-outline">
14
17
  Default Modal
15
18
  </button>
16
- <button onclick="openModal('modal-lg')" class="mw-btn mw-btn-outline">
19
+ <button data-mw-modal="modal-lg" class="mw-btn mw-btn-outline">
17
20
  Large Modal
18
21
  </button>
19
- <button onclick="openModal('modal-xl')" class="mw-btn mw-btn-outline">
22
+ <button data-mw-modal="modal-xl" class="mw-btn mw-btn-outline">
20
23
  XL Modal
21
24
  </button>
22
25
  </div>
@@ -24,212 +27,160 @@
24
27
  <!-- Example Triggers -->
25
28
  <h4 class="mw-mb-3">Examples</h4>
26
29
  <div class="mw-grid-2 mw-mb-8">
27
- <button onclick="openModal('modal-form')" class="mw-btn mw-btn-primary">
30
+ <button data-mw-modal="modal-form" class="mw-btn mw-btn-primary">
28
31
  Open Form Modal
29
32
  </button>
30
33
  <button data-mw-modal="modal-confirm" class="mw-btn mw-btn-secondary">
31
- Open Confirmation (&lt;dialog&gt;)
34
+ Open Delete Confirmation
32
35
  </button>
33
36
  </div>
34
37
 
38
+ <!-- The close buttons carry no handler - the framework script closes the dialog
39
+ they sit in - and closedby="any" dismisses it on a backdrop click, so there
40
+ is no mw-modal-backdrop element either. `data-mw-modal` on a trigger opens
41
+ the dialog with that id. -->
35
42
  <!-- Small Modal -->
36
- <div id="modal-sm" class="mw-modal-overlay">
37
- <div class="mw-modal mw-modal-sm">
38
- <div class="mw-modal-header">
39
- <h4 class="mw-modal-title">Small Modal</h4>
40
- <button onclick="closeModal('modal-sm')" class="mw-modal-close">
41
- &#120299;
42
- </button>
43
- </div>
44
- <div class="mw-modal-body">
45
- <p class="mw-text-muted">
46
- Max-width <strong>370px</strong>. Good for short confirmations or
47
- alerts.
48
- </p>
49
- </div>
50
- <div class="mw-modal-footer">
51
- <button onclick="closeModal('modal-sm')" class="mw-btn mw-btn-outline">
52
- Close
53
- </button>
54
- </div>
43
+ <dialog id="modal-sm" class="mw-modal mw-modal-sm" closedby="any">
44
+ <div class="mw-modal-header">
45
+ <h4 class="mw-modal-title">Small Modal</h4>
46
+ <button class="mw-modal-close">&#120299;</button>
55
47
  </div>
56
- <div class="mw-modal-backdrop" onclick="closeModal('modal-sm')"></div>
57
- </div>
48
+ <div class="mw-modal-body">
49
+ <p class="mw-text-muted">
50
+ Max-width <strong>370px</strong>. Good for short confirmations or alerts.
51
+ </p>
52
+ </div>
53
+ <div class="mw-modal-footer">
54
+ <button class="mw-modal-close mw-btn mw-btn-outline">Close</button>
55
+ </div>
56
+ </dialog>
58
57
 
59
58
  <!-- Default Modal -->
60
- <div id="modal-default" class="mw-modal-overlay">
61
- <div class="mw-modal">
62
- <div class="mw-modal-header">
63
- <h4 class="mw-modal-title">Default Modal</h4>
64
- <button onclick="closeModal('modal-default')" class="mw-modal-close">
65
- &#120299;
66
- </button>
67
- </div>
68
- <div class="mw-modal-body">
69
- <p class="mw-text-muted">
70
- Max-width <strong>520px</strong>. The default size - fits most use
71
- cases.
72
- </p>
73
- </div>
74
- <div class="mw-modal-footer">
75
- <button
76
- onclick="closeModal('modal-default')"
77
- class="mw-btn mw-btn-outline"
78
- >
79
- Cancel
80
- </button>
81
- <button
82
- onclick="closeModal('modal-default')"
83
- class="mw-btn mw-btn-primary"
84
- >
85
- Confirm
86
- </button>
87
- </div>
59
+ <dialog id="modal-default" class="mw-modal" closedby="any">
60
+ <div class="mw-modal-header">
61
+ <h4 class="mw-modal-title">Default Modal</h4>
62
+ <button class="mw-modal-close">&#120299;</button>
88
63
  </div>
89
- <div class="mw-modal-backdrop" onclick="closeModal('modal-default')"></div>
90
- </div>
64
+ <div class="mw-modal-body">
65
+ <p class="mw-text-muted">
66
+ Max-width <strong>520px</strong>. The default size - fits most use cases.
67
+ </p>
68
+ </div>
69
+ <div class="mw-modal-footer">
70
+ <button class="mw-modal-close mw-btn mw-btn-outline">Cancel</button>
71
+ <button class="mw-modal-close mw-btn mw-btn-primary">Confirm</button>
72
+ </div>
73
+ </dialog>
91
74
 
92
75
  <!-- Large Modal -->
93
- <div id="modal-lg" class="mw-modal-overlay">
94
- <div class="mw-modal mw-modal-lg">
95
- <div class="mw-modal-header">
96
- <h4 class="mw-modal-title">Large Modal</h4>
97
- <button onclick="closeModal('modal-lg')" class="mw-modal-close">
98
- &#120299;
99
- </button>
100
- </div>
101
- <div class="mw-modal-body">
102
- <p class="mw-text-muted">
103
- Max-width <strong>720px</strong>. More room for richer content or forms.
104
- </p>
105
- </div>
106
- <div class="mw-modal-footer">
107
- <button onclick="closeModal('modal-lg')" class="mw-btn mw-btn-outline">
108
- Cancel
109
- </button>
110
- <button onclick="closeModal('modal-lg')" class="mw-btn mw-btn-primary">
111
- Confirm
112
- </button>
113
- </div>
76
+ <dialog id="modal-lg" class="mw-modal mw-modal-lg" closedby="any">
77
+ <div class="mw-modal-header">
78
+ <h4 class="mw-modal-title">Large Modal</h4>
79
+ <button class="mw-modal-close">&#120299;</button>
114
80
  </div>
115
- <div class="mw-modal-backdrop" onclick="closeModal('modal-lg')"></div>
116
- </div>
81
+ <div class="mw-modal-body">
82
+ <p class="mw-text-muted">
83
+ Max-width <strong>720px</strong>. More room for richer content or forms.
84
+ </p>
85
+ </div>
86
+ <div class="mw-modal-footer">
87
+ <button class="mw-modal-close mw-btn mw-btn-outline">Cancel</button>
88
+ <button class="mw-modal-close mw-btn mw-btn-primary">Confirm</button>
89
+ </div>
90
+ </dialog>
117
91
 
118
92
  <!-- XL Modal -->
119
- <div id="modal-xl" class="mw-modal-overlay">
120
- <div class="mw-modal mw-modal-xl">
121
- <div class="mw-modal-header">
122
- <h4 class="mw-modal-title">XL Modal</h4>
123
- <button onclick="closeModal('modal-xl')" class="mw-modal-close">
124
- &#120299;
125
- </button>
126
- </div>
127
- <div class="mw-modal-body">
128
- <p class="mw-text-muted">
129
- Max-width <strong>960px</strong>. Use for detail views, data tables, or
130
- complex layouts.
131
- </p>
132
- </div>
133
- <div class="mw-modal-footer">
134
- <button onclick="closeModal('modal-xl')" class="mw-btn mw-btn-outline">
135
- Cancel
136
- </button>
137
- <button onclick="closeModal('modal-xl')" class="mw-btn mw-btn-primary">
138
- Confirm
139
- </button>
140
- </div>
93
+ <dialog id="modal-xl" class="mw-modal mw-modal-xl" closedby="any">
94
+ <div class="mw-modal-header">
95
+ <h4 class="mw-modal-title">XL Modal</h4>
96
+ <button class="mw-modal-close">&#120299;</button>
141
97
  </div>
142
- <div class="mw-modal-backdrop" onclick="closeModal('modal-xl')"></div>
143
- </div>
98
+ <div class="mw-modal-body">
99
+ <p class="mw-text-muted">
100
+ Max-width <strong>960px</strong>. Use for detail views, data tables, or
101
+ complex layouts.
102
+ </p>
103
+ </div>
104
+ <div class="mw-modal-footer">
105
+ <button class="mw-modal-close mw-btn mw-btn-outline">Cancel</button>
106
+ <button class="mw-modal-close mw-btn mw-btn-primary">Confirm</button>
107
+ </div>
108
+ </dialog>
144
109
 
145
110
  <!-- Form Modal -->
146
- <div id="modal-form" class="mw-modal-overlay">
147
- <div class="mw-modal mw-modal-lg">
148
- <div class="mw-modal-header">
149
- <h4 class="mw-modal-title">New Contact</h4>
150
- <button onclick="closeModal('modal-form')" class="mw-modal-close">
151
- &#120299;
152
- </button>
111
+ <dialog id="modal-form" class="mw-modal mw-modal-lg" closedby="any">
112
+ <div class="mw-modal-header">
113
+ <h4 class="mw-modal-title">New Contact</h4>
114
+ <button class="mw-modal-close">&#120299;</button>
115
+ </div>
116
+ <div class="mw-modal-body">
117
+ <div class="mw-grid-2 mw-mb-3">
118
+ <label for="modal-firstname">
119
+ <span>First Name</span>
120
+ <input
121
+ id="modal-firstname"
122
+ type="text"
123
+ class="mw-input"
124
+ placeholder="Jane"
125
+ />
126
+ </label>
127
+ <label for="modal-lastname">
128
+ <span>Last Name</span>
129
+ <input
130
+ id="modal-lastname"
131
+ type="text"
132
+ class="mw-input"
133
+ placeholder="Doe"
134
+ />
135
+ </label>
153
136
  </div>
154
- <div class="mw-modal-body">
155
- <div class="mw-grid-2 mw-mb-3">
156
- <label for="modal-firstname">
157
- <span>First Name</span>
158
- <input
159
- id="modal-firstname"
160
- type="text"
161
- class="mw-input"
162
- placeholder="Jane"
163
- />
164
- </label>
165
- <label for="modal-lastname">
166
- <span>Last Name</span>
167
- <input
168
- id="modal-lastname"
169
- type="text"
170
- class="mw-input"
171
- placeholder="Doe"
172
- />
173
- </label>
174
- </div>
175
- <div class="mw-mb-3">
176
- <label for="modal-email">
177
- <span>Email</span>
178
- <input
179
- id="modal-email"
180
- type="email"
181
- class="mw-input"
182
- placeholder="jane@example.com"
183
- />
184
- </label>
185
- </div>
186
- <div class="mw-mb-3">
187
- <label for="modal-role">
188
- <span>Role</span>
189
- <select id="modal-role" class="mw-select">
190
- <option value="" disabled selected>Select a role</option>
191
- <option>Developer</option>
192
- <option>Designer</option>
193
- <option>Product Manager</option>
194
- <option>Other</option>
195
- </select>
196
- </label>
197
- </div>
198
- <div class="mw-mb-3">
199
- <label for="modal-notes">
200
- <span>Notes</span>
201
- <textarea
202
- id="modal-notes"
203
- rows="3"
204
- class="mw-textarea"
205
- placeholder="Optional notes..."
206
- ></textarea>
207
- </label>
208
- </div>
209
- <label class="mw-toggle">
210
- <input type="checkbox" />
211
- <div class="mw-toggle-track"></div>
212
- <span class="mw-toggle-label">Send welcome email</span>
137
+ <div class="mw-mb-3">
138
+ <label for="modal-email">
139
+ <span>Email</span>
140
+ <input
141
+ id="modal-email"
142
+ type="email"
143
+ class="mw-input"
144
+ placeholder="jane@example.com"
145
+ />
213
146
  </label>
214
147
  </div>
215
- <div class="mw-modal-footer">
216
- <button onclick="closeModal('modal-form')" class="mw-btn mw-btn-outline">
217
- Cancel
218
- </button>
219
- <button onclick="closeModal('modal-form')" class="mw-btn mw-btn-primary">
220
- Save Contact
221
- </button>
148
+ <div class="mw-mb-3">
149
+ <label for="modal-role">
150
+ <span>Role</span>
151
+ <select id="modal-role" class="mw-select">
152
+ <option value="" disabled selected>Select a role</option>
153
+ <option>Developer</option>
154
+ <option>Designer</option>
155
+ <option>Product Manager</option>
156
+ <option>Other</option>
157
+ </select>
158
+ </label>
222
159
  </div>
160
+ <div class="mw-mb-3">
161
+ <label for="modal-notes">
162
+ <span>Notes</span>
163
+ <textarea
164
+ id="modal-notes"
165
+ rows="3"
166
+ class="mw-textarea"
167
+ placeholder="Optional notes..."
168
+ ></textarea>
169
+ </label>
170
+ </div>
171
+ <label class="mw-toggle">
172
+ <input type="checkbox" />
173
+ <div class="mw-toggle-track"></div>
174
+ <span class="mw-toggle-label">Send welcome email</span>
175
+ </label>
223
176
  </div>
224
- <div class="mw-modal-backdrop" onclick="closeModal('modal-form')"></div>
225
- </div>
177
+ <div class="mw-modal-footer">
178
+ <button class="mw-modal-close mw-btn mw-btn-outline">Cancel</button>
179
+ <button class="mw-modal-close mw-btn mw-btn-primary">Save Contact</button>
180
+ </div>
181
+ </dialog>
226
182
 
227
183
  <!-- Confirmation Modal -->
228
- <!-- Same classes, a <dialog> instead of a div: Escape, the focus trap, the
229
- inert page behind it and the scroll lock all come from the element. The
230
- close buttons need no onclick - the framework script closes the dialog
231
- they sit in - and closedby="any" is what dismisses it on a backdrop
232
- click, so there is no mw-modal-backdrop either. -->
233
184
  <dialog id="modal-confirm" class="mw-modal mw-modal-sm" closedby="any">
234
185
  <div class="mw-modal-header">
235
186
  <h4 class="mw-modal-title">Delete Item</h4>
@@ -59,7 +59,14 @@
59
59
  the picture, not on a theme surface, and the light theme's shadows are cut for
60
60
  paper and vanish there. Pair it with <code>mw-header-reveal</code> on the
61
61
  header and the page opens on the picture alone: the bar stays away for the
62
- first 130px of scroll and has ridden in by 420px.
62
+ first 130px of scroll and has ridden in by 420px. A control marked
63
+ <code>mw-header-keep</code> sits that ride out - it travels the bar's distance
64
+ the other way and waits at the position it will hold once the bar is there, so
65
+ a login button is on the picture from the first frame and nothing jumps at the
66
+ end. On a narrow screen it is how the burger reaches the reader before the bar
67
+ exists. While it floats it carries a ring and a soft shadow so a dark control
68
+ still reads on a dark picture, and focus leaves the bar alone - it is on
69
+ screen already, so a click on the theme toggle no longer hauls the header in.
63
70
  </p>
64
71
  <p class="mw-text-muted mw-text-measure mw-mb-6">
65
72
  Add <code>mw-parallax-rise</code> next to it and the picture lifts by
@@ -119,7 +126,11 @@
119
126
  <span class="mw-code-block-language">HTML</span>
120
127
  </div>
121
128
  <div class="mw-code-container">
122
- <pre><code>&lt;header class="mw-header mw-header-reveal"&gt;...&lt;/header&gt;
129
+ <pre><code>&lt;header class="mw-header mw-header-reveal"&gt;
130
+ ...
131
+ &lt;button class="mw-login-btn mw-header-keep"&gt;...&lt;/button&gt;
132
+ &lt;button class="mw-menu-btn mw-header-keep"&gt;...&lt;/button&gt;
133
+ &lt;/header&gt;
123
134
 
124
135
  &lt;main class="mw-main"&gt;
125
136
  &lt;section class="mw-section mw-parallax-sticky mw-parallax-rise"&gt;
@@ -1,4 +1,5 @@
1
1
  @use 'sass:map';
2
+ @use 'sass:math';
2
3
  @use 'variables' as v;
3
4
  @use 'functions' as *;
4
5
 
@@ -171,15 +172,27 @@
171
172
  // Grows the hit area without touching the silhouette - for a control whose size
172
173
  // is the design: a 9px gallery dot, an 18px mini button. Neighbours need a gap
173
174
  // of at least twice $grow, or two hit areas overlap and a tap lands on the one
174
- // next door. Takes ::after, so it is out for anything carrying a `data-tooltip`.
175
- @mixin hit-area($grow: 6px) {
175
+ // next door.
176
+ //
177
+ // A `data-tooltip` owns both pseudo-elements, so the overlay has nowhere to go
178
+ // and used to land on the arrow, leaving its paint on the trigger as a grey
179
+ // diamond. The selector carries that exclusion rather than a note to callers,
180
+ // who apply this to a class and cannot see which instance has the attribute.
181
+ // Those grow the box instead - it costs the silhouette, but of the two only the
182
+ // target is load-bearing under a thumb.
183
+ @mixin hit-area($grow: 6px, $box: 1.5rem) {
176
184
  position: relative;
177
185
 
178
- &::after {
186
+ &:not([data-tooltip])::after {
179
187
  content: '';
180
188
  position: absolute;
181
189
  inset: -#{$grow};
182
190
  }
191
+
192
+ &[data-tooltip] {
193
+ min-width: $box;
194
+ min-height: $box;
195
+ }
183
196
  }
184
197
 
185
198
  // One shape for every scroll container and the page itself
@@ -287,6 +300,19 @@
287
300
  $_corner-stretch: 1.5;
288
301
  $_corner-opaque: 55%;
289
302
 
303
+ // `surface(0)` is the natural way to say "no border to subtract", but a unitless
304
+ // zero makes every `calc(<length> - 0)` below invalid - and an invalid calc takes
305
+ // its whole declaration with it. Losing the radius and the mask leaves the accent
306
+ // as a plain 2px border all the way round the box, which is how a modal ended up
307
+ // framed instead of cornered.
308
+ @function _accent-width($value) {
309
+ @if math.is-unitless($value) {
310
+ @return $value * 1px;
311
+ }
312
+
313
+ @return $value;
314
+ }
315
+
290
316
  // What is left of the signature once the silhouette is switched off: the same
291
317
  // arc, on one corner, thinner and over a shorter run. The two-corner shape is
292
318
  // the loud half and mw-corners-even exists to turn it off - but a surface with
@@ -297,7 +323,8 @@ $_corner-opaque: 55%;
297
323
  // it follows --mw-radius-scale down to a square, where the arc becomes a
298
324
  // bracket in the corner rather than disappearing.
299
325
  @mixin corner-mark($border-width: 1px) {
300
- $r: max(0px, calc(#{radius('md')} - #{$border-width}));
326
+ $width: _accent-width($border-width);
327
+ $r: max(0px, calc(#{radius('md')} - #{$width}));
301
328
 
302
329
  content: '';
303
330
  position: absolute;
@@ -319,8 +346,9 @@ $_corner-opaque: 55%;
319
346
  // max(), not a bare subtraction: --mw-radius-scale can take the radius to 0,
320
347
  // and while a negative border-radius is clamped for us, a negative circle in
321
348
  // the mask below would throw the whole gradient away and lose the arc.
322
- $sharp: max(0px, calc(#{radius-sharp()} - #{$border-width}));
323
- $round: max(0px, calc(#{radius('xl')} - #{$border-width}));
349
+ $width: _accent-width($border-width);
350
+ $sharp: max(0px, calc(#{radius-sharp()} - #{$width}));
351
+ $round: max(0px, calc(#{radius('xl')} - #{$width}));
324
352
  // Opaque to where the curve ends, fading from there, so the arc runs into the
325
353
  // straight edge instead of stopping at the tangent. 1.8 is bounded by the
326
354
  // shortest surface: past about 2 the two arcs meet on a 50px box and read as
@@ -146,6 +146,12 @@
146
146
  padding: spacing('0');
147
147
  border: none;
148
148
  color: inherit;
149
+ // `.mw-modal` carries `position: relative` from surface(), which is what its
150
+ // corner accent is drawn against - and being an author rule it beats the UA's
151
+ // `dialog:modal { position: fixed }`. The dialog then centres itself in the
152
+ // document rather than the viewport, so on a long page it opens somewhere far
153
+ // above the fold with only its backdrop to show for it.
154
+ position: fixed;
149
155
  // In the top layer there is nothing left to sit above
150
156
  z-index: auto;
151
157
  // The UA centres a dialog with auto margins; these keep that while giving
@@ -267,8 +273,13 @@
267
273
  justify-content: flex-end;
268
274
  }
269
275
 
270
- // Close button
271
- .mw-modal-close {
276
+ // Close button. `:not(.mw-btn)` because the class is two things at once: the
277
+ // hook main.js closes a dialog by, and the look of the X in the header. A
278
+ // footer button that dismisses carries both it and `mw-btn mw-btn-primary` -
279
+ // and this rule sits after the button styles in the same layer at the same
280
+ // specificity, so without the guard it wins and paints the primary action in
281
+ // `background: none`.
282
+ .mw-modal-close:not(.mw-btn) {
272
283
  background: none;
273
284
  font-size: font-size('md');
274
285
  cursor: pointer;