JSFiddle - React, Tailwind, and code Playground

HTML

<div class="field">
    <label>Number</label>
    <input type="number" min="2" max="10" step="2"/>
    <button type="button">Test</button>
</div>

<div class="field">
    <label>Email</label>
    <input type="email"/>
    <button type="button">Test</button>
</div>

<div class="field">
    <label>String</label>
    <input type="text" maxlength="5" required="required"/>
    <button type="button">Test</button>
</div>

<div class="field">
    <label>Regex</label>
    <input type="text" maxlength="5" required="required" pattern="^foo"/>
    <button type="button">Test</button>
</div>

CSS

span.success {
    color: green;
}
span.error {
    color: red;
}
label {
    display: inline-block;
    width: 6em;
}

JavaScript

$(function() {
    $('input').on('keyup', function(e) {
        if (e.keyCode == 13) {
            $(this).siblings('button').click();
        }
    });
    
    $('button').on('click', function() {
        var input = $(this).prev();
        var field = $(this).parent();
        field.find('span').remove();
        
        try {
            ValidationFactory.create(input)();
            field.append('<span class="success">Value is OK</span>');
        } catch (e) {
            field.append('<span class="error">' + e + '</span>');
        }
    });
});

var Validators = {
    String: function(value, opts) {
        var min = opts.min ? opts.min : 0;
        var max = opts.max ? opts.max : value.length;

        if (value.length < min) {
            throw 'Value too short';
        }

        if (value.length > max) {
            throw 'Value too long';
        }
    },
    Pattern: function(value, opts) {
        if (!opts.pattern) {
            throw 'Pattern not defined';
        }

        if (typeof opts.pattern == 'string') {
            opts.pattern = new RegExp(opts.pattern);
        }

        if (!opts.pattern.test(value)) {
            throw 'Invalid value';
        }
    },
    Number: function(value, opts) {
        var value = Number(value);

        if (Number.isNaN(value)) {
            throw 'Invalid value';
        }

        if (!Number.isNaN(opts.min) && value < opts.min) {
            throw 'Value too small';
        }

        if (!Number.isNaN(opts.max) && value > opts.max) {
            throw 'Value too large';
        }

        if (!Number.isNaN(opts.step) && value % opts.step) {
            throw 'Value must be multiple of ' + opts.step;
        }
    },
};

var Chain = function() {
    var validators = Array.prototype.slice.call(arguments);

    return function() {
        for (var i = 0; i < validators.length; i++) {
            validators[i]();
        }
    }
};

var ValidationFactory = {
    create: function(field) {
        var f...