form(action='#' method="get")
  p HTML5 validation doesn't just have to apply to individual form fields. It can also apply to <code>fieldset</code> and <code>form</code> elements. <a href="https://codepen.io/ross-angus/post/fun-with-html5-form-validation">Full write-up</a>.
  fieldset
    legend Personal information
    p
      <label for="forename">First name</label>
      <input type="text" id="forename" required>
    p
      <label for="surname">Family name</label>
      <input type="text" id="surname" required>
    p
      <label for="email">Email address</label>
      <input type="email" id="email" required>

  fieldset
    legend More personal information
    p
      Gender
      <input type="radio" id="gender-female" name="gender" required>
      <label for="gender-female">Female</label>
      <input type="radio" id="gender-male" name="gender" required>
      <label for="gender-female">Male</label>
      <input type="radio" id="gender-none" name="gender" required>
      <label for="gender-none">There is almost never a good reason to ask this question</label>

  fieldset
    legend <em>Very</em> personal information
    p
      <label for="politics">Select your political alignment</label>
      <input type="range" id="politics" min="0" max="100" value="50" step="1">
    p
      <label for="hair">Colour of mother's hair, at time of your birth</label>
      <input type="color" id="hair" value="#99cc99">
    p
      <label for="password">Most frequently used password</label>
      <input type="password" id="password" required>
  p
    button This won't submit anywhere. <em>Promise</em>.
    
h2 Disclaimer
p Adding content into the <code>:before</code> and <code>:after</code> elements is a bad idea. It can't be selected and doesn't appear in the accessibility DOM.
View Compiled
body {
  font-family: Calibri, Segoe, "Segoe UI", "Gill Sans", "Gill Sans MT", sans-serif;
}
input, select, textarea {
  display: block;
  font-family: inherit;
  font-size: 1em;
  width: 100%;
}
input[type="radio"] {
  display: inline-block;
  width: auto;
}
form, fieldset {
  margin: 1em;
  padding: 1em;
}
p, fieldset {
  margin: 0 0 1em;
  &:last-child {margin-bottom: 0;}
}

form, fieldset {
  position: relative;
  &:before, &:after {
    display: block;
    font-weight: bold;
  }
  // Error
  &:before {
    color: #900;
    margin: 0 0 1em;
  }
  // Thanks
  &:after {
    color: #060;
    margin: 1em 0 0;
  }
  &:invalid {
    border: dashed .2em #fcc;
    &:before {
      content: "You've still got some form fields to complete here."
    }
  }
  &:valid {
    border: solid .2em #9c9;
    &:after {
      content: "Good job, buddy! I promise I won't misuse this information."
    }
  }
}

fieldset {
  &:invalid {
    border: dashed .2em #fcc;
    &:before {
      content: "You've still got some form fields to complete here.";
    }
  }
  // Added to the bottom of a valid <fieldset/>
  &:valid {
    border: solid .2em #9c9;
    // ✓ Complete
    &:before {
      color: #060;
      content: "✓";
      font-size: 20vw;
      left: 0;
      line-height: 1;
      opacity: .2;
      pointer-events: none;
      position: absolute;
      right: 0;
      text-align: center;
      top: 50%;
      transform: translateY(-50%);
    }  
    &:after {
      content: "Thanks! Now do the next section.";
    }
  }
  // This is added to the last <fieldset/>, when that fieldset is valid
  // Note that if earlier fieldsets are invalid, this content will still appear
  &:last-of-type:valid:after {
      content: "Now hit that submit button, so I can have your data."
  }
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.