<form id="form" action="">
  <h1>Register</h1>
  <label for="username">Username</label>
  <input name="username" id="username" type="text" data-rules='["required"]'>
  <label for="email">Email</label>
  <input name="email" type="email" id="email" data-rules='["required","email"]'>
  <label for="password">Password</label>
  <input name="password" type="password" id="password" data-rules='["required","minimum:8"]'>
  <label for="passwordConf">Confirm Password</label>
  <input name="passwordConf" type="password" id="passwordConf" data-rules='["required","minimum:8"]'>
  <input type="submit">
</form>
* {
  font-family: "Inter", sans-serif;
}
form {
  width: 30%;
  min-width: 350px;
  margin: auto;
}
label {
  font-size: 1.5em;
}

input {
  width: 100%;
  display: block;
  font-size: 2em;
  border: solid 4px;
  border-color: hsl(210, 100%, 30%);
  margin-bottom: 1.5em;
}
input[type="submit"] {
  width: fit-content;
  font-size: 1.5em;
}
input[type="submit"]:focus {
  background-color: hsl(210, 100%, 80%);
}
.invalid {
  border-color: darkred;
  background-color: hsl(0, 30%, 95%);
}
import kingshottIodine from "https://cdn.skypack.dev/@kingshott/iodine";

let form = document.getElementById("form");
let inputs = [...form.querySelectorAll("input[data-rules]")];

function onSubmit(event) {
  inputs.map((input) => {
    if (Iodine.is(input.value, JSON.parse(input.dataset.rules)) !== true) {
      event.preventDefault();
      input.classList.add("invalid");
    }
  });
}
form.addEventListener("submit", onSubmit);

External CSS

  1. https://fonts.googleapis.com/css2?family=Inter:wght@500&amp;display=swap

External JavaScript

This Pen doesn't use any external JavaScript resources.