skip to content

An image placed inside a div with no padding or margins still leaves a few pixels of space below it. Which CSS layout rule causes that gap, and what removes it?

level: middleimportance: should knowfreq 48%

answer

  1. images are inline-level by default
  2. baseline, not the box bottom
  3. room reserved for descenders
  4. gap tracks parent line-height
  5. block or vertical-align fixes it

basics

~20 s

The image is an inline-level box, so it sits on the text baseline of its line box, and the descender space reserved below that baseline shows as a gap. Set the image to display: block, or align it with vertical-align: bottom.

solid answer

~50 s

Images are inline-level replaced elements, so the image joins an inline formatting context and, by default, `vertical-align: baseline` puts its bottom edge on the **text baseline** of the line box. A line box always reserves room below the baseline for descenders — the tails of letters like g and y — and that reserved strip is determined by the parent's `font-size` and `line-height`, not by the image. So the parent ends up a few pixels taller than the image. The giveaway is that the gap changes when you change the parent's `line-height` or `font-size` and disappears if you set `line-height: 0`. The usual fix is `img { display: block }`, which takes the image out of the inline formatting context entirely; `vertical-align: bottom` or `vertical-align: middle` also works if the image must stay inline. Making the parent a flex or grid container fixes it too, because that blockifies the image.

code

html · 21 lines
html
<style>
  .frame {
    border: 1px solid red;
    font-size: 16px;
    line-height: 1.5;
  }
  .frame img {
    width: 120px;
  }
  .fixed img {
    display: block;
  }
</style>

<div class="frame">
  <img src="cat.png" alt="">
</div>

<div class="frame fixed">
  <img src="cat.png" alt="">
</div>

go deeper

for a junior

Know that images are inline by default and sit on the text baseline, and that display: block on the image is the standard fix. Say that the space is not a margin.

for a middle

Explain the line box and its strut: the parent's font-size and line-height reserve descender space below the baseline, and a baseline-aligned image sits on top of that strip. Name at least two fixes and what each changes.

for a senior

Show the diagnosis: confirm computed margins are zero, change the parent's line-height to see the gap track it, then pick the fix that suits the context — block for layout images, vertical-align for icons inside running text.

for a principal

Own the systemic answer: decide whether the design system's media primitives are block-level by default, so contributors never rediscover this, and make sure the choice does not break the legitimate inline-icon case.

## The image is text, as far as layout is concerned An `<img>` is a **replaced element** — its content comes from outside CSS — but its default outer display type is inline-level. So when it is the only child of a `div`, the browser does not stack it as a block. It creates an **inline formatting context** inside the `div`, generates a line box, and places the image into that line box as if it were an enormous word. ## Line boxes, the strut, and the baseline Every inline formatting context has a **strut**: an invisible zero-width inline box carrying the block container's own `font-family`, `font-size` and `line-height`. The strut fixes where the baseline sits and how much room is reserved above it (ascent) and below it (descent), even when there is no visible text on the line. The image's default alignment is `vertical-align: baseline`, which puts the **bottom edge of the image** on that baseline. The strut's descent still has to fit, so the line box extends below the baseline by that descent — typically a couple of pixels at a 16px font, more with a large `line-height`. The `div` sizes itself to the line box, so it is taller than the image by exactly that descent. Nothing about the image changed; the space is the room the line reserves for letters that hang below the baseline. This is why the two diagnostic tests work: ```css /* Test 1: does the gap track the parent's typography? */ .wrapper { line-height: 3; } /* gap grows */ /* Test 2: remove the reserved descent */ .wrapper { line-height: 0; } /* gap disappears */ ``` If the gap responds to either, it is baseline space, not a margin, padding or border. ## The fixes **`display: block` on the image.** This is the most robust answer. A block-level image no longer participates in an inline formatting context, so there is no line box, no strut and no baseline to sit on. The parent's height becomes the image's height. ```css img { display: block; } ``` **`vertical-align: bottom` on the image.** This keeps the image inline but aligns its bottom edge with the bottom of the line box rather than the baseline, so the descent strip is no longer below it. `vertical-align: middle` is also commonly used and removes the visible gap in the single-image case, because the image dominates the line box height. **`line-height: 0` (or `font-size: 0`) on the parent.** This shrinks the strut so there is no descent to reserve. It works, but it changes the typography of anything else in that container, so it is a poor default for a container that also holds text. **Make the parent a flex or grid container.** Children of a flex or grid container are **blockified**: an inline-level child is treated as block-level, so the image never joins an inline formatting context in the first place and the gap cannot occur. ## The same cause, other symptoms This is not an image quirk. Any inline-level box that is taller than the text around it produces the same effect: `<svg>`, `<canvas>`, `<iframe>`, `<video>`, and any `display: inline-block` box. A row of `inline-block` cards whose container is mysteriously a few pixels taller than the cards has exactly this cause. Likewise, cards of different heights in such a row line up on their **baselines** rather than their tops, which looks like a bug and is the same rule seen from a different angle. ## When you should not remove it Baseline alignment is the correct behaviour when the image genuinely belongs in running text — an inline icon next to a label, or an emoji-sized glyph in a sentence. There, forcing `display: block` would break the line. The right move is to keep the image inline and tune the alignment: `vertical-align: middle` or a small `vertical-align` length offset so the icon's optical centre lines up with the text's x-height. The rule of thumb is: an image that is *content in a sentence* stays inline and gets aligned; an image that is *a block in a layout* becomes `display: block` or a flex/grid child, and then the gap question never arises.

  • Why does the gap change size when you change the parent's line-height?
    Because the space is the descent of the line box's strut, and the strut is built from the block container's own `font-size` and `line-height`. A taller `line-height` reserves more room below the baseline, so the strip under the baseline-aligned image grows. The image itself never changes size.
  • Would putting the image in a flex container also remove the gap?
    Yes. A flex or grid container blockifies its children, so the image becomes a block-level flex item rather than an inline-level box. With no inline formatting context there is no line box, no strut and no baseline for the image to sit on, so no descender space is reserved.

saying these in an interview costs you the question

  • Assumes the parent has hidden padding or a margin
  • Says images ship with a default bottom margin
  • Hides it with a fixed negative margin-bottom
  • Blames the div's border rather than the line box
  • Cannot explain why line-height: 0 makes it vanish

context