tuffgal 0.1.0-alpha.9 → 0.2.0-alpha.2
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/CHANGELOG.md +149 -2
- package/README.md +78 -3
- package/dist/cli.d.ts +50 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +204 -18
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +11 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/config.d.ts +38 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +20 -1
- package/dist/config.js.map +1 -1
- package/dist/coverage/screens.d.ts +11 -6
- package/dist/coverage/screens.d.ts.map +1 -1
- package/dist/coverage/screens.js +12 -7
- package/dist/coverage/screens.js.map +1 -1
- package/dist/reporter/assets/report.css +415 -51
- package/dist/reporter/assets/report.js +502 -68
- package/dist/reporter/template.d.ts +8 -1
- package/dist/reporter/template.d.ts.map +1 -1
- package/dist/reporter/template.js +517 -85
- package/dist/reporter/template.js.map +1 -1
- package/dist/reporter/writeReport.d.ts +2 -2
- package/dist/reporter/writeReport.d.ts.map +1 -1
- package/dist/reporter/writeReport.js +3 -3
- package/dist/reporter/writeReport.js.map +1 -1
- package/dist/runner/approve.d.ts +34 -9
- package/dist/runner/approve.d.ts.map +1 -1
- package/dist/runner/approve.js +54 -10
- package/dist/runner/approve.js.map +1 -1
- package/dist/runner/approveFrom.d.ts +58 -0
- package/dist/runner/approveFrom.d.ts.map +1 -0
- package/dist/runner/approveFrom.js +384 -0
- package/dist/runner/approveFrom.js.map +1 -0
- package/dist/runner/breakpointPasses.d.ts +39 -0
- package/dist/runner/breakpointPasses.d.ts.map +1 -0
- package/dist/runner/breakpointPasses.js +107 -0
- package/dist/runner/breakpointPasses.js.map +1 -0
- package/dist/runner/bridges/database.d.ts +4 -3
- package/dist/runner/bridges/database.d.ts.map +1 -1
- package/dist/runner/bridges/database.js +4 -3
- package/dist/runner/bridges/database.js.map +1 -1
- package/dist/runner/exitCode.d.ts +34 -0
- package/dist/runner/exitCode.d.ts.map +1 -0
- package/dist/runner/exitCode.js +41 -0
- package/dist/runner/exitCode.js.map +1 -0
- package/dist/runner/manifest.d.ts +149 -0
- package/dist/runner/manifest.d.ts.map +1 -0
- package/dist/runner/manifest.js +229 -0
- package/dist/runner/manifest.js.map +1 -0
- package/dist/runner/mode.d.ts +41 -0
- package/dist/runner/mode.d.ts.map +1 -0
- package/dist/runner/mode.js +43 -0
- package/dist/runner/mode.js.map +1 -0
- package/dist/runner/orphanScan.d.ts +68 -0
- package/dist/runner/orphanScan.d.ts.map +1 -0
- package/dist/runner/orphanScan.js +150 -0
- package/dist/runner/orphanScan.js.map +1 -0
- package/dist/runner/run.d.ts +85 -1
- package/dist/runner/run.d.ts.map +1 -1
- package/dist/runner/run.js +267 -41
- package/dist/runner/run.js.map +1 -1
- package/dist/runner/runAction.d.ts +16 -0
- package/dist/runner/runAction.d.ts.map +1 -1
- package/dist/runner/runAction.js +112 -13
- package/dist/runner/runAction.js.map +1 -1
- package/dist/runner/runStory.d.ts +15 -4
- package/dist/runner/runStory.d.ts.map +1 -1
- package/dist/runner/runStory.js +41 -19
- package/dist/runner/runStory.js.map +1 -1
- package/dist/runner/storyFilter.d.ts +8 -0
- package/dist/runner/storyFilter.d.ts.map +1 -1
- package/dist/runner/storyFilter.js +11 -0
- package/dist/runner/storyFilter.js.map +1 -1
- package/dist/schema/action.d.ts +11 -11
- package/dist/schema/result.d.ts +125 -6
- package/dist/schema/result.d.ts.map +1 -1
- package/dist/schema/result.js.map +1 -1
- package/dist/screenshots/baselineStore.d.ts +92 -10
- package/dist/screenshots/baselineStore.d.ts.map +1 -1
- package/dist/screenshots/baselineStore.js +102 -18
- package/dist/screenshots/baselineStore.js.map +1 -1
- package/dist/screenshots/capture.d.ts +10 -5
- package/dist/screenshots/capture.d.ts.map +1 -1
- package/dist/screenshots/capture.js +31 -18
- package/dist/screenshots/capture.js.map +1 -1
- package/package.json +1 -1
|
@@ -57,6 +57,15 @@
|
|
|
57
57
|
box-sizing: border-box;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/* The story filter prunes stories, breakpoint groups, and actions by toggling
|
|
61
|
+
the native `hidden` attribute. Those are layout containers, so a future
|
|
62
|
+
`display:` rule on `.action`/`.breakpoint-group` would outrank the UA
|
|
63
|
+
`[hidden]` default and silently resurrect filtered rows as visible and
|
|
64
|
+
keyboard-focusable. Pin it so `hidden` always wins. */
|
|
65
|
+
[hidden] {
|
|
66
|
+
display: none !important;
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
html {
|
|
61
70
|
-webkit-font-smoothing: antialiased;
|
|
62
71
|
-moz-osx-font-smoothing: grayscale;
|
|
@@ -144,14 +153,59 @@ h2 {
|
|
|
144
153
|
text-wrap: balance;
|
|
145
154
|
}
|
|
146
155
|
|
|
147
|
-
|
|
156
|
+
/* Environment-mismatch banner, first inside <main>. The "warning" meaning is
|
|
157
|
+
carried by the heading word + body copy, never by color alone (1.4.1). Body
|
|
158
|
+
text stays --text (≥4.5:1); the red accent lives only on the left border (a
|
|
159
|
+
non-text ≥3:1 cue, mirroring .action-error). The list of diverging keys reads
|
|
160
|
+
as monospace <code> tokens. */
|
|
161
|
+
.env-mismatch {
|
|
148
162
|
margin-top: 0.5rem;
|
|
163
|
+
padding: 0.75rem 1rem;
|
|
164
|
+
background: var(--bg-elevated);
|
|
165
|
+
border: 1px solid var(--border);
|
|
166
|
+
border-left: 3px solid var(--failed);
|
|
149
167
|
}
|
|
150
168
|
|
|
151
|
-
.
|
|
169
|
+
.env-mismatch h2 {
|
|
170
|
+
margin-top: 0.25rem;
|
|
171
|
+
color: var(--changed);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.env-mismatch p {
|
|
175
|
+
margin: 0.5rem 0;
|
|
176
|
+
color: var(--text);
|
|
177
|
+
font-family: var(--font-sans);
|
|
178
|
+
font-size: 0.875rem;
|
|
179
|
+
text-wrap: pretty;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.env-mismatch ul {
|
|
152
183
|
display: flex;
|
|
153
184
|
flex-wrap: wrap;
|
|
154
|
-
gap:
|
|
185
|
+
gap: 0.5rem;
|
|
186
|
+
margin: 0.5rem 0 0;
|
|
187
|
+
padding: 0;
|
|
188
|
+
list-style: none;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.env-mismatch li {
|
|
192
|
+
color: var(--changed);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Deleted (orphaned-baseline) section, a peer landmark after the stories. */
|
|
196
|
+
.deleted {
|
|
197
|
+
margin-top: 0.5rem;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.deleted p {
|
|
201
|
+
margin: 0 0 0.5rem;
|
|
202
|
+
color: var(--text-muted);
|
|
203
|
+
font-family: var(--font-sans);
|
|
204
|
+
font-size: 0.875rem;
|
|
205
|
+
text-wrap: pretty;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.deleted ul {
|
|
155
209
|
margin: 0;
|
|
156
210
|
padding: 0.75rem 1rem;
|
|
157
211
|
list-style: none;
|
|
@@ -159,8 +213,24 @@ h2 {
|
|
|
159
213
|
border: 1px solid var(--border);
|
|
160
214
|
}
|
|
161
215
|
|
|
162
|
-
.
|
|
163
|
-
|
|
216
|
+
.deleted li {
|
|
217
|
+
padding: 0.15rem 0;
|
|
218
|
+
color: var(--text);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.summary {
|
|
222
|
+
margin-top: 0.5rem;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.summary-list {
|
|
226
|
+
display: flex;
|
|
227
|
+
flex-wrap: wrap;
|
|
228
|
+
gap: 0.5rem;
|
|
229
|
+
margin: 0;
|
|
230
|
+
padding: 0.75rem 1rem;
|
|
231
|
+
list-style: none;
|
|
232
|
+
background: var(--bg-elevated);
|
|
233
|
+
border: 1px solid var(--border);
|
|
164
234
|
}
|
|
165
235
|
|
|
166
236
|
.summary-item {
|
|
@@ -172,27 +242,206 @@ h2 {
|
|
|
172
242
|
|
|
173
243
|
.summary-item .count {
|
|
174
244
|
font-weight: 600;
|
|
245
|
+
color: var(--text-subtle);
|
|
175
246
|
}
|
|
176
247
|
|
|
177
|
-
|
|
178
|
-
|
|
248
|
+
/* Each status total is a single-select filter styled as a colorful, button-like
|
|
249
|
+
pill keyed to its status accent (aligned to the .chip idiom). Its sibling count
|
|
250
|
+
precedes it outside the button. Default (aria-pressed=false) is an OUTLINE pill:
|
|
251
|
+
transparent background, status-colored label + 1px status-colored border. Hover
|
|
252
|
+
adds a faint status tint; pressed becomes a FILLED pill (status background,
|
|
253
|
+
paired --*-fg label) with bold weight. The 1px solid status border on every
|
|
254
|
+
state guarantees a perceivable non-text edge (1.4.11); label/fg pairings clear
|
|
255
|
+
4.5:1 on --bg-elevated in both themes (1.4.3). Pressed is never color-alone
|
|
256
|
+
(1.4.1): it is the only filled + bold pill, readable without hue. Target size
|
|
257
|
+
≥24×24 via min-height/min-width (2.5.8). Stays a <button>: it filters in place,
|
|
258
|
+
never navigates. */
|
|
259
|
+
.summary-filter {
|
|
260
|
+
display: inline-flex;
|
|
261
|
+
align-items: center;
|
|
262
|
+
gap: 0.5rem;
|
|
263
|
+
min-height: 24px;
|
|
264
|
+
min-width: 24px;
|
|
265
|
+
padding: 0.2rem 0.6rem;
|
|
266
|
+
line-height: 1.2;
|
|
267
|
+
border: 1px solid var(--border-bright);
|
|
268
|
+
border-radius: 999px;
|
|
269
|
+
background: transparent;
|
|
270
|
+
color: var(--text);
|
|
271
|
+
font: inherit;
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
transition-property: scale, color, background-color, border-color;
|
|
274
|
+
transition-duration: 0.15s;
|
|
275
|
+
transition-timing-function: ease;
|
|
179
276
|
}
|
|
180
277
|
|
|
181
|
-
.summary-
|
|
278
|
+
.summary-filter[aria-pressed='true'] {
|
|
279
|
+
cursor: default;
|
|
280
|
+
user-select: none;
|
|
281
|
+
font-weight: 600;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.summary-filter:active {
|
|
285
|
+
scale: 0.96;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* Status pills: OUTLINE by default (status label + status border, transparent
|
|
289
|
+
bg), faint status tint on hover, FILLED on pressed (status bg + paired --*-fg
|
|
290
|
+
label). The "all" total stays NEUTRAL on every state: --accent equals --new, so
|
|
291
|
+
tinting "all" blue would falsely imply the "new" filter. Status label + border
|
|
292
|
+
clear their contrast floors on --bg-elevated in both themes; each status token
|
|
293
|
+
paired with its --*-fg fill clears 4.5:1. */
|
|
294
|
+
.summary-item[data-status='pass'] .summary-filter {
|
|
182
295
|
color: var(--pass);
|
|
296
|
+
border-color: var(--pass);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.summary-item[data-status='pass'] .summary-filter:hover {
|
|
300
|
+
background: color-mix(in srgb, var(--pass) 15%, transparent);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.summary-item[data-status='pass'] .summary-filter[aria-pressed='true'] {
|
|
304
|
+
background: var(--pass);
|
|
305
|
+
color: var(--pass-fg);
|
|
183
306
|
}
|
|
184
307
|
|
|
185
|
-
.summary-item[data-status='changed'] .
|
|
308
|
+
.summary-item[data-status='changed'] .summary-filter {
|
|
186
309
|
color: var(--changed);
|
|
310
|
+
border-color: var(--changed);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.summary-item[data-status='changed'] .summary-filter:hover {
|
|
314
|
+
background: color-mix(in srgb, var(--changed) 15%, transparent);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.summary-item[data-status='changed'] .summary-filter[aria-pressed='true'] {
|
|
318
|
+
background: var(--changed);
|
|
319
|
+
color: var(--changed-fg);
|
|
187
320
|
}
|
|
188
321
|
|
|
189
|
-
.summary-item[data-status='failed'] .
|
|
322
|
+
.summary-item[data-status='failed'] .summary-filter {
|
|
190
323
|
color: var(--failed);
|
|
324
|
+
border-color: var(--failed);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.summary-item[data-status='failed'] .summary-filter:hover {
|
|
328
|
+
background: color-mix(in srgb, var(--failed) 15%, transparent);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.summary-item[data-status='failed'] .summary-filter[aria-pressed='true'] {
|
|
332
|
+
background: var(--failed);
|
|
333
|
+
color: var(--failed-fg);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.summary-item[data-status='new'] .summary-filter {
|
|
337
|
+
color: var(--new);
|
|
338
|
+
border-color: var(--new);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.summary-item[data-status='new'] .summary-filter:hover {
|
|
342
|
+
background: color-mix(in srgb, var(--new) 15%, transparent);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.summary-item[data-status='new'] .summary-filter[aria-pressed='true'] {
|
|
346
|
+
background: var(--new);
|
|
347
|
+
color: var(--new-fg);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* "all" stays NEUTRAL — never a status color, never blue. Pressed is a filled
|
|
351
|
+
neutral pill (it is pressed-by-default at load, so this is the resting view). */
|
|
352
|
+
.summary-item[data-status='all'] .summary-filter {
|
|
353
|
+
color: var(--text);
|
|
354
|
+
border-color: var(--border-bright);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.summary-item[data-status='all'] .summary-filter[aria-pressed='true'] {
|
|
358
|
+
background: var(--bg);
|
|
359
|
+
color: var(--text);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.summary-filter:focus-visible {
|
|
363
|
+
outline: 2px solid var(--accent);
|
|
364
|
+
outline-offset: 2px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* Breakpoint filter — the SECOND, independent single-select filter dimension,
|
|
368
|
+
rendered below the status totals inside the summary region (only for
|
|
369
|
+
multi-breakpoint runs). A role="group" row of neutral pills; report.js ANDs it
|
|
370
|
+
with the status filter. The row sits on the page --bg with a top gap that also
|
|
371
|
+
guarantees ≥24px vertical spacing between the two pill rows (2.5.8). */
|
|
372
|
+
.breakpoint-filters {
|
|
373
|
+
display: flex;
|
|
374
|
+
flex-wrap: wrap;
|
|
375
|
+
gap: 0.5rem;
|
|
376
|
+
margin-top: 0.75rem;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/* Neutral pills — no status hue, so the two filter rows read as siblings by
|
|
380
|
+
geometry, not color. Same pill geometry/radius as the status filter.
|
|
381
|
+
Default is an OUTLINE pill: transparent bg, --text-muted border (≥3:1 vs --bg
|
|
382
|
+
in both themes → 1.4.11), --text label (≥4.5:1 → 1.4.3). Hover brightens the
|
|
383
|
+
border + adds a faint neutral tint. Pressed is a FILLED pill: --text fill,
|
|
384
|
+
--bg label, bold weight — the ONLY filled + bold pill, so the pressed state is
|
|
385
|
+
never conveyed by color alone (1.4.1); the inverse fill clears 4.5:1 in both
|
|
386
|
+
themes. Focus ring is --accent 2px / offset 2px, painted on the page --bg
|
|
387
|
+
outside the pill — identical to the status filter. Target size ≥24×24 via
|
|
388
|
+
min-height/min-width (2.5.8). Stays a <button>: it filters in place. */
|
|
389
|
+
.breakpoint-filter {
|
|
390
|
+
display: inline-flex;
|
|
391
|
+
align-items: center;
|
|
392
|
+
gap: 0.5rem;
|
|
393
|
+
min-height: 24px;
|
|
394
|
+
min-width: 24px;
|
|
395
|
+
padding: 0.2rem 0.6rem;
|
|
396
|
+
line-height: 1.2;
|
|
397
|
+
border: 1px solid var(--text-muted);
|
|
398
|
+
border-radius: 999px;
|
|
399
|
+
background: transparent;
|
|
400
|
+
color: var(--text);
|
|
401
|
+
font: inherit;
|
|
402
|
+
cursor: pointer;
|
|
403
|
+
transition-property: scale, color, background-color, border-color;
|
|
404
|
+
transition-duration: 0.15s;
|
|
405
|
+
transition-timing-function: ease;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.breakpoint-filter .breakpoint-name {
|
|
409
|
+
color: inherit;
|
|
410
|
+
font-weight: inherit;
|
|
411
|
+
text-transform: none;
|
|
191
412
|
}
|
|
192
413
|
|
|
193
|
-
.
|
|
414
|
+
.breakpoint-filter .breakpoint-dimensions {
|
|
194
415
|
color: var(--text-subtle);
|
|
195
|
-
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.breakpoint-filter:hover:not([aria-pressed='true']) {
|
|
419
|
+
border-color: var(--text);
|
|
420
|
+
background: color-mix(in srgb, var(--text) 8%, transparent);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.breakpoint-filter[aria-pressed='true'] {
|
|
424
|
+
cursor: default;
|
|
425
|
+
user-select: none;
|
|
426
|
+
font-weight: 600;
|
|
427
|
+
background: var(--text);
|
|
428
|
+
color: var(--bg);
|
|
429
|
+
border-color: var(--text);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/* The pressed pill's dimensions ride the inverse fill: --bg on --text is the
|
|
433
|
+
same ≥4.5:1 pair as the label, so they stay legible. */
|
|
434
|
+
.breakpoint-filter[aria-pressed='true'] .breakpoint-dimensions {
|
|
435
|
+
color: color-mix(in srgb, var(--bg) 65%, var(--text));
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.breakpoint-filter:active {
|
|
439
|
+
scale: 0.96;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.breakpoint-filter:focus-visible {
|
|
443
|
+
outline: 2px solid var(--accent);
|
|
444
|
+
outline-offset: 2px;
|
|
196
445
|
}
|
|
197
446
|
|
|
198
447
|
.chip {
|
|
@@ -236,64 +485,72 @@ h2 {
|
|
|
236
485
|
}
|
|
237
486
|
}
|
|
238
487
|
|
|
239
|
-
|
|
488
|
+
/* The expand-all / collapse-all pair now lives at the right end of the summary
|
|
489
|
+
row (the slot the coverage stats vacated), pushed there by margin-left:auto
|
|
490
|
+
while staying the LAST flex children so DOM order matches the visual order. */
|
|
491
|
+
.story-bulk-toggle {
|
|
240
492
|
display: flex;
|
|
241
|
-
flex-wrap: wrap;
|
|
242
493
|
align-items: center;
|
|
243
|
-
gap: 0.
|
|
244
|
-
margin:
|
|
494
|
+
gap: 0.25rem;
|
|
495
|
+
margin-left: auto;
|
|
496
|
+
line-height: 24px;
|
|
497
|
+
font-size: 0.8125rem;
|
|
245
498
|
}
|
|
246
499
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
500
|
+
/* Link-styled verbs ("Expand" / "Collapse") — still <button>s, they act in
|
|
501
|
+
place. The underline is the affordance; verbs exceed 24px wide intrinsically,
|
|
502
|
+
so min-height alone carries 2.5.8. */
|
|
503
|
+
.story-bulk-toggle-button {
|
|
504
|
+
display: inline-flex;
|
|
250
505
|
align-items: center;
|
|
251
|
-
|
|
252
|
-
margin: 0;
|
|
506
|
+
min-height: 24px;
|
|
253
507
|
padding: 0;
|
|
508
|
+
background: none;
|
|
254
509
|
border: 0;
|
|
510
|
+
color: var(--text);
|
|
511
|
+
font: inherit;
|
|
512
|
+
cursor: pointer;
|
|
513
|
+
vertical-align: middle;
|
|
514
|
+
text-decoration: underline;
|
|
515
|
+
text-decoration-thickness: 1px;
|
|
516
|
+
text-underline-offset: 4.5px;
|
|
255
517
|
}
|
|
256
518
|
|
|
257
|
-
.story-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
font-size: 0.75rem;
|
|
261
|
-
letter-spacing: 0.05em;
|
|
262
|
-
text-transform: uppercase;
|
|
519
|
+
.story-bulk-toggle-button:hover:not(:disabled) {
|
|
520
|
+
color: var(--accent);
|
|
521
|
+
text-decoration-thickness: 2px;
|
|
263
522
|
}
|
|
264
523
|
|
|
265
|
-
.story-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
pointer-events: none;
|
|
524
|
+
.story-bulk-toggle-button:focus-visible {
|
|
525
|
+
outline: 2px solid var(--accent);
|
|
526
|
+
outline-offset: 1px;
|
|
269
527
|
}
|
|
270
528
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
529
|
+
/* Disabled verb: removing the underline is the color-independent cue (no
|
|
530
|
+
opacity — it can compound below the contrast floor). Disabled controls are
|
|
531
|
+
1.4.3/1.4.11 exempt, but the state stays perceptible without color. */
|
|
532
|
+
.story-bulk-toggle-button:disabled {
|
|
533
|
+
color: var(--text-subtle);
|
|
534
|
+
text-decoration: none;
|
|
535
|
+
cursor: not-allowed;
|
|
276
536
|
}
|
|
277
537
|
|
|
278
|
-
.
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
font-size: 0.8125rem;
|
|
282
|
-
min-height: 24px;
|
|
538
|
+
.bulk-scope,
|
|
539
|
+
.bulk-sep {
|
|
540
|
+
color: var(--text-subtle);
|
|
283
541
|
}
|
|
284
542
|
|
|
285
|
-
.story-bulk-toggle-
|
|
286
|
-
|
|
287
|
-
border-color: var(--accent);
|
|
543
|
+
.summary-item:has(+ .story-bulk-toggle) .bulk-sep {
|
|
544
|
+
display: none;
|
|
288
545
|
}
|
|
289
546
|
|
|
290
|
-
.
|
|
291
|
-
|
|
292
|
-
|
|
547
|
+
.bulk-scope {
|
|
548
|
+
cursor: default;
|
|
549
|
+
margin-inline-start: 0.25rem;
|
|
293
550
|
}
|
|
294
551
|
|
|
295
552
|
.story-filter-status {
|
|
296
|
-
margin: 0;
|
|
553
|
+
margin: 0 0 0.75rem;
|
|
297
554
|
color: var(--text-subtle);
|
|
298
555
|
font-size: 0.8125rem;
|
|
299
556
|
}
|
|
@@ -331,6 +588,10 @@ h2 {
|
|
|
331
588
|
box-shadow: inset 3px 0 0 var(--failed);
|
|
332
589
|
}
|
|
333
590
|
|
|
591
|
+
.story[data-status='new'] {
|
|
592
|
+
box-shadow: inset 3px 0 0 var(--new);
|
|
593
|
+
}
|
|
594
|
+
|
|
334
595
|
.story[data-status='changed'] {
|
|
335
596
|
box-shadow: inset 3px 0 0 var(--changed);
|
|
336
597
|
}
|
|
@@ -357,6 +618,11 @@ h2 {
|
|
|
357
618
|
color: var(--pass);
|
|
358
619
|
}
|
|
359
620
|
|
|
621
|
+
.story[data-status='new'] .story-marker,
|
|
622
|
+
.story[data-status='new'] .story-file {
|
|
623
|
+
color: var(--new);
|
|
624
|
+
}
|
|
625
|
+
|
|
360
626
|
.story[data-status='changed'] .story-marker,
|
|
361
627
|
.story[data-status='changed'] .story-file {
|
|
362
628
|
color: var(--changed);
|
|
@@ -380,7 +646,9 @@ h2 {
|
|
|
380
646
|
text-wrap: pretty;
|
|
381
647
|
}
|
|
382
648
|
|
|
383
|
-
|
|
649
|
+
/* `:not([hidden])` on both sides so a filter that hides the first group does
|
|
650
|
+
not strand its separator on the new first-visible group. */
|
|
651
|
+
.breakpoint-group:not([hidden]) + .breakpoint-group:not([hidden]) {
|
|
384
652
|
margin-top: 0.75rem;
|
|
385
653
|
padding-top: 0.75rem;
|
|
386
654
|
border-top: 1px dashed var(--border);
|
|
@@ -509,6 +777,28 @@ h2 {
|
|
|
509
777
|
overflow-x: auto;
|
|
510
778
|
}
|
|
511
779
|
|
|
780
|
+
/* Supplementary per-action notes (candidate / a11y-drift). Both plain prose
|
|
781
|
+
inside li.action; an a11y-only changed row stacks both. Indented to align
|
|
782
|
+
under the action name (past the status badge), muted like other secondary
|
|
783
|
+
prose. Stacked cleanly with a small gap when both render. */
|
|
784
|
+
.candidate-note,
|
|
785
|
+
.a11y-drift-note {
|
|
786
|
+
margin: 0.35rem 0 0 2.2rem;
|
|
787
|
+
color: var(--text-muted);
|
|
788
|
+
font-family: var(--font-sans);
|
|
789
|
+
font-size: 0.8125rem;
|
|
790
|
+
text-wrap: pretty;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
.candidate-note + .a11y-drift-note {
|
|
794
|
+
margin-top: 0.15rem;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
.a11y-drift-note code {
|
|
798
|
+
font-family: var(--font-mono);
|
|
799
|
+
color: var(--text);
|
|
800
|
+
}
|
|
801
|
+
|
|
512
802
|
summary.action-row {
|
|
513
803
|
list-style: none;
|
|
514
804
|
cursor: pointer;
|
|
@@ -629,13 +919,87 @@ summary.action-row:focus-visible {
|
|
|
629
919
|
color: var(--text-subtle);
|
|
630
920
|
}
|
|
631
921
|
|
|
922
|
+
/* No diff image to show (dimension mismatch). The note IS the primary content
|
|
923
|
+
of this slot, so it keeps full `--text` weight; italic alone marks the
|
|
924
|
+
"no data" state. */
|
|
925
|
+
.diff-stats--unavailable .label {
|
|
926
|
+
font-style: italic;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/* Interactive screenshot viewer (interactiveMode). The radio group + caption
|
|
930
|
+
sit in this fieldset, which lives OUTSIDE the clipped `.shot-stage`, so the
|
|
931
|
+
chip focus ring is never clipped by the stage overflow nor painted over
|
|
932
|
+
screenshot pixels. The chips reuse `.chip--toggle` (border `--border-bright`,
|
|
933
|
+
`--accent` focus ring) and the same `.shot-radio-label` base styles as the
|
|
934
|
+
radio-tab viewer — permanently visible, no interactiveMode-specific chip
|
|
935
|
+
styling. Visible chips are the mouse/touch path to every variant (including
|
|
936
|
+
Diff, which the press gesture does not cover) and double as the viewer's
|
|
937
|
+
only visible affordance; the press-and-hold blink-compare in report.js is
|
|
938
|
+
enhancement on top. */
|
|
939
|
+
.shot-interactive {
|
|
940
|
+
display: flex;
|
|
941
|
+
flex-wrap: wrap;
|
|
942
|
+
align-items: center;
|
|
943
|
+
gap: 0.5rem;
|
|
944
|
+
margin: 0.5rem 0 0.5rem 2.2rem;
|
|
945
|
+
padding: 0;
|
|
946
|
+
border: 0;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/* Persistent caption naming the DISPLAYED variant — the committed one, or the
|
|
950
|
+
press-and-hold preview while it is held (report.js keeps it in sync). Text on
|
|
951
|
+
`--text` (≥4.5:1), so the active variant is never conveyed by color alone —
|
|
952
|
+
it pairs the checked chip's accent border with this readable label.
|
|
953
|
+
aria-hidden keeps it visual only: the checked radio is the single accessible
|
|
954
|
+
source of committed state. */
|
|
955
|
+
.shot-caption {
|
|
956
|
+
margin: 0 0 0 0.25rem;
|
|
957
|
+
color: var(--text);
|
|
958
|
+
font-size: 0.8125rem;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.shot-caption-variant {
|
|
962
|
+
font-weight: 600;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/* The stage clips the screenshot to the viewport height. Its border uses
|
|
966
|
+
`--border-bright` (3:1 non-text contrast). overflow:hidden only ever clips
|
|
967
|
+
the image — the controls live in the sibling fieldset above. */
|
|
968
|
+
.shot-stage {
|
|
969
|
+
margin-top: 0.5rem;
|
|
970
|
+
margin-left: 2.2rem;
|
|
971
|
+
padding: 0.5rem;
|
|
972
|
+
border: 1px solid var(--border-bright);
|
|
973
|
+
background: var(--bg);
|
|
974
|
+
width: fit-content;
|
|
975
|
+
max-width: 100%;
|
|
976
|
+
overflow: hidden;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/* Scale the screenshot to fit within the viewport height (and the panel width)
|
|
980
|
+
so a tall capture never forces the page to scroll. width/height:auto preserve
|
|
981
|
+
the aspect ratio while `max-height` caps it to the viewport. */
|
|
982
|
+
.shot-image {
|
|
983
|
+
display: block;
|
|
984
|
+
width: auto;
|
|
985
|
+
height: auto;
|
|
986
|
+
max-width: 100%;
|
|
987
|
+
max-height: 85vh;
|
|
988
|
+
outline: 1px solid var(--img-outline);
|
|
989
|
+
outline-offset: -1px;
|
|
990
|
+
}
|
|
991
|
+
|
|
632
992
|
@media (prefers-reduced-motion: reduce) {
|
|
633
993
|
.skip-link,
|
|
634
994
|
summary.action-row::after,
|
|
635
|
-
.chip
|
|
995
|
+
.chip,
|
|
996
|
+
.summary-filter,
|
|
997
|
+
.breakpoint-filter {
|
|
636
998
|
transition: none;
|
|
637
999
|
}
|
|
638
|
-
.chip:active
|
|
1000
|
+
.chip:active,
|
|
1001
|
+
.summary-filter:active,
|
|
1002
|
+
.breakpoint-filter:active {
|
|
639
1003
|
scale: none;
|
|
640
1004
|
}
|
|
641
1005
|
}
|