D
P
0
← All articles Baca dalam Bahasa Indonesia

Shopify, Liquid & the Theme CLI

Logo Squished Even Though `width: auto` Is Set? An `img { max-width: 100% }` Reset Clamping to the Parent

· · 5 min read
Logo Squished Even Though `width: auto` Is Set? An `img { max-width: 100% }` Reset Clamping to the Parent

The symptom

I was porting a store's header and footer design into a new theme. The logo came as a square image file, and only the wordmark part of it needed to be visible. The trick I used is a common one: a small 200×36 wrapper with overflow: hidden, and the image scaled up to height: 280px with width: auto, so the wrapper acts as a window that crops away the rest of the artwork.

Once it went in, the result was wrong in a way that itched. The crop height was right, the position was right, but the wordmark looked squished. The letters were pulled inward horizontally, like text forced into a column that is too narrow. And it was not just the header. The footer had exactly the same problem.

My first instinct was to blame myself: wrong height value, or some leftover transform from the old theme scaling the image non uniformly. I checked both, and both were clean. width was clearly auto, so in theory the browser should have been deriving the width from the image's own ratio.

The number that gave it away

What ended the guessing was not reading more CSS. It was measuring the element. I hovered the image in DevTools and looked at the box dimensions:

expected : 280 × 280
actual   : 200 × 280

The source file is square and the height is 280, so the width should have been 280 too. Instead it was 200, a number I had never written anywhere for this image. But 200 did look familiar, because it is exactly the width of the wrapper.

So I switched to the Computed tab, looked up max-width, and there it was:

max-width: 100%

Not from any rule of mine. That value came from the theme's base reset.

The root cause

The reset is the classic line you will find in almost every modern theme:

img,
picture,
video,
canvas,
svg {
  max-width: 100%;
}

It looks harmless, and usually it is. But the order the browser resolves sizes in gives that line a side effect you never see coming.

When height is explicit and width is auto, the browser first derives the width from the image's intrinsic ratio. For a square file at 280px tall, that gives 280px. Only then does min/max clamping run: the computed width exceeds max-width, which resolves to 100% of the parent, or 200px, so the used width gets cut down to 200px.

What does not happen is any correction afterwards. The height stays at 280px because I wrote it explicitly rather than leaving it auto, so nothing re-derives it from the ratio. The end result is 200×280 for a file that is square, rendered at the wrong aspect ratio. The squish is not an illusion. The browser really is compressing it.

This also explains why that reset is safe in almost every other case. The normal pattern is a width that follows the container plus height: auto. If the width gets clamped, the height is recomputed proportionally, so the ratio survives. The reset only bites in the exact opposite setup: one dimension pinned by hand, the other left auto, and the auto result larger than the parent.

Worth separating this from two neighbours it often gets confused with. This is not an element being squeezed by flexbox, and not an SVG stretched by its aspect ratio attribute. Here the element is a plain raster image, and the compression comes from the cascade, from a rule I never wrote and did not know was active.

The fix

One declaration, on the logo's own selector:

.site-logo img {
  height: 280px;
  width: auto;
  max-width: none; /* override the theme reset */
}

max-width: none lifts the ceiling, the auto calculation survives intact, and the image goes back to 280×280. The overflow: hidden wrapper keeps doing its job of cropping away what should not be visible.

What I deliberately did not do: delete or loosen the global reset. That line exists for a good reason, which is keeping content images from blowing out of their containers on narrow screens. Ripping it out for the sake of one logo trades a visible bug for a dozen that will show up later. Override at the one element that intentionally exceeds its parent, not at the global level everything else depends on. Header and footer both came right once each logo's own selector got that declaration.

For a quick sanity check, I compare the image's intrinsic ratio against the ratio of its rendered box:

const el = document.querySelector(".site-logo img");
const box = el.getBoundingClientRect();
console.log(el.naturalWidth / el.naturalHeight, box.width / box.height);

If those two numbers disagree, the image really is being distorted, and you can stop hunting through the logo file itself.

What I took away