JSFiddle - React, Tailwind, and code Playground

by LyndseyB

HTML

<div class='o-wrapper'>
  <form method="post" class='c-signup-form' data-js='formValidate'>
    <label class='c-signup-form__label'><span>First Name </span><input type="text" name="firstName" data-required></label>
    <label class='c-signup-form__label'><span>Last Name </span><input type="text" name="lastName" data-required></label>
    <label class='c-signup-form__label'><span>Email Address </span><input type="text" name="emailAddress" data-required></label>
    <label><button type="submit">Submit Form</button></label>
  </form>
</div>

SCSS

* {
  box-sizing: border-box;
}

.o-wrapper {
  margin-left: auto;
  margin-right: auto;
  max-width: 1000px;
}

.c-signup-form {
  border: 1px solid #e3e2de;
  border-radius: 5px;
  padding: 1rem;
  background-color: #f7f7f7;
}

.c-signup-form__label {
  display: block;  
  padding-bottom: 1rem;
}

.c-signup-form__label > span {
  display: inline-block;
  width: 25%;
}

 .c-signup-form__label > input {
    padding-top: 3px;
    padding-bottom: 3px;
    border: 1px solid #aaa;
  }
  
.c-alert {
  display: block;
  padding: 1rem;
  width: 100%;
  margin-bottom: 1rem;
}

.c-alert--warn {
   border-color: #c28a8a;
   background-color: #ffe0e0;
}

.c-alert--ok {
   border-color: #17a55b;
   background-color: #c6e3c7;
}

.u-required-field {
  color: #c28a8a;
  font-weight: bold;
  float: right;
  padding-right: 5px;
}

.u-field-invalid {
  background-color: #ffe0e0;
  border: 1px solid #c28a8a;
}

.u-hidden {
  visibility: hidden;
}

@media (max-width: 500px) {
  .c-signup-form__label > span {
    display: block;
    width: 100%;
    padding-bottom: 0.5rem;
  }
  
  .c-signup-form__label > span ~ * {
    width: 60%;
  }
}

JavaScript

(function() {	
	var utils = {
  	getRequiredField: function() {
    	return '<span class="u-required-field">*</span>';
    },
    
    checkEmpty(fields) {
    	return [].filter.call(fields, function(field) {
      	return !field || field.value === '';
      });
    },
    
    createAlert(element, message, type = 'warn') {
    	var alert = '<span class="c-alert c-alert--'+ type + '">' + message + '</span>';
      
      if(element.querySelector('.c-alert')) {
      	element.querySelector('.c-alert').classList.add('c-alert--'+ type);
        element.querySelector('.c-alert').innerHTML = message;
        return;
      }
      element.insertAdjacentHTML('afterbegin', alert);   
    }
  };

	var page = {
  	init: function() {
    	var methods = document.querySelectorAll('[data-js]');
      
      [].forEach.call(methods, function(method) {
        var methodName = method.getAttribute('data-js');

        if(page[methodName]) {
        	page[methodName](method);
        }
      });
    }
  };
	
  page.formValidate = function(element) {
  	if(!element) {
    	return;
    }
    
    var fields = element.querySelectorAll('[data-required]');
    
    if(fields.length > 0) {
			[].forEach.call(fields, function(field) {
      	field.previousSibling.innerHTML += utils.getRequiredField();
        
        field.addEventListener('input', function() {
        	if(this.value !== '') {
          	this.classList.remove('u-field-invalid');
          }
        });
      });
    }
    
    element.querySelector('[type="submit"]').addEventListener('click', function(e) {
    	e.preventDefault();
      
    	var invalidFields = utils.checkEmpty(fields);
      var button = this;
      
      if(invalidFields.length > 0) {
      	[].forEach.call(invalidFields, function(invalidField) {
        	invalidField.classList.add('u-field-invalid');
        });
        
        utils.createAlert(element, 'Please fill out all required fields');
      } else {
      	// create AJAX request
       ...