0

I have a hidden SVG in my HTML that contains all of the site's icons. It looks something like this (I'm including only the "half gold star" icon for the purposes of this question):

<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
    <symbol width="19.783" height="20" viewBox="0 0 19.783 20" id="icon-half-star" role="img" aria-labelledby="icon-half-star-title">
        <title id="icon-half-star-title">Half a star</title>
        <defs>
            <clipPath id="icon-half-star-clip-a">
                <rect width="18.783" height="18" fill="#f0f0f0"/>
            </clipPath>
            <clipPath id="icon-half-star-clip-b">
                <rect width="10" height="20" transform="translate(137 2256)" fill="#fff"/>
            </clipPath>
            <clipPath id="icon-half-star-clip-c">
                <rect width="18.783" height="18" fill="#f4ba1c"/>
            </clipPath>
        </defs>
        <g transform="translate(1 1.001)">
            <g clip-path="url(#icon-half-star-clip-a)">
                <path d="M18.734,6.8a1,1,0,0,0-.859-.686l-5.422-.492L10.311.605a1,1,0,0,0-1.837,0L6.331,5.622.908,6.115A1,1,0,0,0,.341,7.861l4.1,3.594L3.231,16.778a1,1,0,0,0,1.485,1.079l4.676-2.8,4.675,2.8a1,1,0,0,0,1.486-1.079l-1.208-5.323,4.1-3.594A1,1,0,0,0,18.734,6.8" transform="translate(0 0.001)" fill="#f0f0f0"/>
            </g>
        </g>
        <g transform="translate(-137 -2256)" clip-path="url(#icon-half-star-clip-b)">
            <g transform="translate(137.783 2257.001)">
                <g clip-path="url(#icon-half-star-clip-c)">
                    <path d="M18.734,6.8a1,1,0,0,0-.859-.686l-5.422-.492L10.311.605a1,1,0,0,0-1.837,0L6.331,5.622.908,6.115A1,1,0,0,0,.341,7.861l4.1,3.594L3.231,16.778a1,1,0,0,0,1.485,1.079l4.676-2.8,4.675,2.8a1,1,0,0,0,1.486-1.079l-1.208-5.323,4.1-3.594A1,1,0,0,0,18.734,6.8" transform="translate(0 0.001)" fill="#f4ba1c"/>
                </g>
            </g>
        </g>
    </symbol>
</svg>

When I reference this symbol later in the document using <svg width="19.783" height="20" viewBox="0 0 19.783 20"><use href="#icon-half-star" /></svg>, it displays as a full gold star. In other words, the <clipPath> elements are being ignored. What should I be doing differently?

I put the <clipPath> elements inside the SVG that has a <use> element and that fixed the problem, but that's not an ideal solution. I don't want to have to type the <clipPath>s every time I reference the icon.

2
  • 1
    Use position:absolute instead of display:none. Also I would like to comment that your svg is full of transform="translate(... going to and fro. You can simplify the code and eliminate all the transforms. Also consider using a <defs> star with <use> for the grey and the orange stars (since the d attribute is the same). Additionally you may want to clip only the orange star.
    – enxaneta
    Commented Jul 4 at 20:14
  • Browsers only support graphics elements with <use> Commented Jul 4 at 20:53

2 Answers 2

2

As commented by enxaneta:

  • prefer to hide SVG inline assets via absolute positioning like so:
    style="position:absolute;height:0;width:0;" you may also add a ARIA attribute like aria-hidden="true"
  • avoid unnecessary transformations: they make your code rather hard to maintain

Besides, you may not need SVG <clipPath> definitions for simple rectangular clipping – CSS clip path properties like clip-path:inset() can also do it.

svg{
  width:50vmin;
  border: 1px solid #ccc;
}
<svg viewBox="0 0 19.783 20">
  <use href="#icon-half-star" />
</svg>

<svg style="position:absolute;height:0;width:0;" aria-hidden="true">
    <symbol width="19.783" height="20" viewBox="0 0 19.783 20" id="icon-half-star" role="img" aria-labelledby="icon-half-star-title">
        <title id="icon-half-star-title">Half a star</title>
        <defs>
           <path id="star_shape" d="M19.73 7.8a1 1 0 00-.85-.69l-5.43-.49-2.14-5.02a1 1 0 00-1.84 0l-2.14 5.02-5.42 .5a1 1 0 00-.57 1.74l4.1 3.6-1.21 5.32a1 1 0 001.49 1.08l4.67-2.8 4.68 2.8a1 1 0 001.48-1.08l-1.2-5.33 4.1-3.59a1 1 0 00.28-1.06"/>
        </defs>
      <!-- background star -->
      <use href="#star_shape" fill="#f0f0f0"/>
      <!-- star half-filled -->
      <use href="#star_shape" fill="#f4ba1c" style="clip-path:inset(0 50% 0% 0)"/>
    </symbol>
</svg>

By converting your path data to relative commands you can easily re-position your path by adjusting the first M command coordinates. This way we can get rid of unnecessary x/y-offsets via transforms.

0

A modern browser Web Component solution, avoiding <use> and (potential) duplicate ID issues.

<svg-star></svg-star>
<svg-star clip=25 ></svg-star>
<svg-star clip=50 color=red ></svg-star>
<svg-star clip=75 bgcolor=grey ></svg-star>
<svg-star clip=100></svg-star>

5 stars

svg{
  width:110px;
  border: 1px solid #ccc;
}
<script>
  customElements.define("svg-star", class extends HTMLElement {
    connectedCallback(){
      let d = `M19.73 7.8a1 1 0 00-.85-.69l-5.43-.49-2.14-5.02a1 1 0 00-1.84 0l-2.14 5.02-5.42 .5a1 1 0 00-.57 1.74l4.1 3.6-1.21 5.32a1 1 0 001.49 1.08l4.67-2.8 4.68 2.8a1 1 0 001.48-1.08l-1.2-5.33 4.1-3.59a1 1 0 00.28-1.06`;
      let bgcolor = this.getAttribute("bgcolor") || "#f0f0f0";
      let color   = this.getAttribute("color") || "gold";
      let clip    = this.getAttribute("clip") || 0;
      this.innerHTML = `<svg viewBox="0 0 20 20">` +
      `<path fill="${bgcolor}" d="${d}"/>` +
      `<path fill="${color}" style="clip-path:inset(0 ${clip}% 0% 0)" d="${d}"/>` +
      `</svg>`
    }
  });
</script>

<svg-star></svg-star>
<svg-star clip=25 ></svg-star>
<svg-star clip=50 color=red ></svg-star>
<svg-star clip=75 bgcolor=grey ></svg-star>
<svg-star clip=100></svg-star>

Not the answer you're looking for? Browse other questions tagged or ask your own question.