Search code examples
cssflexbox

Why does CSS flex seem to apply padding of inner element twice?


I've found a weird quirk with CSS display: flex where it seems to be applying the padding of an inner element twice.

I've extracted this snippet from my project. Each paragraph should be 21px high, while their inner spans have some padding that brings their total height to 20.94px, just fitting inside. Without padding the spans would have a height of 17px, so this padding adds 3.94px.

The last paragraph however is set to display: flex. The height of the final span is 24.94px, which = 21px + 3.94px. So it's not exactly applying the padding twice, because that would be 20.94 + 3.94. Instead it's applying the padding to the height of the parent paragraph? Which really makes no sense at all! Or maybe instead the line-height is being calculated from a different value?

Any ideas what exactly is happening here, and what I can do to return the final paragraph to its proper height?

In case this is a browser quirk/bug more than a CSS quirk, I'm testing this in Chrome 116.

p {
  font-family: Georgia;
  font-size: 15px;
  line-height: 1.4;
  margin: 0;
}

p span {
  padding: 0.132em 0;
}

p:last-child {
  display: flex;
}
<div>
  <p><span>Paragraph 1</span></p>
  <p><span>Paragraph 2</span></p>
  <p><span>Paragraph 3</span></p>
</div>


Solution

  • Inline Flex Items

    Problem

    <span>s are inline elements, which means it is assigned display: inline by default. Padding only applies to padding-left and padding-right for inline elements, so in the OP CSS:

    p span {
      padding: 0.132em 0;
    }
    

    Is actually:

    p span {
      padding: 0;
    }
    

    Moreover if any flex item (ex. p:last-child span) is display: inline, it will behave as display: inline-block. An inline-block element behaves like an inline element but padding-top and padding-bottom applies to it, hence the last <p> is taller than the other two <p>s.

    Solution

    Since each <span> is supposed to have padding-top and padding-bottom, simply assign display: inline-block to each <span>:

    p span {
      display: inline-block; /* Add this */
      padding: 0.132em 0;
    }
    

    In the example below, each <p> is outlined in blue and each <span> is outlined in red. Note the first three <p>s the OP CSS is applied and the first two <p>s are shorter in height than the third <p>. Then in the last three <p>s display: inline-block is applied to each <span> and the height of each <p> is equal.

    Original Post refers to either you or your posted question.

    :root {
      font: 15px/1.4 Georgia;
    }
    
    div+div {
      margin-top: 25px;
    }
    
    p {
      margin: 0;
      outline: 1px dashed blue;
    }
    
    p span {
      padding: 0.132em 0;
      outline: 2px dashed red;
    }
    
    p:last-child {
      display: flex;
    }
    
    .solution span {
      display: inline-block;
    }
    <div class="original">
      <p><span>Paragraph 1</span></p>
      <p><span>Paragraph 2</span></p>
      <p><span>Paragraph 3</span></p>
    </div>
    
    <div class="solution">
      <p><span>Paragraph 4</span></p>
      <p><span>Paragraph 5</span></p>
      <p><span>Paragraph 6</span></p>
    </div>