skip to Main Content

I am trying to add a custom scrollbar to a web component.

I am not very familiar with web components but i understand that it is difficult to apply pseudo-elements to parts. From my reading, one way to apply styling in the shadow dom that might work is to include the style inline in the javascript of the component? However my js skills aren’t good enough to know where to place it in the js! Can anyone advise, where should the style go in the below to apply styles to modal class within the x-modal component?

Thanks in advance for any help with this.

// js/common/overlay/modal.js
import { animate as animate5, timeline as timeline2 } from "vendor";
var Modal = class extends DialogElement {
  connectedCallback() {
    super.connectedCallback();
    this.setAttribute("aria-modal", "true");
  }
  get shadowDomTemplate() {
    return this.getAttribute("template") || "modal-default-template";    
  }
  get shouldLock() {
    return true;
  }
  get shouldAppendToBody() {
    return true;
  }
  createEnterAnimationControls() {
    if (matchesMediaQuery("sm")) {
      return animate5(this, { opacity: [0, 1] }, { duration: 0.2 });
    } else {
      return timeline2([
        [this.getShadowPartByName("overlay"), { opacity: [0, 1] }, { duration: 0.3, easing: [0.645, 0.045, 0.355, 1] }],
        [this.getShadowPartByName("content"), { transform: ["translateY(100%)", "translateY(0)"] }, { duration: 0.3, easing: [0.645, 0.045, 0.355, 1], at: "<" }]
      ]);
    }
  }
  createLeaveAnimationControls() {
    if (matchesMediaQuery("sm")) {
      return animate5(this, { opacity: [1, 0] }, { duration: 0.2 });
    } else {
      return timeline2([
        [this.getShadowPartByName("overlay"), { opacity: [1, 0] }, { duration: 0.3, easing: [0.645, 0.045, 0.355, 1] }],
        [this.getShadowPartByName("content"), { transform: ["translateY(0)", "translateY(100%)"] }, { duration: 0.3, easing: [0.645, 0.045, 0.355, 1], at: "<" }]
      ]);
    }
  }
};

if (!window.customElements.get("x-modal")) {
  window.customElements.define("x-modal", Modal);
}
.modal::-webkit-scrollbar {
  background-color: #fff;
  width: 16px;
  overflow: auto;
} /* SUB background of the scrollbar except button or resizer */
.modal::-webkit-scrollbar-track {
  background-color: #fff;
} /* SUB scrollbar itself */
.modal::-webkit-scrollbar-thumb {
  background-color: #babac0;
  border-radius: 16px;
  border: 4px solid #fff;
} /* SUB set button(top and bottom of the scrollbar) */
.modal::-webkit-scrollbar-button {
  display: none;
}

2

Answers


  1. I don’t see the constructor in the modal class in your code.

    Please modify your code.

    var Modal = class extends DialogElement {
        constructor() {
            super()
            // todo: here add shadow dom
        }
        ...
    

    And then you need to use Shadow DOM.

    It hides the markup structure, styles, and behavior from the rest of the code on the page.

    A reference code.

    <style>
      .content {
        background: #fff !important;
      }
    </style>
    <body>
    <x-modal/>
    </body>
    <script>
      class Modal extends HTMLElement {
        constructor() {
          super()
    
          let style = document.createElement("style");
          style.textContent = `
              .modal::-webkit-scrollbar {
                background-color: #fff;
                width: 16px;
                overflow: auto;
              }
    
              /* SUB background of the scrollbar except button or resizer */
              .modal::-webkit-scrollbar-track {
                background-color: #fff;
              }
    
              /* SUB scrollbar itself */
              .modal::-webkit-scrollbar-thumb {
                background-color: #babac0;
                border-radius: 16px;
                border: 4px solid #fff;
              }
    
              /* SUB set button(top and bottom of the scrollbar) */
              .modal::-webkit-scrollbar-button {
                display: none;
              }
    
              .modal {
                overflow: scroll;
                width: 400px;
                height: 400px;
                border: 1px solid;
              }
    
              .content {
                height: 700px;
                background: red;
              }`
    
          const div = document.createElement('div');
          const content = document.createElement('div');
          div.classList.add("modal")
          content.classList.add("content")
          content.textContent = "this is a modal"
          div.appendChild(content)
          this.attachShadow({mode: "open"});
          this.shadowRoot.appendChild(style);
          this.shadowRoot.appendChild(div);
        }
      }
    
      if (!window.customElements.get("x-modal")) {
        window.customElements.define("x-modal", Modal);
      }
    
    </script>
    
    Login or Signup to reply.
  2. We got here because Chromium doesn’t support ::part(name)::webkit-scrollbar yet.

    So you want to inject that webkit stuff straight into a shadowDOM

    Changing a mode:"open" shadowDOM is doable after it was created in the DOM, but a PITA

    Easier is to stick to the OOP stuff and extend your own my-x-modal from the original x-modal
    Then append an extra <style> tag to the shadowDOM

    <my-x-modal color="green">FOO</my-x-modal>
    <my-x-modal color="blue">FOO</my-x-modal>
    <script>
      const element = (tag, props = {}) => Object.assign(document.createElement(tag), props);
      customElements.define("x-modal", class extends HTMLElement { // ORIGINAL <X-MODAL>
        constructor() {
          super()
            .attachShadow({mode:"open"})
            .append(
              element("STYLE",{
               innerHTML : 
                `:host{display:inline-block;overflow:scroll}` +
                `:host::-webkit-scrollbar { background: red }` // ORIGINAL COLOR
              }),
              element("SLOT")
            );
        }
        connectedCallback() {
            this.innerHTML += "BAR";
        }
      });
      customElements.whenDefined("x-modal").then(() => {
        // CREATE YOUR OWN <MY-X-MODAL>
        customElements.define("my-x-modal", class extends customElements.get("x-modal") {
          connectedCallback() {
            super.connectedCallback && super.connectedCallback();
            this.shadowRoot.append(
              element("STYLE", {
                // OVERLOAD COLOR
                innerHTML: `:host::-webkit-scrollbar { background: ${this.getAttribute("color")} }`
              }),
              element("SPAN", {
                innerHTML: `BAZ`
              })
            );
          }
        })
      })
    </script>

    FYI The original Wordle was a Web Component. With the above method it was "enhanced" to do:
    https://mordle.github.io/?sentence=danny,hacks,super,wordle,wordle,lingo,words

    Long blog post:
    https://dev.to/dannyengelman/more-wordle-mordle-extending-josh-his-web-components-4li

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search