<main>
  <form action="#" novalidate>
    <p>This form uses the Constraint Validity API to handle input errors</p>
    <div class="form-control">
      <label for="email">Email address</label>
      <input type="email" name="email" id="email" required minlength="4"/>
      <small class="error"></small>
    </div>

    <button type="submit">Submit</button>
  </form>
</main>
* {
  box-sizing: border-box;
}

main {
  line-height: 1.4;
  height: 100vh;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #efefef;
  font-family: system-ui;
}

form {
  background-color: white;
  box-shadow: 0 10px 20px rgba(0,0,0,0.05);
  padding: 20px;
  border-radius: 8px;
  width: 300px;
  
  & > * + * {
    margin-top: 10px;
  }
}

label, input {
  display: block;
  width: 100%;
}

label {
  font-size: 0.85em;
  color: #444;
}

input {
  padding: 8px 6px;
  border: 1px solid #ddd;
  border-radius: 4px;
  margin-top: 3px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}

button {
  display: inline-block;
  background-color: rgb(79 70 229);
  color: white;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 4px 12px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.1);
  font-size: 16px;
}

small {
  font-size: 0.7em;
}

.error {
  color: crimson;
  display: none;
  margin-top: 3px;
  
  &.active {
    display: inline-block;
  }
}
View Compiled
// Lifted basically wholesale from MDN:
// https://github.com/mdn/learning-area/blob/main/html/forms/form-validation/detailed-custom-validation.html
const form  = document.getElementsByTagName('form')[0];

const email = document.getElementById('email');
const error = document.querySelector('.error');

function hideError() {
  error.innerText = "";
  error.classList.remove("active");
}

email.addEventListener('input', hideError);

form.addEventListener('submit', function (event) {
  // We're preventing default for the example here; in reality
  // you'd probably only preventDefault if there were errors.
  event.preventDefault();

  if(!email.validity.valid) {
    showError();
  }
});

function showError() {
  if(email.validity.valueMissing) {
    // If the field is empty
    // display the following error message.
    error.textContent = 'Please enter an email address.';
  } else if(email.validity.typeMismatch) {
    // If the field doesn't contain an email address
    // display the following error message.
    error.textContent = 'Entered value needs to be an e-mail address.';
  } else if(email.validity.tooShort) {
    // If the data is too short
    // display the following error message.
    error.textContent = `Email should be at least ${ email.minLength } characters; you entered ${ email.value.length }.`;
  }
  
  error.classList.add('active');
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.