<cool-counter> with Signals

by WebComponents

HTML

<script>
  // tiny Signals: swap for @preact/signals-core or the TC39 signal-polyfill without changing the component
  let current = null; // the effect that is running now
  const signal = (value) => {
    const subs = new Set();
    return {
      get value() { current && subs.add(current); return value },
      set value(v) { if (v !== value) { value = v; [...subs].forEach(fn => fn()) } }
    };
  };
  const effect = (fn) => { const run = () => { current = run; fn(); current = null }; run(); return run };
  const computed = (fn) => { const s = signal(); effect(() => s.value = fn()); return { get value() { return s.value } } };

  customElements.define('cool-counter', class extends HTMLElement {
    #count = signal(666);
    #bg = computed(() => this.#count.value % 2 === 0 ? 'lightblue' : 'lightgreen');
    get value() { return this.#count.value }
    set value(v) { this.#count.value = v }
    connectedCallback() {
      const createElement = (tag, props = {}) => Object.assign(document.createElement(tag), props);
      this.append(
        createElement("style", {
          textContent: "button{border-radius:4px;cursor:pointer;padding:0.5em 1em}"
        }),
        this.button = createElement("button", {
          onclick: (evt) => this.value++
        })
      ); // append
      effect(() => {
        this.button.textContent = `Increment! (count: ${this.value})`;
        this.button.style.background = this.#bg.value;
      });
    } // connectedCallback
  }) // define <cool-counter>
</script>

<cool-counter></cool-counter>