You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(jspsych): capture document stylesheets so replay preserves CSS
Previously the recorder only serialized the display element subtree, so
replays reconstructed DOM structure but rendered unstyled — class hooks
like .jspsych-display-element had no rules to attach to.
Snapshot document.styleSheets at session start. For each <style> tag we
record the resolved cssRules text (falling back to textContent); for each
<link rel=stylesheet> we record the href and, when accessible, the
resolved CSS so cross-origin sheets degrade to href-only rather than
breaking. Also covers @import-only sheets that have no DOM owner.
|`rng.seed`| The seed installed via `jsPsych.randomization.setSeed` for the session, or `null` if `Math.random` was already non-native at recording start. |
48
50
|`rng.math_random_patched`|`true` while recording is active; `Math.random` is wrapped to log every call into `rng_calls`. |
49
51
|`display_element_id`| The `id` attribute of the display element (`#jspsych-content` by default). |
52
+
|`stylesheets`| Snapshot of every stylesheet attached to the document at session start. See [Stylesheets](#stylesheets). |
50
53
|`trials`| Per-trial recordings, in chronological order. See [`TrialRecording`](#trialrecording). |
|`rng_calls`| Chronological log of every `Math.random` output. Includes calls outside trial boundaries (parameter eval, ITI, `on_finish`). |
@@ -120,7 +123,32 @@ Every node in `initial_dom` is assigned a monotonically-increasing integer `id`.
120
123
- Canvas/WebGL pixel content. Only the element and its dimensions are recorded.
121
124
- Audio/video media data. Only playback events (`media.play`, `media.pause`, etc.) and the source URL are recorded.
122
125
- Shadow DOM. jsPsych does not use it in core; recordings will not capture mutations inside shadow roots.
123
-
- Styles applied via `<link rel="stylesheet">` referencing external sheets. The link element itself is recorded; the resolved CSS is not. Replayers generally need to load the same stylesheets out-of-band.
126
+
- Stylesheet additions, removals, or rule mutations that occur after `start()`. The session-level [stylesheets](#stylesheets) snapshot is taken once at session start; it is not updated when later trials inject `<style>` tags or modify rules at runtime.
`stylesheets` is the snapshot of every entry in `document.styleSheets` at session start. It exists so a replayer can re-apply the same CSS to the reconstructed DOM — `initial_dom` carries class hooks like `.jspsych-display-element`, and without the matching rules the replay would render unstyled.
137
+
138
+
| Field | Description |
139
+
| ----- | ----------- |
140
+
|`kind`|`"inline"` for `<style>` tags; `"link"` for `<link rel="stylesheet">`. |
141
+
|`css`| Resolved rule text (joined `cssRules.cssText`). `null` for `<link>` sheets when `cssRules` access throws (cross-origin sheets without CORS headers). |
142
+
|`href`| The link's resolved URL. Replayers can refetch the source from this URL when `css` is `null`. |
143
+
|`media`| The sheet's `media` attribute, or `null` if unset. |
144
+
145
+
**Replay guidance.** For each entry, inject a `<style>` element with the captured `css` text into the replayer's document head. When `css` is `null` for a `link` entry, fetch the stylesheet from `href` (or substitute a known-good copy of the same asset) and inject the result. Apply the `media` attribute on the injected element so media-conditional rules behave correctly.
146
+
147
+
**Limitations.**
148
+
149
+
- Snapshot is taken once at `start()`. Later mutations to the document's stylesheets — additions, removals, or `CSSOM` rule edits — are not tracked.
150
+
-`@import` rules within a captured stylesheet are recorded as text. The imported sheet itself is not inlined; if the replayer's environment cannot resolve the import URL, those rules will not apply.
151
+
- Pseudo-classes (`:hover`, `:focus`) and media queries are recorded as part of the rule text but only take effect during replay if the replayer reproduces the corresponding state or viewport.
0 commit comments