JSFiddle - React, Tailwind, and code Playground

HTML

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>

    <div>
        <span data-bind="counter"></span>
        <button onclick="increment()">Increment</button>
        <hr/>

        <input data-model="name" />
        <input data-model="lastname" />
        <button onclick="add()">Add</button><br/>
        You are about to add: <span data-bind="name"></span> <span data-bind="lastname"></span>

        <table>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Lastname</th>
                </tr>
            </thead>
            <tbody>
                <template data-for="list">
                    <tr>
                        <td data-each-bind="$.name"></td>
                        <td data-each-bind="$.lastname"></td>
                    </tr>
                </template>
            </tbody>
        </table>
    </div>

    <script>

        /**
         * Define the initial state:
         * Add `data-bind="counter"` to an html element, it will reload every time the counter is updated 
         * Add `data-model="name"` to an html input, the state will update every time the input changes
         * Add `data-for="list"` to an html template, a list will be created cloning the html first child
         **/
        let state = new Bynd({
            counter: 0,
            name: '',
            lastname: '',
            list: [
                { name: 'ciao', lastname: 'ciaone' },
                { name: 'prova', lastname: 'provetta' },
                { name: '2', lastname: '4' },
            ]
        })

        function increment() {
            // Update the variable to reload the html elements linked to this variable
            state.counter++
        }
        function add() {
            // Push a new element to...

JavaScript

function setNested(obj, path, value) {
    var schema = obj;
    var pList = path.split('.');
    var len = pList.length;
    for (var i = 0; i < len - 1; i++) {
        var elem = pList[i];
        if (!schema[elem]) schema[elem] = {}
        schema = schema[elem];
    }

    schema[pList[len - 1]] = value;
}
function eventifyPush(arr, callback) {
    arr.push = function (e) {
        Array.prototype.push.call(arr, e);
        callback(arr);
    };
};

class Bynd {
    constructor(variables) {
        this.$variables = {}
        this.defineProperties(variables)
        this.bindModels()
    }
    defineProperties(variables) {
        // Dynamically create getters/setters starting for each variable passed in the costructor
        for (let variable in variables) {
            let init = variables[variable]
            Object.defineProperty(this, variable, {
                get: () => this.$variables[variable],
                set: v => {
                    this.$variables[variable] = v;
                    // Every time the setter is called, reload the html elements that are binded to the variable
                    this.bind(variable)
                    this.bindFor(variable)
                },
            })
            this[variable] = init
            if (init instanceof Array) {
                // Every time the push method (for arrays) is called, reload the html elements that are binded to the variable
                eventifyPush(this[variable], (e) => {
                    this.bind(variable)
                    this.bindFor(variable)
                })
            }
        }
    }
    bindModels() {
        [...document.querySelectorAll("[data-model]")].forEach(d => {
            const queryVariable = d.getAttribute('data-model')
            d.oninput = () => {
                setNested(this, queryVariable, d.value)
            }
        })
    }
    bind(variable) {
        /**
         * [data-bind^= it means starting with. This is used to search...