Search code examples
htmlcsssvg

Rendered SVG size differs when using `<use>` vs `<img>`


I have two implementations of consuming the same SVG content: one through a <use> element and one through an <img>. Despite the same content, viewbox, and sizing on both, the <use> element comes up shorter -- whereas the <img> is 160x160, <use> is 150x122. For the life of me I cannot seem to find an answer as to why this is.

The problem I'm looking to solve is to merge 10 or so individual SVG files, consumed via an <img>, into a singular sprite sheet to avoid unnecessary network requests (they're always consumed together, making this a perfect use for sprites). However, the sizing doesn't match up when switching to the sprite sheet.

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <style>
            div {
                display: flex;
                flex-direction: column;
                max-width: 10rem;
            }
            img, svg {
                width: 100%;
            }
        </style>
    </head>
    <body>
        <div>
            <svg>
                <use href="./icon-sprite.svg#icon" />
            </svg>
            <img src="./icon.svg" />
        </div>
    </body>
</html>

icon-sprite.svg

<svg xmlns="http://www.w3.org/2000/svg">
    <symbol id="icon" viewBox="0 0 54 54">
        <path d="M52 5H2C.9 5 0 5.9 0 7v40c0 1.1.9 2 2 2h50c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zM1 7c0-.55.45-1 1-1h50c.55 0 1 .45 1 1v9H1zm52 40c0 .55-.45 1-1 1H2c-.55 0-1-.45-1-1V17h52z" fill="#37474f" />
        <path d="M6 8.5a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5zm0 4c-.83 0-1.5-.67-1.5-1.5S5.17 9.5 6 9.5s1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm6-4a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5zm0 4c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm6-4a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5zm0 4c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM48 10c0-.55-.45-1-1-1H35c-.55 0-1 .45-1 1v2c0 .55.45 1 1 1h12c.55 0 1-.45 1-1zm-1 2H35v-2h12zM29.8 29.58l3.2 2.28V44c0 1.65 1.35 3 3 3s3-1.35 3-3V31.86l3.2-2.28c.44-.32.8-1.03.8-1.58v-5c0-.55-.32-1.32-.7-1.7L39 18v6l-3 2-3-2v-6l-3.3 3.3c-.38.38-.7 1.15-.7 1.7v5c0 .55.36 1.26.8 1.58zM30 23c0-.28.2-.8.4-1l1.6-1.6v4.15l.45.3 3 2 .55.36.56-.37 3-2 .44-.3V20.2l1.6 1.7c.2.2.4.82.4 1.1v5c0 .23-.2.63-.4.77l-3.18 2.27-.42.3V44c0 1.1-.9 2-2 2s-2-.9-2-2V31.34l-.42-.3-3.18-2.27c-.2-.14-.4-.54-.4-.77z" fill="#37474f" />
        <path stroke-miterlimit="10" d="M36 44V33" fill="#b2ff59" stroke="#37474f" stroke-linecap="round" />
        <path d="M27 36h-4V19h-8v17h-4l8 11zm-12 1h1V20h6v17h3.04L19 45.3 12.96 37z" fill="#37474f" />
    </symbol>
</svg>

icon.svg

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54">
    <path d="M52 5H2C.9 5 0 5.9 0 7v40c0 1.1.9 2 2 2h50c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zM1 7c0-.55.45-1 1-1h50c.55 0 1 .45 1 1v9H1zm52 40c0 .55-.45 1-1 1H2c-.55 0-1-.45-1-1V17h52z" fill="#37474f" />
    <path d="M6 8.5a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5zm0 4c-.83 0-1.5-.67-1.5-1.5S5.17 9.5 6 9.5s1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm6-4a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5zm0 4c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm6-4a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5zm0 4c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM48 10c0-.55-.45-1-1-1H35c-.55 0-1 .45-1 1v2c0 .55.45 1 1 1h12c.55 0 1-.45 1-1zm-1 2H35v-2h12zM29.8 29.58l3.2 2.28V44c0 1.65 1.35 3 3 3s3-1.35 3-3V31.86l3.2-2.28c.44-.32.8-1.03.8-1.58v-5c0-.55-.32-1.32-.7-1.7L39 18v6l-3 2-3-2v-6l-3.3 3.3c-.38.38-.7 1.15-.7 1.7v5c0 .55.36 1.26.8 1.58zM30 23c0-.28.2-.8.4-1l1.6-1.6v4.15l.45.3 3 2 .55.36.56-.37 3-2 .44-.3V20.2l1.6 1.7c.2.2.4.82.4 1.1v5c0 .23-.2.63-.4.77l-3.18 2.27-.42.3V44c0 1.1-.9 2-2 2s-2-.9-2-2V31.34l-.42-.3-3.18-2.27c-.2-.14-.4-.54-.4-.77z" fill="#37474f" />
    <path stroke-miterlimit="10" d="M36 44V33" fill="#b2ff59" stroke="#37474f" stroke-linecap="round" />
    <path d="M27 36h-4V19h-8v17h-4l8 11zm-12 1h1V20h6v17h3.04L19 45.3 12.96 37z" fill="#37474f" />
</svg>

Solution

  • To build this into a snippet I had to use an inline SVG for the sprite, to avoid cross-domain security issues. But all I had to do to get the sprite the same size as the image was to add an aspect-ratio: 1/1.

    Note that neither your icon.svg nor your icon-sprite.svg have an intrinsic size, so if you do not specify a size, you are relying on the browser default behaviour which may vary from browser to browser. Your CSS specifies the width of the SVGs (100% of a flexbox which is 10em wide), but not the height. I believe the spec does say that a user agent should use a default size for an SVG if the size is not specified. It seems that, in my browser at least, the regular SVG defaults to an aspect-ratio of 1:1 when the height is not specified, whereas the sprite seems to default to zero height.

    Not sure why you were getting different results, but it could be browser-dependent. I am using Safari on a Mac. I tried in Chrome also and the sprite doesn’t need the aspect-ratio to be specified manually.

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="utf-8">
            <style>
                div {
                    display: flex;
                    flex-direction: column;
                    max-width: 10rem;
                    gap: 1em;
                }
                img, svg {
                    width: 100%;
                }
            </style>
        </head>
        <body>
            <svg style="display: none">
              <defs>
                <symbol id="icon" viewBox="0 0 54 54">
                  <path d="M52 5H2C.9 5 0 5.9 0 7v40c0 1.1.9 2 2 2h50c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zM1 7c0-.55.45-1 1-1h50c.55 0 1 .45 1 1v9H1zm52 40c0 .55-.45 1-1 1H2c-.55 0-1-.45-1-1V17h52z" fill="#37474f" />
                  <path d="M6 8.5a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5zm0 4c-.83 0-1.5-.67-1.5-1.5S5.17 9.5 6 9.5s1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm6-4a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5zm0 4c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm6-4a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5zm0 4c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM48 10c0-.55-.45-1-1-1H35c-.55 0-1 .45-1 1v2c0 .55.45 1 1 1h12c.55 0 1-.45 1-1zm-1 2H35v-2h12zM29.8 29.58l3.2 2.28V44c0 1.65 1.35 3 3 3s3-1.35 3-3V31.86l3.2-2.28c.44-.32.8-1.03.8-1.58v-5c0-.55-.32-1.32-.7-1.7L39 18v6l-3 2-3-2v-6l-3.3 3.3c-.38.38-.7 1.15-.7 1.7v5c0 .55.36 1.26.8 1.58zM30 23c0-.28.2-.8.4-1l1.6-1.6v4.15l.45.3 3 2 .55.36.56-.37 3-2 .44-.3V20.2l1.6 1.7c.2.2.4.82.4 1.1v5c0 .23-.2.63-.4.77l-3.18 2.27-.42.3V44c0 1.1-.9 2-2 2s-2-.9-2-2V31.34l-.42-.3-3.18-2.27c-.2-.14-.4-.54-.4-.77z" fill="#37474f" />
                  <path stroke-miterlimit="10" d="M36 44V33" fill="#b2ff59" stroke="#37474f" stroke-linecap="round" />
                  <path d="M27 36h-4V19h-8v17h-4l8 11zm-12 1h1V20h6v17h3.04L19 45.3 12.96 37z" fill="#37474f" />
                </symbol>
              </defs>
            </svg>
    
            <div>
                <svg style="aspect-ratio: 1/1; border: 2px solid lime;">
                    <use href="#icon" />
                </svg>
                <img style="border: 2px solid cyan;" src="https://donald.au/bugs/so-77582685/icon.svg" />
            </div>
        </body>
    </html>