Fluid images with srcset: where the defaults bite
Plain markup for a responsive image starts with nothing but the element itself:
<img src="./img.jpg" alt="" />
Without any CSS, that image renders at its intrinsic size — a 400px-wide source draws 400px wide. Adding width and height attributes is still worthwhile, since the browser can reserve space before the bytes arrive, even when the image ends up fluid:
<img src="./img.jpg" alt="" width="400" height="300" />
Still unremarkable. Toss in max-width: 100% and the image behaves as expected: space is preserved, it shrinks with the container, and it never grows past its natural size.
Adding multiple sources changes the layout
Set max-width aside for a moment and feed the image several candidates:
<img src="./img.jpg" alt=""
/>
The width blows out. This does not render at 200px or 400px but at 100vw, apparently because that is the default sizes value. The common mental model treats sizes as a hint for source selection rather than as layout information — but that model is wrong here, and layout is genuinely affected in every browser tested. Changing only the sizes attribute is enough to see it.
The width attribute caps smaller than intended
Restore the responsible markup by adding width and height back in:
<img src="./img.jpg" alt="" width="200" height="137"
/>
The blowout is gone, with or without sizes, but a new quirk appears: it works much like max-width: 200px. Sources wider than 200px are available, yet the rendered width is capped at 200px.
Using
max-width: 100%constrains the image to the container, but be careful when you use this withsrcset—it may cap smaller than you want when using[width]! Pair withwidth: autoto fix this.
Pairing the two declarations settles all of the above:
img {
max-width: 100%;
}
img[width] {
width: auto; /* Defer to max-width */
}
img[width][height] {
height: auto; /* Preserve aspect ratio */
}
/* Let SVG scale without boundaries */
img[src$=".svg"] {
width: 100%;
height: auto;
max-width: none;
}


