Why can a Cypress snapshot render unstyled when the app's CSS comes from a CDN?
answer
- Not every stylesheet can be read
- Some styling is stored by reference
- Restore may hit the network again
- Rule text versus a bare href
- Cross-origin sheets keep only the URL
basics
~20 sCypress stores a stylesheet's rule text only when the browser lets it read the sheet. A cross-origin sheet is kept as its href alone, and restoring refetches it, so a changed or unreachable URL makes the snapshot render wrong.
solid answer
~50 sWhen Cypress captures a snapshot it walks the page's `<link rel="stylesheet">` and `<style>` elements and tries to read each sheet's rules. For a sheet the browser lets it read, it stores the rule text — hashed so identical sheets are kept once — and rewrites relative `url()` references to absolute ones so fonts and background images still resolve. For a cross-origin sheet the browser refuses that access, so Cypress falls back to storing only the `href`. Restoring that snapshot writes a fresh `<link rel="stylesheet" href="...">` into the head and the browser fetches it **at that moment**. That makes the styling of an old snapshot depend on a live network fetch: if the CDN is unreachable, has since shipped a new build at the same URL, or requires credentials the runner does not send, the restored ticket queue will not look like what the test actually saw.
go deeper
Know that a restored snapshot can look unstyled without the application being broken, and check the live page before raising it as a defect.
Explain the split: readable sheets are stored as rule text, unreadable ones as a bare href that the browser fetches again when the snapshot is restored.
Diagnose this quickly in a real session — separate a styling-delivery problem from a rendering problem, and lean on structural assertions rather than on appearance.
Set the expectation that appearance is never the evidence a snapshot provides, and keep team debugging conventions off anything that depends on a fetch nobody controls.
## Two ways a stylesheet can be captured A Cypress snapshot stores the page's markup and its styling on separate tracks. The clone of `<body>` has every `<style>` and `<link rel="stylesheet">` removed from it; the styling is captured as an ordered list beside it and re-inserted when the snapshot is restored. How each entry in that list is captured depends on one thing: **whether the browser will hand Cypress the sheet's rules**. - An inline `<style>` tag is easy — its rules are read straight off it. - A `<link>` to a same-origin stylesheet is read through the sheet's rule list and stored as **rule text**. - A `<link>` to a cross-origin stylesheet — a design system on a CDN, a font service, a third-party widget's styles — cannot be read. The browser either throws a security error or reports no rules at all, and Cypress stores **only the `href`**. There is a fourth case worth knowing: only stylesheets that apply to the screen are captured at all. A sheet whose `media` attribute is absent, or is `screen` or `all`, is collected; a `media="print"` sheet is skipped. ## What each case costs at restore time | Captured as | What restore does | Failure mode | |---|---|---| | Rule text from a `<style>` tag | Writes a `<style>` with that text | Essentially none | | Rule text from a same-origin `<link>` | Writes a `<style>` with that text | Essentially none | | A bare `href` | Writes a `<link>` the browser fetches now | Depends on the network and on the URL still serving the same bytes | The first two are self-contained: the bytes are already in the snapshot, so restoring it is a pure in-memory operation. The third is not. It is a **promise to fetch**, redeemed only when you go back and look. ## Why the by-reference case bites Storing an `href` seems harmless until you notice how far apart capture time and restore time can be: - **The URL now serves different bytes.** A CDN path that is not content-hashed can pick up a new build between the run and the moment you revisit a command, and the snapshot silently re-styles itself with the new CSS. - **The URL is unreachable.** Working offline, on a VPN that does not route to the CDN, or against a service having an outage leaves the restored page unstyled — everything is present in the DOM but laid out as if no CSS existed. - **The fetch needs credentials the fetch does not carry.** A stylesheet behind an authenticating gateway can be fetched fine by the application and then fail on the re-fetch. - **Timing changes what you see.** The re-fetch is asynchronous, so a restored snapshot can flash unstyled and then settle. None of this is a bug in the application. It is the capture being honest about what it could and could not read. ## Diagnosing it on a support-ticket queue If a restored snapshot of the ticket queue looks structurally right but visually wrong, work through it in this order: 1. **Check whether the styling comes from another origin.** Look at the `<link>` elements the app loads and note which ones are not on the app's own origin. 2. **Reload the page and compare.** If the live application also looks unstyled, the problem is the CDN, not the snapshot mechanism. 3. **Confirm the DOM is intact.** Inspect the restored elements. If the classes and structure are all present, you are looking at a styling-delivery problem, not a rendering bug in the app. 4. **Stop trusting appearance and assert on structure.** Classes, attributes and text in a restored snapshot are exactly what the page had; how it looks is the part that depends on a fetch you do not control. ## The habit this should form Treat a snapshot's **markup as archival** and its **appearance as provisional**. The DOM in a snapshot is a faithful record of an instant; the styling is a faithful record only to the extent that the stylesheets were readable. In practice that means an investigation should be built on assertions about structure and content — the row exists, it carries the escalated class, its text reads what it should — rather than on the restored page looking right, because looking right depends on infrastructure that was never part of the capture.
- Why does Cypress rewrite relative url() references in captured stylesheet rules?The rules are re-inserted as a `<style>` tag in the document rather than from the original stylesheet's location, so a relative `url()` would resolve against the wrong base. Cypress makes those paths absolute at capture time so background images and web fonts still load on restore.
- Which stylesheets does a Cypress snapshot skip entirely?Ones that do not apply to the screen. Cypress keeps a stylesheet only when its `media` attribute is absent or matches `screen` or `all`, so a `media="print"` sheet never reaches a snapshot and print-only styling cannot be inspected this way.
saying these in an interview costs you the question
- Assumes every stylesheet is inlined into the snapshot
- Thinks a snapshot is fully offline once captured
- Blames the app for an unstyled restored snapshot
- Expects print styles to appear in a snapshot