Barebones CSS for Fluid Images
A few days back 
Primary goal: I want to test width: 100% versus max-width: 100% and how those interact with [width][height] and srcset attributes.
Now, my usual take was to pop some width: 100% CSS on that thing and call it a day. width: 100% CSS forces the image to fill up the width of the container. This overrides any [width] attribute you have set. This has the downside that the image can outgrow it’s own dimensions and can appear blurry.
Each case below uses a 200×200 image in both a 150px container (to shrink) and a 300px container (to grow).
width: 100%
Without [width][height]
Using [width][height]
The 100% in max-width refers to the container dimensions. Sometimes the [width] attribute will be smaller than this container, which means the image will not always be full width. Practically speaking, the image will never grow larger than its own internal or intrinsic dimensions.
Another way to think about this, the image width can range between 0 and [width], depending on the container size.
Editors note: the above section had a pretty glaring error and was corrected thanks to @CarolSaysThings, @HarryMoore2409, and @nhoizey! Sorry about that, y’all.
max-width: 100%
Without [width][height]
Using [width][height]
Let’s use srcset to add another eligible image width (now 200px and 400px) and see what happens. Spoiler alert: no surprises here! 🎉
srcset and width: 100%
Without [width][height]
Using [width][height]
Keeping our two eligible image widths in play (200px and 400px) let’s swap to use max-width: 100%.
srcset and max-width: 100%
Without [width][height]
Using [width][height]
This is where the rubber finally meets the road. For me, the above test was the most surprising part of the research—and why a deeper analysis of this perhaps introductory topic was worthwhile (for me).
When I traditionally used width: 100% it bulldozed the [width] attribute. But a strict max-width: 100% now competes with the [width] attribute. One solution is to add width: auto to pair with our max-width: 100% CSS.
September 9, 2021 Update: This article has been amended to add one additional caveat about mixing height: auto and width: auto.
That looks like this:
srcset and max-width: 100%
Using [width][height] and width: auto ⚠️ incurs CLS costs
When Eleventy Image generates markup for more than one size, the <img> element uses the lowest size/quality source. But this behavior has me thinking that when srcset is in play it should use the largest dimensions for the [width] and [height] attributes. I wonder what y’all think about that? Practically, it would look like this:
srcset and max-width: 100%
Using [width="400"][height="400"]
This makes max-width: 100% a bit more predictable, as the rendered size now matches the behavior when [width][height] are not included or when width: auto was left off. The maximum width now correctly matches the intrinsic width of the largest image in our srcset list.
February 24, 2021 Update: Eleventy Image v0.8.0 was released with the above [width][height] attribute optimization.
Again—practically I would recommend to pair max-width: 100% with width: auto to fix this in the easiest way but this might help avoid some confusion for some folks that aren’t aware of this.
September 9, 2021 Update: This article has been amended to add one caveat about mixing height: auto and width: auto.
Conclusions (aka TL;DR)
- All of these approaches operate the same when the container is smaller than the image.
- Using
width: 100%can give you some blurry images if you’re not careful with your container sizes (without Content Layout Shift penalties). - 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]! Use the largest[width]and[height]attributes to fix this. - ⚠️ Do not use
width: autopaired withheight: autoas this can incur Content Layout Shift penalties. - But perhaps esoterically I’m walking away with this remaining question: when you have multiple image sizes listed in a
srcsetlist, which dimensions do you use for the[width]and[height]attributes? I kinda want to use the largest one—any obvious downsides to that?
February 24, 2021 Update: Eleventy Image v0.8.0 was released to use the largest size for the [width][height] attributes.
Copy and Paste
Writing this blog post has swayed me to part from my width: 100% ways! I think this is what my boilerplate will be moving forward:
img {
max-width: 100%;
}
img[width][height] {
height: auto; /* Preserve aspect ratio */
}
/* Let SVG scale without boundaries */
img[src$=".svg"] {
width: 100%;
height: auto;
max-width: none;
}
February 24, 2021 Update: Chris Coyier posted a great follow up to this post on CSS Tricks with some super valuable extra information about how srcset affects rendered image dimensions.
Content Layout Shift Addendum
September 9, 2021 Update: Prior versions of this blog post recommended img[width] { width: auto; } to solve the problem of images rendering too small when the width and height attributes were smaller than the maximum intrinsic size supplied via the srcset attribute. The best fix here is to use the largest dimensions for your width and height attributes (even if you use a lower quality version for the src).
Additionally, 
width: auto in this way conflicts with the default styles for aspect-ratio and incurs Content Layout Shift penalties.
I tested and confirmed this to be true when you pair width: auto and height: auto together. You can view the tests on Codepen:
- ✅
<img>with{ max-width: 100% }no CLS penalties - ✅
<img>with{ max-width: 100%; width: auto }no CLS penalties - ✅
<img>with{ max-width: 100%; height: auto }no CLS penalties - ❌
<img>with{ max-width: 100%; width: auto; height: auto }console logs CLS penalties. - ✅
<img>with{ width: 100%; height: auto }no CLS penalties


