2

I'm including an svg element in a h1 like this:

<h1>
    64px ABC
    <span class="icon">
        <svg width="64" height="64" viewBox="0 0 64 64">
            <rect id="block" x="4" y="4" width="56" height="56" rx="16" ry="16" stroke-width="1" />
        </svg> 
    </span>
    xyz
</h1>

I then position the icon a little lower to align nicely with the text using relative positioning.

h1 {
    font-size: 64px;
}

.icon {
  position: relative;
  top: 0.14453125em; /* Calculated using https://seek-oss.github.io/capsize/ */
}

svg {
  height: 1em;
  fill: currentColor;
}

This all works, but at a height of 1em, the svg increases the height of it's parent element.

This is the h1 without the svg:

And this is with the svg included:

There's a difference of 5px.

When I change the height to e.g. .9em, this doesn't happen.

I've tried many different options, without the desired result - being to keep the height of the parent element unchanged.

Why does this happen and how can we make sure it doesn't?

CodePen: https://codepen.io/denobelsoftware/pen/MWjKKXO

let iconsHidden = false;
const elements = document.querySelectorAll(".icon");

document.querySelector("#hide-icons").addEventListener('click', function(event) {
  for (var i = 0; i < elements.length; i++) {
    if(iconsHidden){
      elements[i].classList.add('hidden');
    } else elements[i].classList.remove('hidden');
}
  iconsHidden = !iconsHidden;
});
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700&display=swap');

h1 {
    font-size: 64px;
    font-family: 'Roboto', sans-serif;
    font-weight: normal;
    position: relative; /* for the rulers */
}

.icon {
  position: relative;
  top: 0.14453125em;
}

svg {
  height: 1em;
  fill: currentColor;
}

h1:after{
    width: 100%;
    height: 1px;
    content: "";
    position: absolute;
    bottom: 0.244140625em;
    left: 0;
    background-color: rgba(255, 0, 0, 0.23);
    z-index: -100;
}

h1:before{
    width: 100%;
    height: 1px;
    content: "";
    position: absolute;
    bottom: 0.955078125em;
    left: 0;
    background-color: rgba(255, 0, 0, 0.23);
    z-index: -100;
}

.hidden {
  display: none;
}
<body>
    <h1>
        64px ABC
        <span class="icon">
            <svg width="64" height="64" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg">
                <rect id="block" x="4" y="4" width="56" height="56" rx="16" ry="16" stroke-width="1" />
            </svg> 
        </span>
        xyz
    </h1>

    <h1>
        64px ABC
        <span class="icon">
            <svg width="64" height="64" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg">
                <rect id="block" x="4" y="4" width="56" height="56" rx="16" ry="16" stroke-width="1" />
            </svg>
        </span>
        xyz
    </h1>

    <input type="checkbox" id="hide-icons">
    <label for="hide-icons"> Hide icons</label>
</body>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
Jay Regal
  • 3,243
  • 1
  • 16
  • 18

1 Answers1

5

Make the span display:inline-block and add vertical-align:top to avoid the default baseline alignment that will make the linebox bigger then also use line-height:0 to make sure the svg inside will not affect its size. Then you may need to readjust the top value:

@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700&display=swap');

h1 {
    font-size: 64px;
    font-family: 'Roboto', sans-serif;
    font-weight: normal;
    position: relative; /* for the rulers */
}

.icon {
  position: relative;
  top: 0.14453125em;
  display:inline-block;
  vertical-align:top;
  line-height:0;
}

svg {
  height: 1em;
  fill: currentColor;
}

h1:after{
    width: 100%;
    height: 1px;
    content: "";
    position: absolute;
    bottom: 0.244140625em;
    left: 0;
    background-color: rgba(255, 0, 0, 0.23);
    z-index: -100;
}

h1:before{
    width: 100%;
    height: 1px;
    content: "";
    position: absolute;
    bottom: 0.955078125em;
    left: 0;
    background-color: rgba(255, 0, 0, 0.23);
    z-index: -100;
}

.hidden {
  display: none;
}
<body>
    <h1>
        64px ABC
        <span class="icon">
            <svg width="64" height="64" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg">
                <rect id="block" x="4" y="4" width="56" height="56" rx="16" ry="16" stroke-width="1" />
            </svg> 
        </span>
        xyz
    </h1>

    <h1>
        64px ABC

        xyz
    </h1>

    <input type="checkbox" id="hide-icons">
    <label for="hide-icons"> Hide icons</label>
</body>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Thanks, this avoids the bigger linebox. It also makes the text in the h1 vertical-align to the top, which should stay baseline. Do you see a way to avoid this? – Jay Regal Dec 04 '20 at 08:57
  • @RNobel how it's making the h1 aligned to top? as you can see both examples are exactly the same (with and without the icon) – Temani Afif Dec 04 '20 at 09:34
  • When you open dev tools and disable ```vertical-align: top;``` you'll see that the text moves down – Jay Regal Dec 04 '20 at 11:32
  • @RNobel and this is exactly your issue. The text moves down because the span is creating more space on the bottom thus it needs more space on the top to be placed inside the line. That space is the culprit making your linebox bigger. The text is not moving and the baseline is not changing, the span is making the difference – Temani Afif Dec 04 '20 at 11:34
  • @RNobel notice how your pseudo element aren't moving but since the line is bigger then logically it will need more vertical space and you have the illusion of the text being pushed down but it's not. It's the linebox that was bigger like you noticed and you wanted to fix – Temani Afif Dec 04 '20 at 11:40
  • Ah, now I get it! :) So if I measure the distance between the baseline and top it's 59px. The span is 64px high, so there's 5px short. This causes the box to grow with 5px and the text looks like it's pushed down 5px because of that. Setting ```vertical-align: top;``` changes this because between top and bottom, there's more than enough space to fit the 64px. Is that correct? – Jay Regal Dec 04 '20 at 16:56
  • @RNobel yes, the span was initially aligned to the baseline so as you said it start from there and grow to the top with its height. I changed this so it start from the top to the bottom instead and only its 64px will be considered which is OK since the linebox height is already bigger than that so it fits inside (the line-height:0 wll also ensure we don't have more than 64px inside the span too) – Temani Afif Dec 04 '20 at 18:34
  • Awesome, thanks. Are there any resources or courses that cover details like this? – Jay Regal Dec 04 '20 at 19:28
  • 1
    @RNobel I wrote some answers around that topic if you want: https://stackoverflow.com/a/55978512/8620333 / https://stackoverflow.com/a/64959101/8620333 / https://stackoverflow.com/a/63319772/8620333 – Temani Afif Dec 04 '20 at 19:35
  • Thank you so much, you saved me hours of trials, after having already spent hours of trials ... – Cesare Polonara Apr 22 '22 at 00:02