<form id="form">
  
  <div>
    パスワード
    <input type="password" name="_password" required/>
  </div>
  
  <div>
    新しいパスワード
    <input type="password" name="_passwordConfirm" required/>
    <small id="error"></small>
  </div>
  
  <button type="submit">送信</button>
</form>
input,
select {
  &:valid {
    border: 1px solid green;
  }
  &:invalid {
    border: 1px solid red;
  }
}

// 案件上デフォルトのstyleで使われることがほぼないのでstyleは適当
[type='radio'],
[type='checkbox'] {
  &:valid {
    box-shadow: green 0px 0px 3px 1px;
  }
  &:invalid {
    box-shadow: red 0px 0px 3px 1px;
  }
}

small {
  color: red;
}
View Compiled
const $form = document.getElementById('form');
const $error = document.getElementById('error');

// 親要素(form)にイベントをつけると子要素のイベントが拾える
$form.addEventListener('change', update);
$form.addEventListener('input', update);

function update(e) {
  const type = e.target.type;
  const validity = e.target.validity;
  
  // フォームの子要素の取得
  const $password = $form.elements['_password'];  // $form[子要素のname属性]でもアクセスできる
  const $passwordConfirm = $form.elements['_passwordConfirm'];
  
  // カスタムエラーを初期化
  $passwordConfirm.setCustomValidity('');
  if ($password.value !== $passwordConfirm.value) {
    // パスワードと新しいパスワードが一致しない場合、カスタムエラーをセットする
    $passwordConfirm.setCustomValidity('パスワードが一致しません');
  }
  // エラーメッセージを更新
  $error.innerHTML = $passwordConfirm.validationMessage;
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.