Search code examples
csssvgshadow-dom

svg: styling a path within <use>


I'm trying to use CSS to style the fill of a specific <path> within a <symbol>. I've assigned fill:inherit to the path in the symbol <defs> and I can query the <use> instances of the symbol within the SVG DOM, but I can't seem to access that path within these instances. I looked into inherit for the CSS and currentColor for the fill of the <path> but no luck. Any help is appreciated.

<svg>
  <defs>
    <style>
      symbol#DateCard path.purple {
        fill:inherit;
      }
      use[*|href="#DateCard"] path.purple{
        fill:#ff0095;
        transition:fill .6s; 
      }
      use[*|href="#DateCard"] path.purple:hover{
        path:#ff0000;
      }
    </style>
    <symbol id="DateCard">
      <path class="purple" fill="currentColor" d="..."/>
    </symbol>
  </defs>
  <use xlink:href="#DateCard"/>
<svg>

I can query the path within symbol, but when I query the specific paths within a <use> instance, this returns an empty NodeList:

document.querySelectorAll('use[*|href="#DateCard"] path.purple')


Solution

  • Styling inside the shadow DOM needs to be done inside the shadow DOM.

    What I learned from Styling SVG Content with CSS | Codrops is that CSS variables are very useful in this case. So, here I created different ways of styling: using a style attribute and style element inside the symbol in combination with CSS variables from the "outside" of the symbol.

    .card1 {
      --path1-color: #0099CC;
      --path2-color: #FFDF34;
    }
    
    .card2 {
      --path1-color: #00008B;
      --path2-color: #FF8C00;
    }
    
    .card2:hover {
      --path1-color: #00BFFF;
    }
    <svg viewBox="0 0 200 100" width="400">
      <defs>
        <symbol id="DateCard">
          <style>
            .path2 {
              fill: var(--path2-color);
              transition: fill 1s;
            }
            .path2:hover {
              fill: #800000;
            }
          </style>
          <path class="path1" style="fill: var(--path1-color);transition: fill .6s;" d="M20 40 a 20 20 0 1 1 1 0"/>
          <path class="path2" d="M40 60 a 20 20 0 1 1 1 0"/>
        </symbol>
      </defs>
      <use href="#DateCard" class="card1"/>
      <use href="#DateCard" class="card2" transform="translate(80 0)"/>
    <svg>

    Update

    OP ask if the hover effect can be achieved using attributes in SVG. An alternative to the :hover pseudo class would be an animation started by the mouse entering and leaving the animated element. Unfortunately it is not as flexible as CSS -- it is difficult to style <animate>.

    Here is an example on animating the second <path> in the symbol:

    .card1 {
      --path1-color: #0099CC;
      --path2-color: #FFDF34;
    }
    
    .card2 {
      --path1-color: #00008B;
      --path2-color: #FF8C00;
    }
    
    .card2:hover {
      --path1-color: #00BFFF;
    }
    <svg viewBox="0 0 200 100" width="400">
      <defs>
        <symbol id="DateCard">
          <style>
            .path2 {
              fill: var(--path2-color);
            }
          </style>
          <path class="path1" style="fill: var(--path1-color);transition: fill .6s;" d="M20 40 a 20 20 0 1 1 1 0"/>
          <path class="path2" d="M40 60 a 20 20 0 1 1 1 0">
            <animate attributeName="fill" dur="1s" values="#FF8C00;#800000" begin="mouseenter" fill="freeze" />
            <animate attributeName="fill" dur="1s" from="#800000" to="#FF8C00" begin="mouseleave" fill="freeze" />
          </path>
        </symbol>
      </defs>
      <use href="#DateCard" class="card1"/>
      <use href="#DateCard" class="card2" transform="translate(80 0)"/>
    <svg>