@orkestrel/test 0.0.10 → 0.0.12

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The interactive ARIA roles a bare accessible name is searched across.
2
+ * Names the interactive ARIA roles a bare accessible name is searched across.
3
3
  *
4
4
  * @remarks
5
5
  * A person names a control, not a role, so the one-argument resolver searches every role a control
@@ -45,7 +45,7 @@ export declare function blendColor(front: Color, back: Color): Color;
45
45
  export declare function build<K extends keyof HTMLElementTagNameMap>(tag: K, options?: ElementOptions): HTMLElementTagNameMap[K];
46
46
 
47
47
  /**
48
- * The page a browser paints an unstyled document onto.
48
+ * Names the color a browser paints an unstyled document with.
49
49
  *
50
50
  * @remarks
51
51
  * This is the floor a backdrop walk ends on wherever the caller wants the browser's own canvas
@@ -55,23 +55,43 @@ export declare function build<K extends keyof HTMLElementTagNameMap>(tag: K, opt
55
55
  export declare const CANVAS_COLOR: Color;
56
56
 
57
57
  /**
58
- * The attribute marking the runner's tester pane, and the rule that sizes it, while a frame is
59
- * staged.
58
+ * Names the attribute marking the runner's tester pane, and the rule that sizes it, while a frame
59
+ * is staged.
60
60
  *
61
61
  * @remarks
62
62
  * `stagePane` writes it onto the pane and onto the stylesheet it appends, and `releasePane` finds
63
- * both by it. Nothing else reads it, so a document carrying it after a capture returned is a pane
64
- * that was never released.
63
+ * both by it. The stylesheet's value is the viewport the tester had before the first staging, in
64
+ * `<width>x<height>` form, which is what `releasePane` hands back. Nothing else reads it, so a
65
+ * document carrying it after a capture returned is a pane that was never released.
65
66
  */
66
67
  export declare const CAPTURE_PANE = "data-capture-pane";
67
68
 
69
+ /**
70
+ * Bounds the restagings one capture takes before it refuses a document whose height never settles.
71
+ *
72
+ * @remarks
73
+ * `captureFrame` stages the pane at the content edge `measureContent` reads, and a rule bound to
74
+ * the viewport height lays that document out taller against the taller pane, so the edge has to be
75
+ * read again after every staging. The re-reading stops when the pane and the edge agree, and a rule
76
+ * that adds height with every pane never reaches that point, so the re-reading is bounded here and
77
+ * the shot is refused rather than taken at a height that is already stale.
78
+ *
79
+ * The bound is the measured need plus one. A document holding half the pane plus a fixed block
80
+ * settles in two restagings, because the second carries the growth the first produced and lands on
81
+ * the fixed point; a document whose growth is capped part way settles in three, because it takes
82
+ * one restaging past the cap before it comes back down to the edge. Nothing measured needs a
83
+ * fourth, so a fourth is the headroom that keeps a settling document off the refusal.
84
+ */
85
+ export declare const CAPTURE_STAGINGS = 4;
86
+
68
87
  /**
69
88
  * Shoots one frame at one viewport size and proves the file on disk holds this run's bytes.
70
89
  *
71
90
  * @param options - The path to write, the viewport to shoot at, and the element to shoot.
72
91
  * @returns The absolute path of the written frame, after it has been read back and matched.
73
- * @throws Thrown when the pane cannot be staged, when the provider wrote the frame somewhere else,
74
- * and when the bytes on disk are not the ones this shot produced.
92
+ * @throws Thrown when the pane cannot be staged, when the document's height never settles under
93
+ * {@link CAPTURE_STAGINGS} restagings, when the provider wrote the frame somewhere else, and when
94
+ * the bytes on disk are not the ones this shot produced.
75
95
  *
76
96
  * @remarks
77
97
  * The path a screenshot call returns is the path it meant to write, so it is not evidence a file
@@ -81,8 +101,41 @@ export declare const CAPTURE_PANE = "data-capture-pane";
81
101
  * path, so the two are compared by the segments that survive resolving `.` and `..` lexically — the
82
102
  * refusal is what a provider resolving that path against a different base would trip.
83
103
  *
104
+ * The frame covers the whole document at `options.width`, whatever `options.height` is. The
105
+ * provider shoots the tester's body in the top-level page's own coordinates, so a document taller
106
+ * than the pane is painted for the pane's height and the rows below it are the runner's page rather
107
+ * than the document — a frame that reads as the surface down to the fold and as bare canvas after
108
+ * it. The document is therefore laid out at the declared viewport first and, where it is taller
109
+ * than that, the pane is staged again at the height the document needs, for the shot alone.
110
+ *
111
+ * That height is {@link measureContent}, never less than `options.height`, because the declared
112
+ * viewport is the smallest frame a variant asks for. The reading is the content's own edge rather
113
+ * than the body's box: the box is the larger of the content and the pane, so it stretches with
114
+ * every pane staged over it and a capture that staged too tall a pane could not read its way back
115
+ * down. Rounding up is what covers a body ending part way through a row, which the box does and an
116
+ * integer scroll height does not.
117
+ *
118
+ * The edge is read again after every staging, because a rule bound to the viewport height — a `vh`
119
+ * length, a fixed footer, a full-height panel — lays the document out taller against the taller
120
+ * pane, so a surface built out of those photographs as its scrolled-open self rather than as one
121
+ * screen, and the reading taken before that staging is stale by exactly what the reflow added.
122
+ * Restaging at the edge alone converges on such a document without arriving: each staging closes
123
+ * the same fraction of what is left, so a rule keeping half the pane reads 1322, 1561, 1681, and
124
+ * 1741 against a fixed point of 1800. Each staging therefore carries the growth the one before it
125
+ * produced — the pane is the edge plus that growth — which lands on the fixed point rather than
126
+ * creeping up to it. The first staging carries no growth, because nothing has grown yet, so a
127
+ * document of fixed content is staged at its own edge and shot there rather than at a pane the
128
+ * overshoot stretched.
129
+ *
130
+ * The re-reading stops when the pane and the edge agree, which is the pane the shot is taken at. A
131
+ * rule that adds height with every pane never reaches that point, so the re-reading is bounded by
132
+ * {@link CAPTURE_STAGINGS} and the shot is refused with
133
+ * `Capture frame at <path> never settled after <n> restagings: <h> over a <h> pane` rather than
134
+ * written at a height that is already wrong.
135
+ *
84
136
  * Omit `options.element` to shoot the whole page. The pane is staged for the frame and released
85
- * before this returns, on the failing path as well as the passing one.
137
+ * before this returns, on the failing path as well as the passing one, which hands the tester back
138
+ * the viewport it had before the first staging.
86
139
  *
87
140
  * @example
88
141
  * ```ts
@@ -91,17 +144,17 @@ export declare const CAPTURE_PANE = "data-capture-pane";
91
144
  */
92
145
  export declare function captureFrame(options: FrameOptions): Promise<string>;
93
146
 
94
- /** One theme-and-viewport pair a capture run renders. */
147
+ /** Represents one theme-and-viewport pair a capture run renders. */
95
148
  export declare interface CaptureVariant {
96
- /** The variant's name, which is the second half of every filename the run writes. */
149
+ /** Holds the variant's name, which is the second half of every filename the run writes. */
97
150
  readonly name: string;
98
- /** The viewport width in pixels. */
151
+ /** Holds the viewport width in pixels. */
99
152
  readonly width: number;
100
- /** The viewport height in pixels. */
153
+ /** Holds the viewport height in pixels. */
101
154
  readonly height: number;
102
155
  /**
103
- * The document change this variant needs before the viewport is resized — a theme attribute, a
104
- * density class, a language direction. Omit it when the variant is a viewport alone.
156
+ * Holds the document change this variant needs before the viewport is resized — a theme
157
+ * attribute, a density class, a language direction. Omit it when the variant is a viewport alone.
105
158
  */
106
159
  readonly apply?: () => void;
107
160
  }
@@ -191,7 +244,7 @@ export declare function clickAccessibleWithin(region: string, role: string, name
191
244
  export declare function clickDisclosure(name: string): Promise<void>;
192
245
 
193
246
  /**
194
- * One rendered color as straight sRGB channels and its alpha.
247
+ * Represents one rendered color as straight sRGB channels and its alpha.
195
248
  *
196
249
  * @remarks
197
250
  * The channels run 0–255 and the alpha runs 0–1, which is the shape a computed `rgb()` value already
@@ -200,33 +253,6 @@ export declare function clickDisclosure(name: string): Promise<void>;
200
253
  */
201
254
  export declare type Color = readonly [red: number, green: number, blue: number, alpha: number];
202
255
 
203
- /**
204
- * Determines whether two colors render the same, within the rounding a browser does.
205
- *
206
- * @param first - A CSS color expression or an already-parsed color.
207
- * @param second - A CSS color expression or an already-parsed color.
208
- * @returns `true` when every channel and the alpha agree within the tolerance; `false` otherwise,
209
- * including when either side names no readable color.
210
- *
211
- * @remarks
212
- * Each string side is resolved through {@link rgba}, so a keyword, a token reference, and the
213
- * `rgb()` the engine computes for either of them compare equal without a test converting anything
214
- * first. A side that resolves to nothing makes the answer `false` rather than a throw, because this
215
- * is a predicate.
216
- *
217
- * The tolerance is half a channel step on the 0–255 scale, and the alpha is scaled onto that same
218
- * range before it is compared, so one number covers both. Half a step is what a composite of
219
- * translucent layers and a `color-mix()` round trip actually drift by; anything a reader could see
220
- * is further than that and reports unequal.
221
- *
222
- * @example
223
- * ```ts
224
- * colorEqual('rebeccapurple', 'rgb(102, 51, 153)') // true
225
- * colorEqual('red', [0, 0, 255, 1]) // false
226
- * ```
227
- */
228
- export declare function colorEqual(first: string | Color, second: string | Color): boolean;
229
-
230
256
  /**
231
257
  * Sets one field's value and commits it, the way typing and then leaving the field does.
232
258
  *
@@ -246,52 +272,43 @@ export declare function colorEqual(first: string | Color, second: string | Color
246
272
  export declare function commitInput(element: HTMLInputElement | HTMLTextAreaElement, text: string): void;
247
273
 
248
274
  /**
249
- * The roles whose accessible name is the text a reader can see inside them.
275
+ * Computes the pattern that matches one accessible name a decorative glyph may sit beside.
250
276
  *
251
- * @remarks
252
- * `readName` reads an element in this list from its own rendered text, after every `aria-hidden`
253
- * descendant is dropped, and falls through to `title` for every other role.
254
- */
255
- export declare const CONTENT_ROLES: readonly string[];
256
-
257
- /**
258
- * Measures the WCAG 2.x contrast ratio between an element's computed text and background colors.
259
- *
260
- * @param element - The element whose rendered text contrast to measure.
261
- * @param floor - The opaque color the backdrop walk ends on. Omit it to refuse a stack the floor
262
- * would show through instead of assuming one.
263
- * @returns The relative-luminance contrast ratio.
264
- * @throws Thrown when the element exposes no computed foreground color, and — with `floor` omitted
265
- * — when the walk from the element upwards reaches no opaque layer.
277
+ * @param name - The exact accessible name a person reads, whitespace runs collapsed on the way in.
278
+ * @returns A pattern anchored at both ends, admitting a run of characters that are neither letters
279
+ * nor digits before the name and after it.
266
280
  *
267
281
  * @remarks
268
- * A transparent or translucent background resolves through the element's ancestors: every painted
269
- * layer from the element up to the first opaque one composites top-over-bottom onto that opaque
270
- * base, so a 3% surface tint reads as a tint over what shows through it rather than as a
271
- * full-strength paint. A translucent foreground then resolves against that effective background
272
- * before luminance is measured.
282
+ * A role query that includes hidden elements computes a name from the `aria-hidden` subtrees too,
283
+ * so an icon font's `::before` glyph joins the name a person never hears and an exact string never
284
+ * matches again. This pattern is what {@link resolveRendered} asks the hidden pass with, and its
285
+ * tolerance is bounded to what a glyph can be: a leading or trailing run carrying no letter and no
286
+ * digit. A hidden icon whose own content is a word still defeats it, and a name differing from the
287
+ * requested one by punctuation alone still satisfies it.
273
288
  *
274
- * With `floor` omitted, the walk from the target upwards must reach a fully opaque layer: the
275
- * measurement throws rather than assuming a white canvas wherever that canvas would still be part
276
- * of the answer. The refusal reads the alpha of the deepest layer {@link readLayers} collected, so
277
- * a chain that declares no background color at all, a chain painting only translucent layers, and a
278
- * chain deep enough for its composite to round to the canvas's own channels are refused alike,
279
- * because the number any of them produces is as much a report of the assumption as of the page.
280
- * Supply a floor wherever the caller knows what the stack sits on — a fragment mounted into a
281
- * painted host, or a document whose canvas is {@link CANVAS_COLOR} — and the composite is taken
282
- * over it rather than refused.
289
+ * That bound is affordable because the hidden pass chooses between two refusal voices and returns
290
+ * nothing. The visible pass decides which element a resolver returns, and it matches the exact
291
+ * string against the name the accessibility tree actually publishes.
283
292
  *
284
- * The element itself must expose a computed foreground color either way. A detached element exposes
285
- * none, and the measurement throws rather than guessing one.
293
+ * Pass this to a role query with `exact: true`. That flag is the engine's case-sensitivity switch
294
+ * as well as its exactness one, so a query carrying `exact: false` uppercases the computed name
295
+ * before testing a pattern against it and a lowercase letter in the requested name never matches.
286
296
  *
287
297
  * @example
288
298
  * ```ts
289
- * const container = render('<p style="background: #000; color: #fff">Ready</p>')
290
- * contrast(requireValue(container.firstElementChild)) // 21
291
- * contrast(requireValue(container.firstElementChild), CANVAS_COLOR) // 21, and never refuses
299
+ * computeNamePattern('Add building').test('\uF4FE Add building') // true
292
300
  * ```
293
301
  */
294
- export declare function contrast(element: Element, floor?: Color): number;
302
+ export declare function computeNamePattern(name: string): RegExp;
303
+
304
+ /**
305
+ * Names the roles whose accessible name is the text a reader can see inside them.
306
+ *
307
+ * @remarks
308
+ * `readName` reads an element in this list from its own rendered text, after every `aria-hidden`
309
+ * descendant is dropped, and falls through to `title` for every other role.
310
+ */
311
+ export declare const CONTENT_ROLES: readonly string[];
295
312
 
296
313
  /**
297
314
  * Creates one console channel that records every call it receives and hands that call on unchanged.
@@ -479,7 +496,7 @@ export declare function describeFocus(element: Element): string;
479
496
  export declare function describeTree(element: Element): string;
480
497
 
481
498
  /**
482
- * Options for one built element.
499
+ * Configures one built element.
483
500
  *
484
501
  * @remarks
485
502
  * `classes` is written the way a `class` attribute is written — one space-separated string — so a
@@ -488,11 +505,11 @@ export declare function describeTree(element: Element): string;
488
505
  * with it.
489
506
  */
490
507
  export declare interface ElementOptions {
491
- /** The class list, space-separated, exactly as a `class` attribute writes it. */
508
+ /** Holds the class list, space-separated, exactly as a `class` attribute writes it. */
492
509
  readonly classes?: string;
493
- /** The text the element carries, set as text rather than parsed as markup. */
510
+ /** Holds the text the element carries, set as text rather than parsed as markup. */
494
511
  readonly text?: string;
495
- /** Every attribute to set, keyed by attribute name. */
512
+ /** Holds every attribute to set, keyed by attribute name. */
496
513
  readonly attributes?: Readonly<Record<string, string>>;
497
514
  }
498
515
 
@@ -542,7 +559,34 @@ export declare function expandCaptures(states: readonly string[], variants: read
542
559
  export declare function extractOrphans(root: ParentNode, child: string, parent: string): readonly string[];
543
560
 
544
561
  /**
545
- * The role each `input` type carries.
562
+ * Collects the markup of every element carrying a non-empty `style` attribute and of every `<style>`
563
+ * element, in document order, `root` included in both populations when it is an `Element`.
564
+ *
565
+ * @param root - The subtree to sweep. A detached element and a `DocumentFragment` both work.
566
+ * @returns The `outerHTML` of each such element, in document order; an empty list when the markup
567
+ * declares no style of its own.
568
+ *
569
+ * @remarks
570
+ * These are the declarations the stylesheet never sees: an inline `style` attribute, wherever it
571
+ * sits, and a `<style>` element, whatever it holds. Nothing else counts. A class and a `data-*`
572
+ * attribute name something the cascade resolves, so neither is reported however unusual it looks;
573
+ * an inline `style` on a `<path>` inside an SVG is reported, because a namespace changes nothing
574
+ * about what an inline declaration is.
575
+ *
576
+ * A `style` attribute holding nothing but whitespace declares nothing, so it is not reported. A
577
+ * `DocumentFragment` root contributes its descendants alone, because it is not an `Element`; a
578
+ * `<style>` root and a root carrying an inline attribute are each reported, and an element that is a
579
+ * `<style>` element and carries an inline attribute too is reported once.
580
+ *
581
+ * @example
582
+ * ```ts
583
+ * extractStyles(container) // []
584
+ * ```
585
+ */
586
+ export declare function extractStyles(root: ParentNode): readonly string[];
587
+
588
+ /**
589
+ * Names the role each `input` type carries.
546
590
  *
547
591
  * @remarks
548
592
  * Membership is the contract. The map answers for `button`, `checkbox`, `email`, `number`,
@@ -599,8 +643,8 @@ export declare function findKeyframes(name: string): CSSKeyframesRule | undefine
599
643
  *
600
644
  * @remarks
601
645
  * This proves a declaration exists in the cascade at all, which is a different question from what an
602
- * element resolves to: {@link style} reads the winner, and a rule this finds may be overridden by
603
- * another. Assert on this where the subject is the stylesheet, and on `style` where the subject is
646
+ * element resolves to: {@link readStyle} reads the winner, and a rule this finds may be overridden by
647
+ * another. Assert on this where the subject is the stylesheet, and on `readStyle` where the subject is
604
648
  * the rendered result.
605
649
  *
606
650
  * The match is a substring, so `findRule('.card')` finds `.card`, `.card:hover`, and
@@ -614,7 +658,7 @@ export declare function findKeyframes(name: string): CSSKeyframesRule | undefine
614
658
  export declare function findRule(selector: string): CSSStyleRule | undefined;
615
659
 
616
660
  /**
617
- * What sequential keyboard navigation can reach, before disabled and unrendered elements go.
661
+ * Names what sequential keyboard navigation can reach, before disabled and unrendered elements go.
618
662
  *
619
663
  * @remarks
620
664
  * `describeFocus` queries this selector and then drops what a browser drops: an element the
@@ -624,20 +668,40 @@ export declare function findRule(selector: string): CSSStyleRule | undefined;
624
668
  */
625
669
  export declare const FOCUSABLE_SELECTOR = "a[href], area[href], button, input, select, summary, textarea, [tabindex]";
626
670
 
627
- /** Options for one captured frame. */
671
+ /** Configures one captured frame. */
628
672
  export declare interface FrameOptions {
629
- /** The frame's path, relative to the calling test file. */
673
+ /** Holds the frame's path, relative to the calling test file. */
630
674
  readonly path: string;
631
- /** The viewport width in CSS pixels the frame is shot at. */
675
+ /** Holds the viewport width in CSS pixels the frame is shot at. */
632
676
  readonly width: number;
633
- /** The viewport height in CSS pixels the frame is shot at. */
677
+ /** Holds the viewport height in CSS pixels the frame is shot at. */
634
678
  readonly height: number;
635
- /** The element to shoot. Omit it to shoot the whole page. */
679
+ /** Holds the element to shoot. Omit it to shoot the whole page. */
636
680
  readonly element?: Element | undefined;
637
681
  }
638
682
 
639
683
  /**
640
- * The role a `th` carries for the header axis its `scope` names.
684
+ * Represents one written frame, read back from the file a capture produced.
685
+ *
686
+ * @remarks
687
+ * The floor is the frame's bottom row, because that row is where coverage shows: a frame shot at a
688
+ * pane shorter than the document ends on the runner's own canvas rather than on the document's
689
+ * background, and the two read as different colors. A row counts as one color only when every
690
+ * channel matches across it, alpha included, and `floor` names the three color channels of that one
691
+ * color in the `rgb(r, g, b)` form a computed style is written in, so a literal comparison,
692
+ * `parseColor`, and `matchesColor` all take it.
693
+ */
694
+ export declare interface FrameReading {
695
+ /** Holds the frame's width in device pixels. */
696
+ readonly width: number;
697
+ /** Holds the frame's height in device pixels. */
698
+ readonly height: number;
699
+ /** Holds the single color the frame's bottom row paints; `undefined` where that row paints several. */
700
+ readonly floor: string | undefined;
701
+ }
702
+
703
+ /**
704
+ * Names the role a `th` carries for the header axis its `scope` names.
641
705
  *
642
706
  * @remarks
643
707
  * A header cell heads a column or a row, and this map answers for the `col` and `row` scopes that
@@ -647,7 +711,8 @@ export declare interface FrameOptions {
647
711
  export declare const HEADER_ROLES: Readonly<Record<string, string>>;
648
712
 
649
713
  /**
650
- * The role each listed tag carries in the accessibility tree when it declares none of its own.
714
+ * Names the role each listed tag carries in the accessibility tree when it declares none of its
715
+ * own.
651
716
  *
652
717
  * @remarks
653
718
  * Membership is the contract. The map answers for the sectioning elements `ARTICLE`, `ASIDE`,
@@ -672,7 +737,7 @@ export declare const IMPLICIT_ROLES: Readonly<Record<string, string>>;
672
737
  * Determines whether a rectangle lies wholly outside the browser viewport.
673
738
  *
674
739
  * @param rectangle - The measured client rectangle to inspect.
675
- * @returns `true` when no part of the rectangle intersects the viewport.
740
+ * @returns True if no part of the rectangle intersects the viewport; false otherwise.
676
741
  *
677
742
  * @example
678
743
  * ```ts
@@ -685,9 +750,9 @@ export declare function isOutsideViewport(rectangle: DOMRectReadOnly): boolean;
685
750
  * Determines whether a person can click one element where it currently sits.
686
751
  *
687
752
  * @param element - The element to judge.
688
- * @returns `true` when the element is connected, visible, laid out with a non-zero box, in the
753
+ * @returns True if the element is connected, visible, laid out with a non-zero box, in the
689
754
  * sequential focus order, neither disabled nor marked `aria-disabled="true"`, and outside every
690
- * `[inert]` subtree; `false` otherwise.
755
+ * `[inert]` subtree; false otherwise.
691
756
  *
692
757
  * @remarks
693
758
  * This is the one reachability filter the layer applies. `resolveRendered`, `clickAccessibleWithin`,
@@ -710,8 +775,7 @@ export declare function isReachable(element: Element): boolean;
710
775
  * Determines whether the accessibility tree presents one element at all.
711
776
  *
712
777
  * @param element - The element to judge.
713
- * @returns `false` when the element is hidden from assistive technology, from sight, or from both;
714
- * `true` otherwise.
778
+ * @returns True if the element is presented to assistive technology and to sight; false otherwise.
715
779
  *
716
780
  * @remarks
717
781
  * A control clipped to a zero-size rectangle is still announced, which is the whole point of that
@@ -734,7 +798,7 @@ export declare function isReachable(element: Element): boolean;
734
798
  export declare function isRendered(element: Element): boolean;
735
799
 
736
800
  /**
737
- * The record of one scenario: every step it took and everything the page said while it ran.
801
+ * Records one scenario: every step it took and everything the page said while it ran.
738
802
  *
739
803
  * @remarks
740
804
  * Recording is off until {@link JournalInterface.start} arms it, so a suite that never starts a
@@ -743,9 +807,9 @@ export declare function isRendered(element: Element): boolean;
743
807
  * journal that swallowed what it read would hide exactly the diagnostics it exists to keep.
744
808
  */
745
809
  export declare interface JournalInterface {
746
- /** Every step recorded since the journal started, in the order it was taken; a snapshot. */
810
+ /** Lists every step recorded since the journal started, in the order it was taken; a snapshot. */
747
811
  readonly steps: readonly JournalStep[];
748
- /** Every console line and uncaught failure the page emitted since it started; a snapshot. */
812
+ /** Lists every console line and uncaught failure the page emitted since it started; a snapshot. */
749
813
  readonly output: readonly string[];
750
814
  /**
751
815
  * Starts a fresh recording, dropping whatever the previous scenario left.
@@ -773,16 +837,75 @@ export declare interface JournalInterface {
773
837
  record(action: string, trigger: string, result: string): void;
774
838
  }
775
839
 
776
- /** One scripted step a journal recorded, and what the surface did about it. */
840
+ /** Represents one scripted step a journal recorded, and what the surface did about it. */
777
841
  export declare interface JournalStep {
778
- /** What the run did, as one verb. */
842
+ /** Names what the run did, as one verb. */
779
843
  readonly action: string;
780
- /** The exact thing it did it to. */
844
+ /** Names the exact thing it did it to. */
781
845
  readonly trigger: string;
782
- /** What was observed on the surface after the step landed. */
846
+ /** Holds what was observed on the surface after the step landed. */
783
847
  readonly result: string;
784
848
  }
785
849
 
850
+ /**
851
+ * Determines whether two colors render the same, within the rounding a browser does.
852
+ *
853
+ * @param first - A CSS color expression or an already-parsed color.
854
+ * @param second - A CSS color expression or an already-parsed color.
855
+ * @returns True if every channel and the alpha agree within the tolerance; false otherwise,
856
+ * including when either side names no readable color.
857
+ *
858
+ * @remarks
859
+ * Each string side is resolved through {@link parseCSSColor}, so a keyword, a token reference, and the
860
+ * `rgb()` the engine computes for either of them compare equal without a test converting anything
861
+ * first. A side that resolves to nothing makes the answer `false` rather than a throw, because this
862
+ * is a predicate.
863
+ *
864
+ * The tolerance is half a channel step on the 0–255 scale, and the alpha is scaled onto that same
865
+ * range before it is compared, so one number covers both. Half a step is what a composite of
866
+ * translucent layers and a `color-mix()` round trip actually drift by; anything a reader could see
867
+ * is further than that and reports unequal.
868
+ *
869
+ * @example
870
+ * ```ts
871
+ * matchesColor('rebeccapurple', 'rgb(102, 51, 153)') // true
872
+ * matchesColor('red', [0, 0, 255, 1]) // false
873
+ * ```
874
+ */
875
+ export declare function matchesColor(first: string | Color, second: string | Color): boolean;
876
+
877
+ /**
878
+ * Measures the row the document's own content ends on, in document coordinates.
879
+ *
880
+ * @returns The content edge, rounded up to a whole row.
881
+ *
882
+ * @remarks
883
+ * The body's box is not the document's height: it is the larger of the content and the pane. A pane
884
+ * taller than the document stretches it, and `document.body.getBoundingClientRect()`,
885
+ * `body.scrollHeight`, `body.offsetHeight`, and `documentElement.scrollHeight` all read that pane
886
+ * back rather than the content under it. So a caller that has staged a pane taller than the
887
+ * document cannot find its way down again from any of them. This reading can, because it is taken
888
+ * over the elements inside the body rather than over the box around them: a document of fixed
889
+ * content answers the same number under a short pane and a tall one, and a document laid out
890
+ * against the viewport answers what that viewport actually laid out.
891
+ *
892
+ * Each element contributes its client rectangle's bottom edge in document coordinates plus its own
893
+ * bottom margin, which sits outside that rectangle, and the largest contribution wins. Taking the
894
+ * largest is what handles a collapsed margin without asking whether it collapsed: a child margin
895
+ * that collapses out through its parent is counted once, at the child, and one the parent's padding
896
+ * holds in is counted once, at the parent. The body's and the root's own bottom padding and margin
897
+ * sit under every child rather than beside them, so they are added after the walk.
898
+ *
899
+ * The sum is rounded up because a box can end part way through a row and a frame cannot hold part
900
+ * of one.
901
+ *
902
+ * @example
903
+ * ```ts
904
+ * const covered = measureContent()
905
+ * ```
906
+ */
907
+ export declare function measureContent(): number;
908
+
786
909
  /**
787
910
  * Measures the WCAG 2.x contrast ratio between two opaque colors.
788
911
  *
@@ -823,8 +946,8 @@ export declare function measureLuminance(color: Color): number;
823
946
  * @remarks
824
947
  * What this buys is the composition, not the attachment: the `append` method returns `void`, and
825
948
  * this hands the element back, so it fits where an expression is expected. The {@link render} helper
826
- * returns its fixture through it, and the {@link rgba} helper probes through `mount(build('span'))`.
827
- * A bare `append` call breaks each of those call sites.
949
+ * returns its fixture through it, and the {@link parseCSSColor} helper probes through
950
+ * `mount(build('span'))`. A bare `append` call breaks each of those call sites.
828
951
  *
829
952
  * Being connected is what the attachment then buys: `getComputedStyle` resolves against the shipped
830
953
  * cascade, custom properties inherit from `:root`, and the element lays out a real box. A detached
@@ -866,39 +989,46 @@ export declare function mount<T extends Element>(element: T): T;
866
989
  export declare function parseColor(value: string): Color | undefined;
867
990
 
868
991
  /**
869
- * Reads one resolved CSS length as a number of pixels.
992
+ * Resolves any CSS color expression to straight sRGB channels, by asking the browser.
870
993
  *
871
- * @param element - The element whose resolved style to inspect.
872
- * @param property - The CSS property name, registered or custom.
873
- * @returns The leading numeric part of the resolved value, and `0` when it carries none.
994
+ * @param value - Any value the `color` property accepts: a keyword, a hex triple, a `var()`
995
+ * reference, a `color-mix()`, or an already-computed `rgb()`.
996
+ * @returns The resolved color's channels, or `undefined` when the CSSOM refuses the value or the
997
+ * computed result names no color {@link parseColor} speaks.
874
998
  *
875
999
  * @remarks
876
- * A resolved length is text with a unit `'12px'` — so this reads the number in front of the unit
877
- * and discards the rest. The unit is not checked: the resolved value of a length is in pixels in
878
- * every case a browser hands back, and a property that resolves to something else is the caller's
879
- * mistake rather than this reader's.
1000
+ * This is the live half of the pair {@link parseColor} opens. `parseColor` reads text and speaks
1001
+ * only the computed syntaxes a cascade hands back; this stages a probe element, hands it to the real
1002
+ * cascade, and reads back what the engine computed which is the only way a keyword, a hex triple,
1003
+ * or a `var()` reference becomes channels at all. The read itself goes through `parseColor`, so both
1004
+ * halves agree on what a computed value means.
880
1005
  *
881
- * An unparsable value reads as `0` rather than as absence, because every caller of this is measuring
882
- * and `'auto'`, `'none'`, and `''` each contribute no pixels to what a reader sees. Where the
883
- * distinction matters, read the text with {@link style} instead.
1006
+ * The probe is mounted, because an unmounted element inherits nothing and a `var()` reference to a
1007
+ * token declared on `:root` would resolve to the initial value instead. It is removed in a `finally`,
1008
+ * so a value that throws on the way through leaves no node behind.
1009
+ *
1010
+ * Refusal is the CSSOM's: an expression it will not parse leaves the probe's inline `color` empty
1011
+ * and this returns `undefined`. A `var()` naming an undeclared custom property is not refused,
1012
+ * because the cascade accepts it and computes the inherited color, so a test that means to catch a
1013
+ * missing token asserts on {@link readToken} rather than on this.
884
1014
  *
885
1015
  * @example
886
1016
  * ```ts
887
- * pixels(button, 'padding-left') // 12
888
- * pixels(button, 'width') // 0 when the width resolves to `auto`
1017
+ * parseCSSColor('rebeccapurple') // [102, 51, 153, 1]
1018
+ * parseCSSColor('not-a-color') // undefined
889
1019
  * ```
890
1020
  */
891
- export declare function pixels(element: Element, property: string): number;
1021
+ export declare function parseCSSColor(value: string): Color | undefined;
892
1022
 
893
- /** The registry of capture states one run places, and the files it wrote placing them. */
1023
+ /** Holds the registry of capture states one run places, and the files it wrote placing them. */
894
1024
  export declare interface PortfolioInterface {
895
- /** The name of the variant this run renders. */
1025
+ /** Holds the name of the variant this run renders. */
896
1026
  readonly variant: string;
897
- /** Every state placed so far, in placement order. */
898
- readonly states: readonly string[];
899
- /** Every path written so far, in write order. */
1027
+ /** Lists every state placed so far, in placement order. */
1028
+ readonly placements: readonly string[];
1029
+ /** Lists every path written so far, in write order. */
900
1030
  readonly paths: readonly string[];
901
- /** The registry expanded across every variant: the filenames a complete portfolio holds. */
1031
+ /** Lists the filenames a complete portfolio holds: the registry expanded across every variant. */
902
1032
  readonly files: readonly string[];
903
1033
  /**
904
1034
  * Places one registered state: applies the variant, stages the pane, and writes the verified
@@ -913,22 +1043,22 @@ export declare interface PortfolioInterface {
913
1043
  place(state: string, element?: Element): Promise<string | undefined>;
914
1044
  }
915
1045
 
916
- /** Options for a capture portfolio. */
1046
+ /** Configures a capture portfolio. */
917
1047
  export declare interface PortfolioOptions {
918
1048
  /**
919
- * Every state name the journeys place, declared once. `place` refuses a name absent from this
920
- * list, so the registry and the disk cannot drift apart.
1049
+ * Lists every state name the journeys place, declared once. `place` refuses a name absent from
1050
+ * this list, so the registry and the disk cannot drift apart.
921
1051
  */
922
1052
  readonly states: readonly string[];
923
- /** Every variant the portfolio can be rendered in. One run renders exactly one of them. */
1053
+ /** Lists every variant the portfolio can be rendered in. One run renders exactly one of them. */
924
1054
  readonly variants: readonly CaptureVariant[];
925
- /** The name of the variant this run renders. Creation throws when no variant carries it. */
1055
+ /** Holds the name of the variant this run renders. Creation throws when no variant carries it. */
926
1056
  readonly variant: string;
927
- /** The directory each written file is placed in, relative to the calling test file. */
1057
+ /** Holds the directory each written file is placed in, relative to the calling test file. */
928
1058
  readonly directory: string;
929
1059
  /**
930
- * Whether this run writes files. An ordinary run leaves it unset, so `place` resizes nothing,
931
- * writes nothing, and records nothing.
1060
+ * Determines whether this run writes files. An ordinary run leaves it unset, so `place` resizes
1061
+ * nothing, writes nothing, and records nothing.
932
1062
  */
933
1063
  readonly enabled?: boolean;
934
1064
  }
@@ -1000,6 +1130,69 @@ export declare function readBackdrop(element: Element, floor: Color): Color;
1000
1130
  */
1001
1131
  export declare function readCascade(): ReadonlySet<string>;
1002
1132
 
1133
+ /**
1134
+ * Collects every class token the markup under one root carries.
1135
+ *
1136
+ * @param root - The subtree to sweep. A detached element and a `DocumentFragment` both work.
1137
+ * @returns The class tokens in document order of first sighting; an empty set for markup carrying no
1138
+ * class at all.
1139
+ *
1140
+ * @remarks
1141
+ * The root's own classes count when the root is an `Element`, so a `DocumentFragment` contributes
1142
+ * its descendants alone. Every element is read through `classList`, which is what makes an SVG
1143
+ * element count the same as an HTML one: `className` on an SVG element is an `SVGAnimatedString`
1144
+ * rather than a string, and a reader splitting that value finds nothing.
1145
+ *
1146
+ * This is the authored half of a class conformance check and {@link readCascade} is the defined
1147
+ * half, so the difference between them is the set of classes the markup uses and no loaded
1148
+ * stylesheet declares.
1149
+ *
1150
+ * @example
1151
+ * ```ts
1152
+ * [...readClasses(container)].filter((name) => !readCascade().has(name))
1153
+ * ```
1154
+ */
1155
+ export declare function readClasses(root: ParentNode): ReadonlySet<string>;
1156
+
1157
+ /**
1158
+ * Measures the WCAG 2.x contrast ratio between an element's computed text and background colors.
1159
+ *
1160
+ * @param element - The element whose rendered text contrast to measure.
1161
+ * @param floor - The opaque color the backdrop walk ends on. Omit it to refuse a stack the floor
1162
+ * would show through instead of assuming one.
1163
+ * @returns The relative-luminance contrast ratio.
1164
+ * @throws Thrown when the element exposes no computed foreground color, and — with `floor` omitted
1165
+ * — when the walk from the element upwards reaches no opaque layer.
1166
+ *
1167
+ * @remarks
1168
+ * A transparent or translucent background resolves through the element's ancestors: every painted
1169
+ * layer from the element up to the first opaque one composites top-over-bottom onto that opaque
1170
+ * base, so a 3% surface tint reads as a tint over what shows through it rather than as a
1171
+ * full-strength paint. A translucent foreground then resolves against that effective background
1172
+ * before luminance is measured.
1173
+ *
1174
+ * With `floor` omitted, the walk from the target upwards must reach a fully opaque layer: the
1175
+ * measurement throws rather than assuming a white canvas wherever that canvas would still be part
1176
+ * of the answer. The refusal reads the alpha of the deepest layer {@link readLayers} collected, so
1177
+ * a chain that declares no background color at all, a chain painting only translucent layers, and a
1178
+ * chain deep enough for its composite to round to the canvas's own channels are refused alike,
1179
+ * because the number any of them produces is as much a report of the assumption as of the page.
1180
+ * Supply a floor wherever the caller knows what the stack sits on — a fragment mounted into a
1181
+ * painted host, or a document whose canvas is {@link CANVAS_COLOR} — and the composite is taken
1182
+ * over it rather than refused.
1183
+ *
1184
+ * The element itself must expose a computed foreground color either way. A detached element exposes
1185
+ * none, and the measurement throws rather than guessing one.
1186
+ *
1187
+ * @example
1188
+ * ```ts
1189
+ * const container = render('<p style="background: #000; color: #fff">Ready</p>')
1190
+ * readContrast(requireValue(container.firstElementChild)) // 21
1191
+ * readContrast(requireValue(container.firstElementChild), CANVAS_COLOR) // 21, and never refuses
1192
+ * ```
1193
+ */
1194
+ export declare function readContrast(element: Element, floor?: Color): number;
1195
+
1003
1196
  /**
1004
1197
  * Reads the rendered text of the element that currently holds focus.
1005
1198
  *
@@ -1015,6 +1208,30 @@ export declare function readCascade(): ReadonlySet<string>;
1015
1208
  */
1016
1209
  export declare function readFocus(): string | undefined;
1017
1210
 
1211
+ /**
1212
+ * Reads one written frame back and reports its size and the color its bottom row paints.
1213
+ *
1214
+ * @param path - The frame's absolute path, as `captureFrame` returns it.
1215
+ * @returns The frame's size in device pixels and its floor.
1216
+ * @throws Thrown when the runner cannot read the path, when the bytes there are not an image this
1217
+ * browser decodes, and when the browser hands out no 2D canvas to measure them on.
1218
+ *
1219
+ * @remarks
1220
+ * The reading comes off the written file rather than off the document that produced it, which is
1221
+ * what makes it evidence about a capture: the browser's own image decoding and an
1222
+ * `OffscreenCanvas` answer for the pixels a viewer would see, so a frame that ends on the runner's
1223
+ * canvas reports that canvas whatever the document's style resolves to. Pass the path the provider
1224
+ * resolved and `captureFrame` returned; the runner's `readFile` command resolves a relative path
1225
+ * against its own root rather than against the calling test file, so a relative path names a file
1226
+ * somewhere else.
1227
+ *
1228
+ * @example
1229
+ * ```ts
1230
+ * const reading = await readFrame(written)
1231
+ * ```
1232
+ */
1233
+ export declare function readFrame(path: string): Promise<FrameReading>;
1234
+
1018
1235
  /**
1019
1236
  * Collects the painted layers standing between one element and the surface it sits on.
1020
1237
  *
@@ -1028,7 +1245,7 @@ export declare function readFocus(): string | undefined;
1028
1245
  * the walk stops at the first fully opaque layer, because nothing above that layer is visible.
1029
1246
  *
1030
1247
  * The stack is what tells a resolved backdrop from an assumed one: the walk reached an opaque
1031
- * surface exactly when its last layer's alpha is `1`. {@link contrast} refuses on that reading,
1248
+ * surface exactly when its last layer's alpha is `1`. {@link readContrast} refuses on that reading,
1032
1249
  * which no comparison of composited colors can replace — 64 half-transparent layers composite to
1033
1250
  * the same channels over opposite floors, because the floor's remaining share falls below the last
1034
1251
  * bit a channel carries.
@@ -1091,6 +1308,13 @@ export declare function readPage(): string;
1091
1308
  * visually-hidden content.
1092
1309
  * @throws When the named region is absent, hidden, or ambiguous.
1093
1310
  *
1311
+ * @remarks
1312
+ * One pass answers this, because absence and concealment share the refusal. The pass asks the role
1313
+ * engine over the elements the accessibility tree presents, so the name matched is the one a screen
1314
+ * reader announces and an `aria-hidden` glyph in a heading a region points at contributes nothing
1315
+ * to it. A region the tree does not present is refused as not visible, which is what a reader
1316
+ * perceiving nothing there means.
1317
+ *
1094
1318
  * @example
1095
1319
  * ```ts
1096
1320
  * readPerception('Run')
@@ -1098,6 +1322,31 @@ export declare function readPage(): string;
1098
1322
  */
1099
1323
  export declare function readPerception(name: string): string;
1100
1324
 
1325
+ /**
1326
+ * Reads one resolved CSS length as a number of pixels.
1327
+ *
1328
+ * @param element - The element whose resolved style to inspect.
1329
+ * @param property - The CSS property name, registered or custom.
1330
+ * @returns The leading numeric part of the resolved value, and `0` when it carries none.
1331
+ *
1332
+ * @remarks
1333
+ * A resolved length is text with a unit — `'12px'` — so this reads the number in front of the unit
1334
+ * and discards the rest. The unit is not checked: the resolved value of a length is in pixels in
1335
+ * every case a browser hands back, and a property that resolves to something else is the caller's
1336
+ * mistake rather than this reader's.
1337
+ *
1338
+ * An unparsable value reads as `0` rather than as absence, because every caller of this is measuring
1339
+ * and `'auto'`, `'none'`, and `''` each contribute no pixels to what a reader sees. Where the
1340
+ * distinction matters, read the text with {@link readStyle} instead.
1341
+ *
1342
+ * @example
1343
+ * ```ts
1344
+ * readPixels(button, 'padding-left') // 12
1345
+ * readPixels(button, 'width') // 0 when the width resolves to `auto`
1346
+ * ```
1347
+ */
1348
+ export declare function readPixels(element: Element, property: string): number;
1349
+
1101
1350
  /**
1102
1351
  * Measures the contrast the focus chrome painted on one control reaches against its own backdrop.
1103
1352
  *
@@ -1158,6 +1407,25 @@ export declare function readRing(control: Element, worn?: Element): number | und
1158
1407
  */
1159
1408
  export declare function readRole(element: Element): string | undefined;
1160
1409
 
1410
+ /**
1411
+ * Reads one custom property from the document element.
1412
+ *
1413
+ * @param name - The custom property name, with or without its leading dashes.
1414
+ * @returns The resolved value, trimmed; an empty string when the document declares no such property.
1415
+ *
1416
+ * @remarks
1417
+ * This is {@link readToken} against `document.documentElement`, which is where a theme declares its
1418
+ * tokens and where a `[data-theme]` switch retunes them. It exists as its own name because that
1419
+ * element is the one a token question is nearly always about, and naming it at every call site
1420
+ * buries the question.
1421
+ *
1422
+ * @example
1423
+ * ```ts
1424
+ * readRootToken('surface')
1425
+ * ```
1426
+ */
1427
+ export declare function readRootToken(name: string): string;
1428
+
1161
1429
  /**
1162
1430
  * Reads the normalized visible text of every element a selector matches, in document order.
1163
1431
  *
@@ -1228,6 +1496,25 @@ export declare function readRules(): readonly CSSRule[];
1228
1496
  */
1229
1497
  export declare function readStates(element: Element): readonly string[];
1230
1498
 
1499
+ /**
1500
+ * Reads one resolved CSS property from a real browser element.
1501
+ *
1502
+ * @param element - The element whose resolved style to inspect.
1503
+ * @param property - The CSS property name, registered or custom.
1504
+ * @returns The browser's resolved property value, trimmed; an empty string when the element resolves
1505
+ * none.
1506
+ *
1507
+ * @remarks
1508
+ * The value is trimmed, so what comes back is the value and never the whitespace around it. Internal
1509
+ * whitespace is kept: `--shadow: 0 0 2px` reads back with its spaces.
1510
+ *
1511
+ * @example
1512
+ * ```ts
1513
+ * readStyle(button, 'padding-left')
1514
+ * ```
1515
+ */
1516
+ export declare function readStyle(element: Element, property: string): string;
1517
+
1231
1518
  /**
1232
1519
  * Reads one element's rendered text the way a name computation reads it.
1233
1520
  *
@@ -1247,6 +1534,32 @@ export declare function readStates(element: Element): readonly string[];
1247
1534
  */
1248
1535
  export declare function readText(element: Element): string;
1249
1536
 
1537
+ /**
1538
+ * Reads one custom property from an element's resolved style.
1539
+ *
1540
+ * @param element - The element whose resolved style to inspect.
1541
+ * @param name - The custom property name, with or without its leading dashes.
1542
+ * @returns The resolved value, trimmed; an empty string when the element inherits no such property.
1543
+ *
1544
+ * @remarks
1545
+ * The dashes are optional because a token is spoken about both ways — `--surface` in a stylesheet
1546
+ * and `surface` in prose — and a reader that accepted only one spelling would turn that into a silent
1547
+ * empty string. An absent token reads as `''`, which is what the CSSOM returns and is
1548
+ * indistinguishable from a token declared empty; assert on the value you expect rather than on
1549
+ * presence.
1550
+ *
1551
+ * Resolution is inheritance, so a token declared on `:root` reads from any mounted descendant and
1552
+ * from an unmounted element reads as `''`. Use {@link readRootToken} where the declaration is the
1553
+ * document's.
1554
+ *
1555
+ * @example
1556
+ * ```ts
1557
+ * readToken(panel, 'surface') // '#ffffff'
1558
+ * readToken(panel, '--surface') // '#ffffff'
1559
+ * ```
1560
+ */
1561
+ export declare function readToken(element: Element, name: string): string;
1562
+
1250
1563
  /**
1251
1564
  * Reads the value a resolved control renders.
1252
1565
  *
@@ -1267,22 +1580,33 @@ export declare function readText(element: Element): string;
1267
1580
  export declare function readValue(role: string, name: string): string;
1268
1581
 
1269
1582
  /**
1270
- * Hands the tester pane back to the runner's own layout.
1583
+ * Hands the tester pane back to the runner's own layout, at the viewport it had before staging.
1271
1584
  *
1272
1585
  * @remarks
1273
1586
  * A staged pane is the runner's fitting scale suppressed, so a pane left staged outlives the capture
1274
1587
  * that needed it and every later act in the file happens on a surface the runner is no longer
1275
1588
  * fitting to its window. What that costs is not a wrong picture: it is a control whose page
1276
1589
  * coordinates fall outside the pane, which the runner's own layout then intercepts, so an ordinary
1277
- * press fails with the voice of a control that is covered. Calling this on an unstaged pane does
1590
+ * press fails with the voice of a control that is covered.
1591
+ *
1592
+ * The viewport goes back too, because a capture resizes the tester and the size it chose belongs to
1593
+ * the frame rather than to the file: a test that runs after one and reads a breakpoint would
1594
+ * otherwise read the last capture's variant. The size comes off the {@link CAPTURE_PANE} value
1595
+ * {@link stagePane} wrote onto the rule element, which is the reading taken before the first
1596
+ * staging. Calling this on an unstaged pane finds no such value, so it changes nothing and resizes
1278
1597
  * nothing.
1279
1598
  *
1599
+ * That hand-back is what makes {@link stagePane} and this pair a capture's staging rather than a
1600
+ * resize: the pair puts the tester back where it found it, so a suite that used it to reach a
1601
+ * breakpoint runs its next step at the old size. Call `page.viewport` from `vitest/browser` for a
1602
+ * journey's own size, and leave this pair to the capture.
1603
+ *
1280
1604
  * @example
1281
1605
  * ```ts
1282
- * releasePane()
1606
+ * await releasePane()
1283
1607
  * ```
1284
1608
  */
1285
- export declare function releasePane(): void;
1609
+ export declare function releasePane(): Promise<void>;
1286
1610
 
1287
1611
  /**
1288
1612
  * Deletes one IndexedDB database and reports what the request actually did.
@@ -1386,6 +1710,14 @@ export declare function resolveAccessible(role: string, name: string): HTMLEleme
1386
1710
  * This is the resolver the acting verbs use, so a click does not fail on a target the act itself
1387
1711
  * scrolls into view. Use {@link resolveAccessible} wherever the target must already be on screen.
1388
1712
  *
1713
+ * It runs two passes, and only the first one can return an element. The visible pass asks the role
1714
+ * engine for the exact name over the elements the accessibility tree presents, which is the name a
1715
+ * screen reader announces: an `aria-hidden` icon beside the text contributes nothing to it. The
1716
+ * hidden pass runs only when the visible pass found nothing at all, and it decides which refusal
1717
+ * the caller hears — a name the page carries nowhere, or a target that is there and out of reach.
1718
+ * That pass must include hidden elements to see a folded control, which is what puts a glyph back
1719
+ * into the computed name, so it asks with {@link computeNamePattern} rather than the exact string.
1720
+ *
1389
1721
  * @example
1390
1722
  * ```ts
1391
1723
  * resolveRendered('tab', 'Drafts')
@@ -1393,57 +1725,6 @@ export declare function resolveAccessible(role: string, name: string): HTMLEleme
1393
1725
  */
1394
1726
  export declare function resolveRendered(first: string, second?: string): HTMLElement;
1395
1727
 
1396
- /**
1397
- * Resolves any CSS color expression to straight sRGB channels, by asking the browser.
1398
- *
1399
- * @param value - Any value the `color` property accepts: a keyword, a hex triple, a `var()`
1400
- * reference, a `color-mix()`, or an already-computed `rgb()`.
1401
- * @returns The resolved color's channels, or `undefined` when the CSSOM refuses the value or the
1402
- * computed result names no color {@link parseColor} speaks.
1403
- *
1404
- * @remarks
1405
- * This is the live half of the pair {@link parseColor} opens. `parseColor` reads text and speaks
1406
- * only the computed syntaxes a cascade hands back; this stages a probe element, hands it to the real
1407
- * cascade, and reads back what the engine computed — which is the only way a keyword, a hex triple,
1408
- * or a `var()` reference becomes channels at all. The read itself goes through `parseColor`, so both
1409
- * halves agree on what a computed value means.
1410
- *
1411
- * The probe is mounted, because an unmounted element inherits nothing and a `var()` reference to a
1412
- * token declared on `:root` would resolve to the initial value instead. It is removed in a `finally`,
1413
- * so a value that throws on the way through leaves no node behind.
1414
- *
1415
- * Refusal is the CSSOM's: an expression it will not parse leaves the probe's inline `color` empty
1416
- * and this returns `undefined`. A `var()` naming an undeclared custom property is not refused,
1417
- * because the cascade accepts it and computes the inherited color, so a test that means to catch a
1418
- * missing token asserts on {@link token} rather than on this.
1419
- *
1420
- * @example
1421
- * ```ts
1422
- * rgba('rebeccapurple') // [102, 51, 153, 1]
1423
- * rgba('not-a-color') // undefined
1424
- * ```
1425
- */
1426
- export declare function rgba(value: string): Color | undefined;
1427
-
1428
- /**
1429
- * Reads one custom property from the document element.
1430
- *
1431
- * @param name - The custom property name, with or without its leading dashes.
1432
- * @returns The resolved value, trimmed; an empty string when the document declares no such property.
1433
- *
1434
- * @remarks
1435
- * This is {@link token} against `document.documentElement`, which is where a theme declares its
1436
- * tokens and where a `[data-theme]` switch retunes them. It exists as its own name because that
1437
- * element is the one a token question is nearly always about, and naming it at every call site
1438
- * buries the question.
1439
- *
1440
- * @example
1441
- * ```ts
1442
- * rootToken('surface')
1443
- * ```
1444
- */
1445
- export declare function rootToken(name: string): string;
1446
-
1447
1728
  /**
1448
1729
  * Sets the tester's viewport and renders the runner's pane at the size that viewport claims.
1449
1730
  *
@@ -1469,6 +1750,11 @@ export declare function rootToken(name: string): string;
1469
1750
  * the window puts its lower half beyond what a pointer can reach, so an ordinary press then fails
1470
1751
  * as a control outside the viewport, in a test that took no picture at all.
1471
1752
  *
1753
+ * This is a capture's staging alone. A suite that resizes the tester for a journey — a breakpoint
1754
+ * to drive, a variant to act at — calls `page.viewport` from `vitest/browser` and leaves the tester
1755
+ * there. Staging and releasing as a pair resizes and then undoes the resize, so the journey step
1756
+ * after it runs at the size the file started at.
1757
+ *
1472
1758
  * The rule is declared rather than written inline, because the runner writes its own scale onto the
1473
1759
  * pane as inline custom properties and rewrites them whenever the tester resizes. A declared rule
1474
1760
  * marked important outranks an inline value and survives every rewrite. It finds the pane by the
@@ -1478,6 +1764,11 @@ export declare function rootToken(name: string): string;
1478
1764
  * The wait is two frames rather than a delay: the first carries the resize into layout and the
1479
1765
  * second is the paint a screenshot reads.
1480
1766
  *
1767
+ * The viewport the tester had before this staging is written onto that rule element as the
1768
+ * {@link CAPTURE_PANE} value, in `<width>x<height>` form, and {@link releasePane} hands it back.
1769
+ * Staging an already-staged pane leaves that value alone, so a capture that stages a second time to
1770
+ * cover a taller document still releases to the viewport the tester started with.
1771
+ *
1481
1772
  * @example
1482
1773
  * ```ts
1483
1774
  * await stagePane(390, 844)
@@ -1485,51 +1776,6 @@ export declare function rootToken(name: string): string;
1485
1776
  */
1486
1777
  export declare function stagePane(width: number, height: number): Promise<void>;
1487
1778
 
1488
- /**
1489
- * Reads one resolved CSS property from a real browser element.
1490
- *
1491
- * @param element - The element whose resolved style to inspect.
1492
- * @param property - The CSS property name, registered or custom.
1493
- * @returns The browser's resolved property value, trimmed; an empty string when the element resolves
1494
- * none.
1495
- *
1496
- * @remarks
1497
- * The value is trimmed, so what comes back is the value and never the whitespace around it. Internal
1498
- * whitespace is kept: `--shadow: 0 0 2px` reads back with its spaces.
1499
- *
1500
- * @example
1501
- * ```ts
1502
- * style(button, 'padding-left')
1503
- * ```
1504
- */
1505
- export declare function style(element: Element, property: string): string;
1506
-
1507
- /**
1508
- * Reads one custom property from an element's resolved style.
1509
- *
1510
- * @param element - The element whose resolved style to inspect.
1511
- * @param name - The custom property name, with or without its leading dashes.
1512
- * @returns The resolved value, trimmed; an empty string when the element inherits no such property.
1513
- *
1514
- * @remarks
1515
- * The dashes are optional because a token is spoken about both ways — `--surface` in a stylesheet
1516
- * and `surface` in prose — and a reader that accepted only one spelling would turn that into a silent
1517
- * empty string. An absent token reads as `''`, which is what the CSSOM returns and is
1518
- * indistinguishable from a token declared empty; assert on the value you expect rather than on
1519
- * presence.
1520
- *
1521
- * Resolution is inheritance, so a token declared on `:root` reads from any mounted descendant and
1522
- * from an unmounted element reads as `''`. Use {@link rootToken} where the declaration is the
1523
- * document's.
1524
- *
1525
- * @example
1526
- * ```ts
1527
- * token(panel, 'surface') // '#ffffff'
1528
- * token(panel, '--surface') // '#ffffff'
1529
- * ```
1530
- */
1531
- export declare function token(element: Element, name: string): string;
1532
-
1533
1779
  /**
1534
1780
  * Reaches a named control only through natural forward Tab traversal from the current focus.
1535
1781
  *