Zach’s ugly mug (his face) Zach Leat­herman

Barebones CSS for Fluid Images

On Twitter (archived) 11k 00 January 30, 2021

A few days back CarolSaysThings’s AvatarCarolSaysThings posed what I expected to be a simple question until I started to question what I already knew. Carol had some image markup (generated by Eleventy Image) and was asking the best way to make the image fluid (match the width of its container). I knew I understood this well enough to use it in my own work but I started to realize that perhaps I didn’t know this topic well enough to teach it so I dove in a little deeper.

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]

The sample Nebula Image from Unsplash on the Eleventy Image docs
The sample Nebula Image from Unsplash on the Eleventy Image docs

Using [width][height]

The sample Nebula Image from Unsplash on the Eleventy Image docs
The sample Nebula Image from Unsplash on the Eleventy Image docs

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]

The sample Nebula Image from Unsplash on the Eleventy Image docs
The sample Nebula Image from Unsplash on the Eleventy Image docs

Using [width][height]

The sample Nebula Image from Unsplash on the Eleventy Image docs
The sample Nebula Image from Unsplash on the Eleventy Image docs

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]

The sample Nebula Image from Unsplash on the Eleventy Image docs
The sample Nebula Image from Unsplash on the Eleventy Image docs

Using [width][height]

The sample Nebula Image from Unsplash on the Eleventy Image docs
The sample Nebula Image from Unsplash on the Eleventy Image docs

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]

The sample Nebula Image from Unsplash on the Eleventy Image docs
The sample Nebula Image from Unsplash on the Eleventy Image docs

Using [width][height]

The sample Nebula Image from Unsplash on the Eleventy Image docs
The sample Nebula Image from Unsplash on the Eleventy Image docs

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

The sample Nebula Image from Unsplash on the Eleventy Image docs
The sample Nebula Image from Unsplash on the Eleventy Image docs

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"]

The sample Nebula Image from Unsplash on the Eleventy Image docs
The sample Nebula Image from Unsplash on the Eleventy Image docs

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 with srcset—it may cap smaller than you want when using [width]! Use the largest [width] and [height] attributes to fix this.
  • ⚠️ Do not use width: auto paired with height: auto as 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 srcset list, 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, radumicu’s Avatarradumicu kindly pointed out that using 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:


< Older
Don’t Shut Down Your Business! Instead Use Eleventy Image
Newer >
Jamstack 101: Getting Started with Eleventy

Zach Leatherman IndieWeb Avatar for https://zachleat.com/is a builder for the web at Font Awesome and the creator of Build Awesome (née IndieWeb Avatar for https://www.11ty.devEleventy/11ty), an award-winning open source website generator. He measures website performance with speedlify and at one point became too fixated on web fonts. He has given 89 talks in nine different countries at events like Beyond Tellerrand, Smashing Conference, Jamstack Conf, CSSConf, and The White House. Formerly part of CloudCannon, Netlify, Filament Group, NEJS CONF, and NebraskaJS. Learn more about Zach »

Shamelessly plug your related post

These are webmentions via the IndieWeb and webmention.io.

Sharing on social media?

This is what will show up when you share this post on Social Media:

How did you do this? I automated my Open Graph images. (Peer behind the curtain at the test page)