JSFiddle - React, Tailwind, and code Playground

HTML

<rich-editor value="Added via HTML"></rich-editor>

CSS

rich-editor { width: 300px; height: 100px }

JavaScript

class RichEditor extends HTMLElement {
	constructor() {
  	super()
    
    let shadowRoot = this.attachShadow({mode: "open"})
        
    console.log("Shadow root", this.shadowRoot)
    
    let content = this.hasAttribute("value") ? this.getAttribute("value") : ""
    
	  shadowRoot.innerHTML = `
    	<style>
      	:host { 
        	border: 2px solid #2f4858; border-radius: 3px; 
          background-color: #f0fff0; 
          display: block; 
          padding: 3px; margin: 10px
        }
        .toolbar { height: 20px; border-bottom: 1px solid #2f4858 }
        .content { color: #33658a; padding-top: 3px }
      </style>
    	<div class="toolbar">
        <strong>B</strong>&nbsp;
        <em>I</em>&nbsp;
        <span style="text-decoration: underline">U</span>
      </div>
      <div class="content">${content}</div>
    `
  }
  
  static get observedAttributes() { 
  	return ["value"] 
  }
  
  attributeChangedCallback(attr, oldVal, newVal) {
  	console.log("In attributeChangedCallback", attr, newVal)
  	if (attr == "value") {
    	this.value = newVal
    }
  }
  
  get value() {
    return this.shadowRoot.querySelector(".content").innerHTML
  } 

  set value(val) {
  	this.shadowRoot.querySelector(".content").innerHTML = val
    this.dispatchEvent(new CustomEvent("change", {detail: this.innerHTML}))
  } 
}

window.customElements.define('rich-editor', RichEditor)

let editor = document.createElement('rich-editor')
document.body.appendChild(editor)
editor.value = "<strong>Added via JS</strong>"
editor.addEventListener("change", (event) => console.log("Caught change event", event.detail))
editor.value = "<strong>Changed via JS</strong>"

setTimeout(() => {
	e = document.querySelector("rich-editor")
  console.log("Calling setAttribute", e)
  e.setAttribute("value", "Changed value via attr")
  console.log("value property", e.value)
}, 1000)