S R T B H P N
Tests.html

Test Hideable PhotoSizer Element - ver 1

Implements W3C/Google webcomponent

Works with FireFox, Chrome, Safari. Edge rendering has problems.
Figure 1. ThreadPool
This is how this page uses the PhotoSizer Element:
  <photosizer-block src="Pictures/ThreadPool.JPG" width="200" class="photoSizerBlock left">
    <span style="font-family:'Comic Sans MS';">Figure 1. ThreadPool</span>
  </photosizer-block>
And this is how this pages styles the PhotoSizer Element:
    #github photosizer-block {
      --border:1px solid black;
      --box-shadow: 3px 3px 5px #999;
      --wrapperPadding: 5px 10px 10px 10px;
      --margin: 20px;
      --captionPadding: 0px 0px 5px 0px;
    }
    /* uncomment .photoBlock to use this class */
    /*#github .photoSizerBlock {
      --background-color: #efe;
      --font-family:Comic Sans MS;
      --font-weight: Bold;
      --border: 3px solid darkgreen;
      --captionPadding: 0px 0px 5px 0px;
      --wrapperPadding: 5px;
      --margin: 30px;
    }*/
View page source for all the details.

Here's the component code:
<script>
  /* custom webcomponent - PhotoSizerBlock*/

  class PhotoSizerBlock extends HTMLElement {
    constructor(...args) {
      const self = super(...args);
      const shadowRoot = this.attachShadow({ mode: 'open' });
      shadowRoot.innerHTML =
        `
          <style>
            #cont {
              display:flex;
              position:relative;
              padding: var(--padding);
            }
            #wrapper {
              width:min-content;
              padding: var(--wrapperPadding); //10px;
              margin: var(--margin);  //20px;
              background-color: var(--background-color);
              color: var(--color);
              box-shadow: var(--box-shadow);
              font-family: var(--font-family);
              font-size: var(--font-size);
              font-weight: var(--font-weight);
              border: var(--border);
              cursor: pointer;
              user-select:none;
              -webkit-user-select:none;
              -moz-user-select:none;
              -ms-user-select:none;
            }
            #closer {
              position:absolute;
              top:20px; right:8px;
              border:2px solid red;
              background-color:black;
              color:white;
              cursor:pointer;
              width:0.5em; height:1em;
            }
            #caption {
              text-align:center;
              padding-bottom:10px;
              background-color:white;
              padding: var(--captionPadding); //0px 0px 10px 0px;
            }
            img {
              user-select:none;
              -webkit-user-select:none;
              -moz-user-select:none;
              -ms-user-select:none;
            }
          </style>
          <div id="cont">
            <slot></slot>
            <div id="closer"></div>
            <div id="wrapper">
              <div id="caption"></div>
              <img />
            </div>
          </div>
        `;
      this.bigger = this.bigger.bind(this);
      this.smaller = this.smaller.bind(this);
      this.toggle = this.toggle.bind(this);
      return self;
    }
    connectedCallback() {
      this.url = this.getAttribute('src');
      this.imgElem = this.shadowRoot.querySelector('img');
      this.imgElem.setAttribute('src', this.url);
      this.width = this.getAttribute('width');
      if (isDefined(this.width))
        this.imgElem.setAttribute('width', this.width);
      this.height = this.getAttribute('height');
      if (isDefined(this.height))
        this.imgElem.setAttribute(this.height);
      this.caption = this.shadowRoot.querySelector('#caption');
      if (isDefined(this.caption)) {
        this.captionText = this.innerHTML;
        this.innerHTML = "";
        this.caption.innerHTML = this.captionText;
      }
      this.imgElem.addEventListener('click', this.bigger);
      this.caption.addEventListener('click', this.smaller);
      this.closer = this.shadowRoot.querySelector('#closer');
      this.closer.addEventListener('click', this.toggle);
    }
    bigger() {
      this.imgElem.width = Number(this.imgElem.width) * 1.25;
    }
    smaller() {
      this.imgElem.width = Number(this.imgElem.width) / 1.25;
    }
    toggle() {
      let wrpr = this.shadowRoot.getElementById("wrapper");
      if (isDefined(wrpr)) {
        if (wrpr.style.display === "none") {
          wrpr.style.display = "block";
          this.closer.style.right = "8px";
        }
        else {
          wrpr.style.display = "none";
          this.closer.style.right = "7px";
        }
      }
    }
  }
  /* register with DOM - don't need to use script block to execute */

  /*---------------------------------------------------------------------------
    * This listener is added because of a component lifecycle issue with chrome
    * See first reference, below.
    */
  document.addEventListener('DOMContentLoaded', function () {
    window.customElements.define('photosizer-block', PhotoSizerBlock);
  });
  /*
  * The listener, above, will be removed and replaced with this when chrome
  * component lifecycle is fixed.
  *
  * window.customElements.define('photosizer-block', PhotoSizerBlock);
  */
 </script>

References:

  1. Issue with Chrome loading innerHTML
  2. polyfills CDN
  3. customelements - google.com
  4. shadowdom - google.com
  5. webcomponents - htm5rocks.com
  6. attributes & properties - alligator.io
  7. styling webcomponent - css-tricks.com
  8. encapsulating style and structure - css-tricks.com
  9. creating custom component from scratch - css-tricks.com
  10. Using custom elements - mozilla.org
  11. introduction to webcomponents - webcomponents.org
  12. build web components - dev.to
  13. tutorial - robinwieruch.de
  14. introduction - grapecity.com