JSFiddle - React, Tailwind, and code Playground

HTML

<div id="root"></div>
<script>
const vnode = create({
  tag: 'div',
  attrs: {class: "app"},
  children: [
    {tag: "h1", children: "Hi, %username%!"},
    {tag: "button", attrs: {class: "button"}, children: "Enter"},
  ],
}, document.getElementById('root'));
</script>

CSS

.app {
  text-align: center;
  marging: 30px;
}

.button {
  font-size: 30px;
}

JavaScript

//
// API
//
const TEXT_NODE = '#';
const R_IS_EVENT = /^on[A-Z]/;
const R_IS_TEXT = /string|number|boolean/;
const CLASS_ATTR = 'class';
const VALUE_ATTR = 'value';

function normalize(node) {
  if (node == null) {
    return {tag: TEXT_NODE, children: ''};
  } else if (node.tag == null) {
  	return {tag: TEXT_NODE, children: node};
  } else {
  	return node;
  }
}

function create(/* Object */node, /* HTMLElement */parent) {
  node = normalize(node); // "str" -> {tag: "#", children: "..."}
  let el;

  if (TEXT_NODE === node.tag) {
    el = document.createTextNode(node.children);
  } else {
    el = document.createElement(node.tag);
    updateAttrs(el, node.attrs); // добавляем аттрибуты
    createChildren(node, el); // создаеём детишек
  }

  node.el = el; // запоминаем DOM
  (parent !== null) && parent.appendChild(el);

  return node;
}

function updateAttrs(/* HTMLElement */el, attrs, oldAttrs) {
  let name;
  let value;
  for (name in attrs) {
    value = attrs[name];
    if (oldAttrs == null || oldAttrs[name] !== value) {
      if (CLASS_ATTR === name) { // className
        el.className = value;
      }
      else if (VALUE_ATTR === name) { // value
        (el[name] !== value)  && (el[name] = value);
      }
      else if (/^on[A-Z]/.test(name)) { // события
        el.addEventListener(name.substr(2), value, false);
      }
      else { // все остальные
        el.setAttribute(name, value);
      }
    }
  }
  
  if (oldAttrs != null) {
    for (name in oldAttrs) {
      value = oldAttrs[name];

      if (attrs[name] == null && attrs[name] !== value) {
        if (R_IS_EVENT.test(name)) {
          el.removeEventListener(name.substr(2), value, false);
        } else {
          el.removeAttribute(name);
        }
      }
    }
  }
}

function createChildren(/* Object */node, /* HTMLElement */parent) {
  const children = node.children;
  const length = children.length;
 
  if (length == null || R_IS_TEXT.test(typeof children)) {
    node.children =...