styleproof 3.1.3 → 3.1.4

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 CHANGED
@@ -7,6 +7,13 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Fixed
11
+
12
+ - **Popup capture no longer collapses distinct triggers that reuse the same
13
+ overlay mount.** Each visible trigger slot that opens a persistent overlay now
14
+ gets its own `<surface>-popup-XX` map, so separate modals or menus rendered at
15
+ the same DOM path are captured instead of being deduped away.
16
+
10
17
  ## [3.1.3] - 2026-06-29
11
18
 
12
19
  ### Added
package/README.md CHANGED
@@ -312,16 +312,16 @@ Anything still moving on its own after that is detected as a volatile region and
312
312
  | Live / volatile regions (tickers, third-party embeds) | auto-detected as still-moving and excluded from direct element comparison |
313
313
  | Non-deterministic capture (replay gap, unseeded randomness) | self-check flags it _while recording_, with a named error |
314
314
 
315
- | You set this — only because it's app-specific | Why it exists |
316
- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
317
- | `STYLEPROOF_REPLAY_FROM` (record / replay) | Base and head capture at different times against a live backend; replaying the base's recorded data pins the head to the same inputs, so the diff is **your code, not data drift**. The one piece of real setup. |
318
- | `replayUrl` / `STYLEPROOF_REPLAY_URL` | Your data endpoints aren't under `**/api/**`. |
319
- | `ignore: ['.selector']` | You want a region gone **explicitly** — auto-exclude already handles most live regions, but a known-noisy element reads clearer named. |
320
- | `liveStates: [{ key, setup, go }]` | A live feature has real states to certify. Capture each state on base and head (`surface-loading`, `surface-loaded`) instead of relying on a single moving page state. |
321
- | `variants: [{ key, setup, go }]` | Non-live deterministic variants, such as nav-open or modal-open states. |
322
- | `popups: true` | Visible click-triggered overlays should be discovered automatically. Captures persistent dialogs, popovers, menus, listboxes, and open data-state overlays as `surface-popup-XX`; keep hover-only or destructive states as explicit `variants`. |
323
- | `clockTime` | Your styling keys off a **specific** date, not just "now". |
324
- | `stabilize: { quietFor, timeout }` | An unusually slow surface needs a longer quiet window before the map is read. |
315
+ | You set this — only because it's app-specific | Why it exists |
316
+ | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
317
+ | `STYLEPROOF_REPLAY_FROM` (record / replay) | Base and head capture at different times against a live backend; replaying the base's recorded data pins the head to the same inputs, so the diff is **your code, not data drift**. The one piece of real setup. |
318
+ | `replayUrl` / `STYLEPROOF_REPLAY_URL` | Your data endpoints aren't under `**/api/**`. |
319
+ | `ignore: ['.selector']` | You want a region gone **explicitly** — auto-exclude already handles most live regions, but a known-noisy element reads clearer named. |
320
+ | `liveStates: [{ key, setup, go }]` | A live feature has real states to certify. Capture each state on base and head (`surface-loading`, `surface-loaded`) instead of relying on a single moving page state. |
321
+ | `variants: [{ key, setup, go }]` | Non-live deterministic variants, such as nav-open or modal-open states. |
322
+ | `popups: true` | Visible click-triggered overlays should be discovered automatically. Captures each matching trigger's persistent dialogs, popovers, menus, listboxes, and open data-state overlays as `surface-popup-XX`; keep hover-only or destructive states as explicit `variants`. |
323
+ | `clockTime` | Your styling keys off a **specific** date, not just "now". |
324
+ | `stabilize: { quietFor, timeout }` | An unusually slow surface needs a longer quiet window before the map is read. |
325
325
 
326
326
  ## Optional: content layer (advisory)
327
327
 
@@ -397,7 +397,7 @@ It's **asynchronous by design**: approval is a checkbox tick handled by a separa
397
397
  | `surfaces` | _required_ | Page states to certify — each `{ key, go, widths?, ignore?, height?, liveStates?, variants?, popups? }`. `go(page)` drives to a settled state. Omit `widths` to auto-detect the app's `@media` breakpoints and sweep one width per band. |
398
398
  | `liveStates` | _none_ | Optional pinned live product states. Each `{ key, setup?, go?, widths?, height?, ignore? }` becomes `<surface>-<state>` and is labeled as a live state in reports. |
399
399
  | `variants` | _none_ | Optional non-live deterministic states under a surface. Each `{ key, setup?, go?, widths?, height?, ignore? }` becomes `<surface>-<variant>` so base/head compare matching states. |
400
- | `popups` | `false` | Optional automatic popup capture. Set `true` or `{ max, triggers, overlays, timeoutMs }` to click visible safe triggers and save discovered overlay states as `<surface>-popup-XX`. |
400
+ | `popups` | `false` | Optional automatic popup capture. Set `true` or `{ max, triggers, overlays, timeoutMs }` to click visible safe triggers and save each opened overlay state as `<surface>-popup-XX`. |
401
401
  | `expected` | _none_ | Your route/view universe. Emits a coverage-guard test (runs without a capture dir) that fails when a route has no surface and isn't excluded — so a new page can't ship uncaptured. |
402
402
  | `exclude` | `{}` | `key → reason` for routes deliberately not captured. Keeps the guard green for known gaps; a key absent from `expected` fails the guard, so the ledger can't go stale. |
403
403
  | `dir` | `STYLEMAP_DIR` | Output label (`base`/`head`); the spec is **inert until set**, so it sits safely beside your other specs. |
package/dist/runner.js CHANGED
@@ -302,13 +302,12 @@ async function assertPopupDeterministic(page, surface, width, height, options, c
302
302
  throw new Error(selfCheckErrorMessage(`${surface.key}-${popupId}`, drift, first.volatile, first.liveCandidates));
303
303
  }
304
304
  }
305
- async function capturePopupCandidate(page, surface, width, height, s, options, candidate, seen) {
305
+ async function capturePopupCandidate(page, surface, width, height, s, options, candidate) {
306
306
  const requests = trackInflightRequests(page);
307
307
  try {
308
308
  const popupKey = await openPopupCandidate(page, surface, width, height, options, candidate);
309
- if (!popupKey || seen.has(popupKey))
309
+ if (!popupKey)
310
310
  return;
311
- seen.add(popupKey);
312
311
  const popupId = `popup-${String(candidate.index + 1).padStart(2, '0')}`;
313
312
  const map = await captureOpenedPopupMap(page, surface, s, requests.pending, popupId);
314
313
  if (s.selfCheck)
@@ -328,9 +327,8 @@ async function capturePopupSurfaces(page, surface, width, height, s) {
328
327
  return;
329
328
  await surface.go(page);
330
329
  const candidates = await markPopupCandidates(page, options);
331
- const seen = new Set();
332
330
  for (const candidate of candidates) {
333
- await capturePopupCandidate(page, surface, width, height, s, options, candidate, seen);
331
+ await capturePopupCandidate(page, surface, width, height, s, options, candidate);
334
332
  }
335
333
  }
336
334
  /** Drive one surface at one width to a settled state and save its style map (+ screenshot).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "styleproof",
3
- "version": "3.1.3",
3
+ "version": "3.1.4",
4
4
  "description": "Catch every CSS change before it ships — review PRs and certify refactors by the browser's computed styles, not pixels. Works with any styling system.",
5
5
  "keywords": [
6
6
  "playwright",