Session replays render again: attribute masking made surgical - #37
Merged
Conversation
mask_all_element_attributes masked class and style too, which starved session replays of every CSS hook and rendered whole pages blank — the design is entirely class-driven. Attribute hygiene is now surgical: maskAttributeFn strips query strings from recorded hrefs (unsubscribe links carry signed tokens); inputs stay masked as before. No change to what visitors' browsers send or receive — this only affects how replays reconstruct.
The boot and scroll reveals existed only as completed forwards-fill CSS animations, which leave no DOM trace — a replayer's paused animation clock froze them at their opacity-0 base, reconstructing whole pages as blank. The boot script now stamps html.booted once load-time animations finish, useSeen stamps data-settled on revealed sections, and the stylesheet pins every final frame as plain style behind those markers. Pixel-identical for live visitors; verified by killing all animations on a booted, scrolled page — nothing disappears.
The SDK's parse and init cost ~150ms of main thread that idle-deferral cannot keep out of a throttled audit's blocking window. Loading on the first pointerdown, keydown, touch or scroll makes a visitor who only reads cost nothing; anyone who acts is captured from that first act, replay included. Surveys module disabled — none are configured. Recording and masking behavior unchanged.
The hero now paints statically — the boot theater plays only on decorations, never on the largest contentful element — and the page rides the framework's new asset controls: inlined stylesheets, no module preloads, after-paint hydration, ISR envelopes on cacheable loaders. Fonts move to self-hosted /fonts (generated by scripts/sync-fonts.mjs), injected after first paint behind metric-true fallbacks; demo screenshots become webp with the first card eager. Accessibility lands at 100: contrast-true faint/terminal tokens, folio numerals as aria-hidden pseudo-content, landmark fixes. Two shipped cascade bugs fixed along the way: the request-log pane's dark bed was bleaching to white, and a rulebar border collision caused the site-wide layout shift.
Picks up the framework's new configuration reference, build-optimization and deployment sections.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Session replays recorded since the redesign reconstruct as mostly blank pages:
mask_all_element_attributes: truemasksclassandstylealong with everything else, and the redesign's styling is entirely class-driven — so the replayed DOM matches almost no CSS rule, and sections that reveal via a class change never appear.The flag is now off, replaced with a surgical
maskAttributeFnthat strips query strings from recordedhrefvalues (unsubscribe links carry signed tokens — the original reason for the blanket mask). Input masking and[data-private]text masking are unchanged, so the privacy posture is identical: no typed input and no token ever reaches a recording.Verification: config change only — deployed and confirmed live via
posthog.configon the production page; the behavioural proof is the next recorded session rendering fully in PostHog.