JSFiddle - React, Tailwind, and code Playground

HTML

<body>Dots: <stack-show></stack-show> </body>

CSS

button { margin: .5rem; }
input { display: inline-block; margin: 0 .5rem; }
stack-btns+span {display: block}

JavaScript

const define = (name, ctx, events, opts = {}) => {
  const l = { handleEvent: e => {
  	ctx.e = e; const act = events[e.type]; if (!act) return
		typeof act == "function" ? act(ctx) : act.find(c => e.target.matches(c[0]))?.[1](ctx)
  } }
  const off = self => self.off.forEach(d => d())
  const on = (self, events) => {
    self.off = Object.entries(events).map(([k, _]) => {
      self.addEventListener(k, l)
      return () => self.removeEventListener(k, l)
    })}
  if (customElements.get(name))
    for (let el of document.querySelectorAll(name)) { off(el); on(el, events) }
  else
    customElements.define(name, class extends HTMLElement {
      connectedCallback() { on(this, events) }
      disconnectedCallback() { off(this) }
    })
}
const mount = (s,html) => document.querySelector(s).innerHTML = html
const show = (stack,ch) => mount("stack-show", `<stack-btns></stack-btns><span>${stack.map(a => `<span>${ch || a.text}</span>`).join("")}</span>`)
const btns = (stack,ch) => mount("stack-btns", `<button data-cmd="add">+</button>${stack.length}<button data-cmd="rm">-</button>*<input value="${ch}">`)
define("stack-show", {ch: ".", stack: [], views: {show, btns}}, {"click": [
	["[data-cmd=add]", ctx => { ctx.stack.push({text: "."}); ctx.render(ctx.stack, ctx.ch) }],
  ["[data-cmd=rm]", ctx => { ctx.stack.pop(); ctx.render(ctx.stack, ctx.ch) }]
 ],
 "init": ctx => { let {stack, views} = ctx;
 	ctx.render = (stack,ch=".") => {views.show(stack,ch); views.btns(stack, ch)}
 	define("stack-btns", ctx, {"input": ({stack,e}) => Object.assign(ctx, {ch:e.target.value}) && ctx.render(stack, ctx.ch)})
 	ctx.render(stack)
 }})
document.querySelector("stack-show").dispatchEvent(new CustomEvent("init"))