JSFiddle - React, Tailwind, and code Playground

by graphettion

HTML

<button-wc variant="contained">Click Me</button-wc>
  <text-field-wc label="Name"></text-field-wc>
  <checkbox-wc label="Agree"></checkbox-wc>

  <button onclick="toggleTheme()">Toggle Theme</button>
  <script>
    function toggleTheme() {
      document.body.dataset.theme = document.body.dataset.theme === 'dark' ? 'light' : 'dark';
    }
  </script>

JavaScript

// Button Component
class ButtonWC extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.render();
  }

  static get observedAttributes() {
    return ['variant'];
  }

  attributeChangedCallback() {
    this.render();
  }

  render() {
    const variant = this.getAttribute('variant') || 'text';
    this.shadowRoot.innerHTML = `
      <style>
        button {
          padding: 6px 16px;
          border-radius: 4px;
          font-size: 14px;
          text-transform: uppercase;
          cursor: pointer;
          transition: background-color 0.2s, box-shadow 0.2s;
        }
        button[variant="contained"] {
          background-color: var(--primary);
          color: #fff;
          border: none;
        }
        button[variant="outlined"] {
          background: transparent;
          border: 1px solid var(--primary);
          color: var(--primary);
        }
        button[variant="text"] {
          background: transparent;
          border: none;
          color: var(--primary);
        }
        button:hover {
          filter: brightness(90%);
        }
        button:focus {
          outline: none;
          box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.5);
        }
      </style>
      <button variant="${variant}">
        <slot></slot>
      </button>
    `;
  }
}
customElements.define('button-wc', ButtonWC);

// TextField Component
class TextFieldWC extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.render();
  }

  static get observedAttributes() {
    return ['label'];
  }

  attributeChangedCallback() {
    this.render();
  }

  render() {
    const label = this.getAttribute('label') || '';
    this.shadowRoot.innerHTML = `
      <style>
        :host {
          display: block;
          position: relative;
          margin: 16px 0;
        }
        label {
          position: absolute;
          top: 8px;
          left: 12px;
         ...