A page uses an HTML <picture> with several art-directed <source> elements, and the narrow mobile crop keeps appearing on wide desktop screens. Given how the browser selects a source, what are the likely causes?
answer
- walk stops at the first hit
- earlier wins, the cascade is the opposite
- an unconditional source shadows everything below
- nothing matched looks like the wrong match
- viewport, not the element's container
basics
~20 sAlmost always source order or overlapping media conditions: the walk stops at the first matching source, so a broad condition listed above a narrow one wins everywhere. Other causes are a source with no matching media at all, srcset misspelled as src, and markup order not matching the intended priority.
solid answer
~50 sStart from the algorithm: the browser walks `<source>` children in document order and commits to the **first** one whose `media` matches and whose `type` it can decode. Given that, a mobile crop on desktop has a short suspect list. Most often the conditions overlap and the mobile source is listed first — `(max-width: 600px)` above `(min-width: 900px)` is fine, but a source with no `media` at all, or one with a condition that is true on every screen, shadows everything below it. Second, the desktop `<source>` may never match: a typo in the query, a `min-width` above any real viewport, or `srcset` written as `src`, which is silently inert inside `<picture>`. Third, if no source matches, the `<img>` renders — and if the `<img>` holds the mobile file, that looks identical to the bug. I would confirm by inspecting the image element's currently-rendered source in devtools rather than guessing from the markup.
go deeper
Know that sources are read top to bottom and the first match wins, so a source with no media attribute placed above others makes them unreachable.
Explain the full selection walk including type, and articulate that a fall-through to the img looks on screen exactly like selecting the wrong source — so you check which file was actually requested rather than reading intent from the markup.
Diagnose from behaviour: inspect the rendered img and the network request, separate no-match from wrong-match, and name the combined media-plus-type gap where a crop group lacks an untyped entry.
Treat duplicated breakpoints between markup and stylesheet as the systemic defect. Push for generated markup from single-sourced breakpoint tokens and a review rule that art-directed images are never hand-authored per page.
## Reason from the algorithm, not from the markup Every bug in this area dissolves once you hold the selection rule precisely: walk the `<source>` children **in document order**; for each, if it declares `media` the query must match, and if it declares `type` the browser must be able to decode it; take the **first** that passes and resolve its `srcset`; if none pass, use the `<img>`. There is no scoring, no specificity, no "best match" — just the first hit. That single rule generates the whole differential diagnosis. ## Cause 1: a shadowing source above the one you wanted The classic form: ```html <picture> <source srcset="square.jpg"> <!-- no media: always matches --> <source media="(min-width: 900px)" srcset="wide.jpg"> <!-- never reached --> <img src="square.jpg" alt="Ada at the analytical engine"> </picture> ``` A `<source>` with no `media` and no `type` matches unconditionally, so everything after it is unreachable. The same thing happens with a condition that is merely too broad — `(min-width: 320px)` above `(min-width: 900px)` is true on every desktop, so the walk stops on the first line. The fix is to treat the source list as an ordered decision table: narrowest, most specific condition first, broadening downward, with the unconditional candidate on the `<img>` where it belongs. People who carry over CSS instincts author it in ascending mobile-first order and get the inverse of what they intended, because in markup **earlier wins** while in the cascade **later wins**. ## Cause 2: the desktop source never matches If the intended source cannot match, the walk falls through to the `<img>` — which usually holds the small crop, so the symptom is identical to shadowing. Things that make a source unmatchable: - **A malformed media query.** An invalid query never matches; there is no error and nothing in the console. - **`src` instead of `srcset`.** Inside `<picture>`, `<source>` uses `srcset`. `src` is the `<video>`/`<audio>` spelling and is inert here, so the source is effectively empty. - **A `type` the browser cannot decode**, when the desktop crop was only ever offered as AVIF and the fallback JPEG line for that crop is missing. That is the combined-axis bug: with both `media` and `type` in play, each crop group needs an entry with no `type` so the crop survives when the format does not. - **A `min-width` threshold above any real viewport**, often after a units mistake. ## Cause 3: what the media query is measured against `media` on `<source>` is evaluated as a media query against the viewport and device — the same context a stylesheet media query sees. It is **not** measured against the element's container or the space the image will occupy after layout. If the image sits in a narrow sidebar on a wide page, `(min-width: 900px)` is true and the wide crop is selected even though the slot is 300 px. Teams who reason about the image's own box rather than the viewport ship exactly this bug. The corollary is a structural one worth stating in an interview: the breakpoints inside `media` attributes are a *second copy* of your layout breakpoints. Nothing keeps the two in sync, so a stylesheet refactor that moves the desktop breakpoint from 900 px to 1024 px silently opens a 124 px band where the layout is desktop and the crop is mobile. That is why these values should be emitted from one source by a component or build step rather than typed per page. ## Confirming rather than guessing Diagnose empirically. The `<img>` is the element that renders, so inspect it and read which file it actually resolved — devtools expose the current source on the image element — and check the network panel for which variant was requested. That distinguishes cause 1 and 2 (a source matched, but the wrong one, versus nothing matched) in seconds, whereas reading the markup invites you to see the intent instead of the behaviour. Also check at real viewport widths, not by resizing a window past a breakpoint and assuming a swap. Verify a fresh load at the target width. ## The one-line answer "First match wins in document order, so either something above it matched first or the desktop source could not match at all — and if nothing matches you get the `<img>`, which is usually the small crop, so those two failures look the same on screen."
- Why does authoring the source list mobile-first, the way you would write CSS, produce the wrong result?Because the two systems resolve conflicts in opposite directions. In CSS, later rules of equal specificity win, so ascending min-width order works. In `<picture>`, the walk stops at the first match, so an ascending list means the smallest threshold matches on every screen and shadows everything beneath it. Author the source list narrowest-condition-first.
- An image sits in a 300px sidebar on a 1400px page and keeps loading the huge desktop crop. Why?`media` on a `<source>` is a media query, evaluated against the viewport and device — not against the element's container or its post-layout size. On a 1400 px viewport a `(min-width: 900px)` condition is true regardless of how narrow the slot is, so the wide crop is selected. Art-directing by container size is not something `media` can express.
- How do you stop the media values in markup from drifting away from the CSS breakpoints?Single-source them. Emit both from one set of named breakpoint tokens through a component or build step, so a change updates markup and stylesheet together. Hand-typed queries in page markup drift the moment someone refactors layout, and the resulting mismatch is a narrow viewport band that no one tests.
- What is the fastest way to confirm which candidate the browser actually chose?Inspect the `<img>` — it is the element that renders — and read the resolved source devtools report on it, then confirm against the network panel to see which file was requested. That separates "a source matched, but the wrong one" from "nothing matched, so the img fallback rendered", which look identical on screen.
saying these in an interview costs you the question
- the most specific media query wins, like CSS specificity
- the media query measures the image's container
- a later source overrides an earlier match
- browsers pick whichever candidate is closest in size
- src and srcset are interchangeable on source