responsive-image-contracts
Installation
SKILL.md
Responsive image contracts
A responsive image is a contract between the file you ship and the box it renders into. Linters can check that the syntax is valid; they cannot tell you the sizes value matches the real layout, that the intrinsic-width labels are honest, or that the LCP image loads first. This lens reviews that judgment: role-based sizing (hero vs grid cell vs thumbnail), load priority, and which candidate the browser actually downloads.
Checklist (lead with the trap)
w-descriptorsrcsetwith nosizes-> the browser assumes100vwand downloads the widest candidate that fits the full viewport. A 400px thumbnail withsrcset="...1600w"and nosizesfetches the 1600w file on a wide screen: wasted bytes and, if it is the LCP element, a slower paint. Addsizesdescribing the real rendered width.sizesmust match the actual CSS layout, not a guess.sizes="100vw"on an image that renders in a 3-column grid (really ~33vw) over-downloads ~3x; a too-smallsizesunder-downloads and renders soft.sizesis a media-condition list read left-to-right, first true wins, with a bare fallback length last. Measure the rendered width across breakpoints; do not eyeball it.- Intrinsic-width descriptors must equal the file's real decoded width.
photo-1200.jpg 640wwhen the file is 1200px poisons the selection math — the browser thinks 640w is enough and serves blur, or the opposite. Verify eachw/xlabel against the actual pixel width of the file it points at. - The hero/LCP image must not be
loading="lazy". Lazy delays discovery and typically regresses LCP. Useloading="eager"(or omitloading) plusfetchpriority="high"so it wins network priority from discovery. Capfetchpriority="high"at 1-2 elements per page — if everything is high, nothing is — and keep genuinely below-the-fold imagesloading="lazy". - Every
<img>needswidth+heightattributes (or a reservedaspect-ratio) or it shifts layout when the file arrives (CLS). An image sized only in CSS (width: 100%; height: auto) with nowidth/heightattributes collapses to height 0 until it downloads, then shoves the content below it down the page. Keep the attributes so the browser derives anaspect-ratioand reserves the box up front, and pair them with CSSmax-width: 100%; height: autoto stay responsive. Do not override with a fixed CSSheight(it distorts the ratio) or drop the attributes (the collapse returns). - Too few candidates. One or two widths in
srcsetcannot serve DPR 2-3 phones and wide desktops well; provide a spread that brackets the realsizesrange. Do not over-file: a fixed-size icon or logo needs nosrcsetat all. - Descriptor type must match intent.
x(density) descriptors are for a fixed display size across DPRs;w(width) descriptors +sizesare for variable layout width. Mixing them, or addingsizesto anx-descriptorsrcset(where it is ignored), is a misuse —sizesis only meaningful withw. <picture>is for art-direction and format fallback, not resolution switching. Use<source media="...">when the crop/aspect genuinely changes per breakpoint, and<source type="image/avif">before webp/jpg for format fallback. Each<source>needs itsmediaortype, and a real<img src alt>must close the<picture>. If only the resolution changes, drop<picture>and putsrcset/sizeson the<img>.sizes="auto"is only valid withloading="lazy". On an eager image the value is invalid and browsers fall back to100vw(over-download). Keep explicitsizeslengths for eager/hero images; only useautoon lazy ones, and keepwidth/heightfor the aspect ratio.
Quick probes
Leads, not proof — HTML/JSX tags can span multiple lines, so confirm on the rendered tag before filing. Mechanical validity belongs to the linters below; use these to find candidates to judge.