javascriptcsscustom-elementcss-variables

How to use a CSS variable inside a custom HTML element?


I have a custom HTML element I made that creates a repeating background pattern which I animate in. I need the ability to dynamically set the color of this pattern with a CSS variable that lives in the parent/main document scope.

I know this is possible, but I think the complication is that I am setting the BG pattern as a CSS background image string.

customElements.define(
  'svg-background',
  class extends HTMLElement {
    connectedCallback() {
      setTimeout(() => {
        // wait till <template> is parsed as DOM
        let svg = this.querySelector('template')
          .innerHTML.replaceAll(/(\n\s*)|(\<!-- .*? --\>)/g, '') //remove linebreaks & indentations after linebreaks, and comments
          .replaceAll('"', "'") //replace double quotes
          .replaceAll('#', '%23'); //escape #
        Object.assign(this.style, {
          backgroundImage: `url("data:image/svg+xml;utf8,${svg}")`,
        });
      });
    }
  },
);

function toggleColor(){
  document.documentElement.classList.toggle('theme-blue')
}
:root{
  --hex-color: red;
}

:root.theme-blue{
  --hex-color: blue;
}

svg-background {
  position: fixed;
  bottom: 0;
  left:0;
  width: 100vw;
  height: 800px;
  transform: translateZ(0);
  pointer-events: none;
  z-index: -1;
}
<button type="button" onclick="toggleColor()">Toggle Color</button>

<svg-background>
  <template>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 800">
      <defs>
        <style>
          use {
            animation: 0.4s fade-in-hex;
            animation-fill-mode: both;
            fill: var(--app-theme-hex);
          }
          @keyframes fade-in-hex {
            from {opacity: 0;}
            to {opacity: 1;}
          }
          #hex-col use:nth-child(1) {animation-delay: 0.9s;}
          #hex-col use:nth-child(2) {animation-delay: 0.8s;}
          #hex-col use:nth-child(3) {animation-delay: 0.7s;}
          #hex-col use:nth-child(4) {animation-delay: 0.6s;}
          #hex-col use:nth-child(5) {animation-delay: 0.5s;}
          #hex-col use:nth-child(6) {animation-delay: 0.4s;}
          #hex-col use:nth-child(7) {animation-delay: 0.3s;}
          #hex-col use:nth-child(8) {animation-delay: 0.2s;}
          #hex-col use:nth-child(9) {animation-delay: 0.1s;}
        </style>
        <path id="hex" d="M0 25.980762113533157L15 0L45 0L60 25.980762113533157L45 51.96152422706631L15 51.96152422706631Z" />
      </defs>
      <!-- We repeat a pattern of 3 columns. One in the middle, and then two on either side that are cut in half so they line up when they meet -->
      <g id="hex-col">
        <use href="#hex" transform="translate(25 25) scale(.2)" />
        <use href="#hex" transform="translate(21 115) scale(.3)" />
        <use href="#hex" transform="translate(18 205) scale(.4)" />
        <use href="#hex" transform="translate(15 295) scale(.5)" />
        <use href="#hex" transform="translate(12 385) scale(.6)" />
        <use href="#hex" transform="translate(10 475) scale(.7)" />
        <use href="#hex" transform="translate(8 565) scale(.8)" />
        <use href="#hex" transform="translate(4 655) scale(.9)" />
        <use href="#hex" transform="translate(0 750)" />
      </g>
      <use href="#hex-col" transform="translate(45 -51)" />
      <use href="#hex-col" transform="translate(-45 -51)" />
    </svg>
  </template>
</svg-background>


Solution

  • I would use it as a mask instead and control the color using the background

    customElements.define(
      'svg-background',
      class extends HTMLElement {
        connectedCallback() {
          setTimeout(() => {
            // wait till <template> is parsed as DOM
            let svg = this.querySelector('template')
              .innerHTML.replaceAll(/(\n\s*)|(\<!-- .*? --\>)/g, '') //remove linebreaks & indentations after linebreaks, and comments
              .replaceAll('"', "'") //replace double quotes
              .replaceAll('#', '%23'); //escape #
            Object.assign(this.style, {
              mask: `url("data:image/svg+xml;utf8,${svg}")`,
            });
          });
        }
      },
    );
    
    function toggleColor(){
      document.documentElement.classList.toggle('theme-blue')
    }
    :root{
      --hex-color: red;
    }
    
    :root.theme-blue{
      --hex-color: blue;
    }
    
    svg-background {
      position: fixed;
      bottom: 0;
      left:0;
      width: 100vw;
      height: 800px;
      transform: translateZ(0);
      pointer-events: none;
      z-index: -1;
      background: var(--hex-color)
    }
    <button type="button" onclick="toggleColor()">Toggle Color</button>
    
    <svg-background>
      <template>
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 800">
          <defs>
            <style>
              use {
                animation: 0.4s fade-in-hex;
                animation-fill-mode: both;
                fill: #000;
              }
              @keyframes fade-in-hex {
                from {opacity: 0;}
                to {opacity: 1;}
              }
              #hex-col use:nth-child(1) {animation-delay: 0.9s;}
              #hex-col use:nth-child(2) {animation-delay: 0.8s;}
              #hex-col use:nth-child(3) {animation-delay: 0.7s;}
              #hex-col use:nth-child(4) {animation-delay: 0.6s;}
              #hex-col use:nth-child(5) {animation-delay: 0.5s;}
              #hex-col use:nth-child(6) {animation-delay: 0.4s;}
              #hex-col use:nth-child(7) {animation-delay: 0.3s;}
              #hex-col use:nth-child(8) {animation-delay: 0.2s;}
              #hex-col use:nth-child(9) {animation-delay: 0.1s;}
            </style>
            <path id="hex" d="M0 25.980762113533157L15 0L45 0L60 25.980762113533157L45 51.96152422706631L15 51.96152422706631Z" />
          </defs>
          <!-- We repeat a pattern of 3 columns. One in the middle, and then two on either side that are cut in half so they line up when they meet -->
          <g id="hex-col">
            <use href="#hex" transform="translate(25 25) scale(.2)" />
            <use href="#hex" transform="translate(21 115) scale(.3)" />
            <use href="#hex" transform="translate(18 205) scale(.4)" />
            <use href="#hex" transform="translate(15 295) scale(.5)" />
            <use href="#hex" transform="translate(12 385) scale(.6)" />
            <use href="#hex" transform="translate(10 475) scale(.7)" />
            <use href="#hex" transform="translate(8 565) scale(.8)" />
            <use href="#hex" transform="translate(4 655) scale(.9)" />
            <use href="#hex" transform="translate(0 750)" />
          </g>
          <use href="#hex-col" transform="translate(45 -51)" />
          <use href="#hex-col" transform="translate(-45 -51)" />
        </svg>
      </template>
    </svg-background>