How far should a Cypress 16 suite lean on `visibilityStrategy: 'legacy'`?
answer
- A bridge with a demolition date
- Deprecated in the release that introduced it
- Scope it per suite, not globally
- The override list is the migration backlog
- Rewrite to algorithm-agnostic assertions
basics
~20 sTreat it as a short migration crutch, not a setting. It is deprecated and scheduled for removal, so use it to keep named specs green through one upgrade while you rewrite the assertions that depended on ancestor clipping.
solid answer
~40 s`visibilityStrategy` exists so a Cypress 16 upgrade need not be a suite-wide rewrite, and it was deprecated in the same release: both the option and the `'legacy'` value are slated for removal. The default `'modern'` delegates to the browser's `Element.checkVisibility()`, so it no longer treats overflow clipping, scroll position, zero-scale transforms, backface rotation or coverage of fixed elements as hidden. Setting `'legacy'` globally buys time but freezes the whole suite on an algorithm with an end date. The defensible shape is to set it per suite or per test on the specific specs that fail, log each one as migration backlog, and convert them to algorithm-agnostic assertions — geometry comparisons, `aria-hidden`, `scrollWidth` against `clientWidth`. A global `'legacy'` is justified only when the failing set makes a staged migration the riskier option.
go deeper
Know that Cypress 16 changed how visibility is decided and that visibilityStrategy exists to opt back into the old algorithm.
Be ready to name what modern no longer treats as hidden, and to show that the option can be scoped to a suite or a test rather than the whole run.
Demonstrate the migration: find the failing specs, scope the override, and replace each assertion with one that passes under either algorithm.
An interviewer at this level expects the plan attached to the flag — who owns removal, what the deprecation costs at the next major, and the rule for new specs.
## What the option is for Cypress 16 changed how visibility is decided. The default `visibilityStrategy: 'modern'` delegates to the browser's own `Element.checkVisibility()` API, which is faster and matches what the browser itself calls visible. `visibilityStrategy: 'legacy'` opts back into the ancestor-walking algorithm from Cypress 15 and earlier. It exists so that an upgrade does not have to be a suite-wide rewrite — and it was **deprecated in the same release**: the option and the `'legacy'` value are both slated for removal in a future major version. That is the whole shape of the decision. `'legacy'` is a bridge with a demolition date, so the question is never "should we set it?" but "what is the plan for taking it back out?". ## What the modern algorithm stops calling hidden The modern check reports an element as hidden when its bounding rect has zero width or height, or when `checkVisibility()` says so — `display: none`, `visibility: hidden` or `collapse`, `content-visibility: hidden`, and `opacity: 0` for a direct visibility assertion. It intentionally does **not** detect the cases the ancestor-walking algorithm did: - clipping by an ancestor's `overflow: hidden` - being scrolled out of view inside an `overflow: auto` or `scroll` ancestor - a `transform` that scales the element to zero on one axis - a rotation past 90 degrees with `backface-visibility: hidden` - being covered, for `fixed` or `sticky` positioned elements For a library catalogue, the practical fallout is a collapsed filter panel built with `max-height: 0` and `overflow: hidden`: the wrapper still reports hidden because its rect collapses, but the checkboxes inside it, which have their own dimensions, do not. ## The staged position, and why it is the defensible one 1. **Do not set it globally as the first move.** A global `'legacy'` freezes the entire suite on the deprecated path and hides how many specs actually depend on it. 2. **Scope it to the specs that fail.** `visibilityStrategy` can be set per suite or per test, so a failing `describe` can carry `{ visibilityStrategy: 'legacy' }` while everything else runs on the default. 3. **Record each one.** The list of scoped overrides is the migration backlog, and it should shrink between releases rather than sit still. 4. **Rewrite the assertion to be algorithm-agnostic.** Compare geometry directly for a scroll-clipped element, assert `aria-hidden="true"` on a collapsed wrapper, compare `scrollWidth` with `clientWidth` for truncated text. These pass under either strategy, which is what makes them the real fix. ## When a global setting is genuinely the right call There is an honest case for setting it globally, and it is a scheduling one, not a technical one: when the failing set is large enough that a staged migration inside the upgrade window is riskier than shipping the upgrade with the bridge in place. Taking the whole of Cypress 16 — the performance work, the retrying cookie and storage queries, memory management — in exchange for one deprecated flag can be the right trade. What makes it defensible rather than negligent is that it comes with the rest of the plan attached: a ticket, an owner, and a rule that new specs are written against the default. ## What to weigh, and what to say out loud - **The deprecation is the dominant fact.** Every spec left on `'legacy'` will fail on the major version that removes it, and it will fail then in a batch, at a moment nobody chose. - **The modern algorithm is closer to the browser's own definition**, so an assertion that only passes under `'legacy'` is often asserting something the browser does not consider hidden. - **Some legacy behaviour was genuinely useful** — scroll-clipping in particular caught real bugs — so migrating means replacing the intent, not just deleting the assertion. - **Mixed settings across a suite are a cost of their own.** Two visibility semantics in one codebase is a thing every reader has to know about; keep the overrides few and visible. The line to hold: `'legacy'` buys time for a specific, listed set of specs, and every use of it carries a plan for its own removal. A team that cannot say which specs need it, or when it comes out, has not made a decision — it has deferred one onto whoever runs the next major upgrade.
- Which cases does the modern strategy stop calling hidden?Clipping by an ancestor's `overflow: hidden`, being scrolled out of an `overflow: auto` container, a transform scaling an element to zero on one axis, a rotation past 90 degrees with `backface-visibility: hidden`, and coverage of a `fixed` or `sticky` element. Those were legacy-only rules.
- How would you rewrite an assertion that only passes under `'legacy'`?Assert the same user-visible fact in a way both algorithms agree on: compare the element's bounding rect with its scroll container's for a clipped element, assert `aria-hidden="true"` on a collapsed wrapper, or compare `scrollWidth` with `clientWidth` for truncated text.
saying these in an interview costs you the question
- Set legacy globally and forget about it
- Legacy is the safer long-term default
- The modern algorithm is simply less accurate
- Nothing needs rewriting once legacy is set
- Deprecated options can be relied on indefinitely