Accessible Form - bare

In this example below you will see how to do a Accessible Form - bare with some HTML / CSS and Javascript

This awesome code was written by jcozzo, you can see more from this user in the personal repository.
You can find the original code on
Copyright jcozzo ©


  • HTML
  • CSS
  • JavaScript
<!DOCTYPE html>
<html lang="en" >

  <meta charset="UTF-8">
  <title>Accessible Form - bare</title>
      <link rel="stylesheet" href="css/style.css">



  <form id="ch_form" class="ch-form" method="post" action="" novalidate="novalidate">
  <div class="contact-info">
    <div class="ch-form-item">
      <label for="first_name">First Name<abbr title="required">*</abbr>:</label>
      <input type="text" id="first_name" name="first_name" title="Please enter your first name" required="" aria-required="true" value="">
    </div> <!-- use required + aria-required="true" -->

    <div class="ch-form-item">
      <label for="last_name">Last Name<abbr title="required">*</abbr>:</label>
      <input type="text" id="last_name" name="last_name" title="Please enter your last name" required="" aria-required="true" value="">

    <div class="ch-form-item">
      <label for="address1">Address line 1<abbr title="required">*</abbr>:</label>
      <span id="address1_ex">Street address, P.O. box, company name, c/o</span>
      <input type="text" id="address1" name="address1" title="Please enter your address" required="" aria-describedby="address1_ex" aria-required="true" value="">

    <!-- aria-describedby -->

    <div class="ch-form-item">
      <label for="address2">Address line 2:</label>
      <span id="address2_ex">Apartment, suite, unit, building, floor, etc.</span>
      <input type="text" id="address2" name="address2" aria-describedby="address2_ex" value="">

    <div class="ch-form-item">
      <label for="city">City<abbr title="required">*</abbr>:</label>
      <input type="text" id="city" name="city" title="Please enter your city" required="" aria-required="true" value="">

    <!-- Fallback if no JS -->
    <div class="ch-form-item" style="display: none;">
      <label for="states">State<abbr title="required">*</abbr>:</label>
      <select id="states" name="states" title="Please enter your state" value="">
        <option value="AL">Alabama</option>
        <option value="AK">Alaska</option>
        <option value="AZ">Arizona</option>
        <option value="AR">Arkansas</option>
        <option value="CA">California</option>
        <option value="CO">Colorado</option>
        <option value="CT">Connecticut</option>
        <option value="DE">Delaware</option>
        <option value="DC">District Of Columbia</option>
        <option value="FL">Florida</option>
        <option value="GA">Georgia</option>
        <option value="HI">Hawaii</option>
        <option value="ID">Idaho</option>
        <option value="IL">Illinois</option>
        <option value="IN">Indiana</option>
        <option value="IA">Iowa</option>
        <option value="KS">Kansas</option>
        <option value="KY">Kentucky</option>
        <option value="LA">Louisiana</option>
        <option value="ME">Maine</option>
        <option value="MD">Maryland</option>
        <option value="MA">Massachusetts</option>
        <option value="MI">Michigan</option>
        <option value="MN">Minnesota</option>
        <option value="MS">Mississippi</option>
        <option value="MO">Missouri</option>
        <option value="MT">Montana</option>
        <option value="NE">Nebraska</option>
        <option value="NV">Nevada</option>
        <option value="NH">New Hampshire</option>
        <option value="NJ">New Jersey</option>
        <option value="NM">New Mexico</option>
        <option value="NY">New York</option>
        <option value="NC">North Carolina</option>
        <option value="ND">North Dakota</option>
        <option value="OH">Ohio</option>
        <option value="OK">Oklahoma</option>
        <option value="OR">Oregon</option>
        <option value="PA">Pennsylvania</option>
        <option value="RI">Rhode Island</option>
        <option value="SC">South Carolina</option>
        <option value="SD">South Dakota</option>
        <option value="TN">Tennessee</option>
        <option value="TX">Texas</option>
        <option value="UT">Utah</option>
        <option value="VT">Vermont</option>
        <option value="VA">Virginia</option>
        <option value="WA">Washington</option>
        <option value="WV">West Virginia</option>
        <option value="WI">Wisconsin</option>
        <option value="WY">Wyoming</option>

    <!-- JS only: only displayed if JS, hide states drop-down div -->
    <div class="ch-form-item" style="">
      <div class="ui-widget">
        <label for="state">State<abbr title="required">*</abbr>: </label>
        <input id="state" type="text" name="state" title="Please enter your state" required="required" aria-required="true" class="ui-autocomplete-input" autocomplete="off">
    <!-- end JS only -->

    <!-- Use type="text" for zip codes -->
    <!-- Pattern is for American postal codes -->

    <div class="ch-form-item">
      <label for="zip">ZIP<abbr title="required">*</abbr>:</label>
      <input type="text" id="zip" name="zip" title="Please enter a valid 5-digit zip code" pattern="(\d{5}([\-]\d{4})?)" required="" aria-required="true" value="">

    <!-- Input masks can do weird things to screen readers:
This formatter was build with accessibility in mind: -->

    <div class="ch-form-item">
      <label for="phone">Phone number:</label>
      <input type="tel" id="phone" name="phone" pattern=".*\d{3}.*\d{3}.*\d{4}" placeholder="(   ) -" title="Please enter a valid phone number">

    <!-- Email pattern -->

    <div class="ch-form-item">
      <label for="email">Email:</label>
      <input type="email" id="email" name="email" pattern="^[a-zA-Z0-9.!#$%&amp;'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" title="Please enter your email address" value="">

    <div class="ch-form-item">
      <label for="dob">Date of Birth:</label>
      <span id="dob_help">MM/DD/YYYY</span>
      <input type="date" id="dob" aria-describedby="dob_help" name="dob" value="">

    <div class="ch-form-item">
      <label for="textarea">Textarea:</label>
      <textarea id="textarea" name="textarea"></textarea>

  <fieldset aria-required="true">
    <legend>Checkboxes (check all that apply)</legend>
    <div class="ch-form-item">
      <input id="opt1" type="checkbox" name="checkbox" value="Option1">
      <label for="opt1">Checkbox option 1</label>
    <div class="ch-form-item">
      <input id="opt2" type="checkbox" name="checkbox" value="Option2">
      <label for="opt2">Checkbox option 2</label>
    <div class="ch-form-item">
      <input id="other" type="checkbox" name="checkbox" value="Other">
      <label for="other">Checkbox other: </label>
      <input type="text" id="textother" name="checkbox" data-checkbox="other" aria-labelledby="other" value="">

  <fieldset aria-required="true">
    <div class="ch-form-item">
      <input id="rad1" type="radio" name="radio" value="Radio1">
      <label for="rad1">Radio option 1</label>
    <div class="ch-form-item">
      <input id="rad2" type="radio" name="radio" value="Radio2">
      <label for="rad2">Radio option 2</label>
    <div class="ch-form-item">
      <input id="rad3" type="radio" name="radio" value="Radio3">
      <label for="rad3">Radio option 3</label>

  <fieldset aria-required="true">
    <legend>Radio buttons in a table (responsive)</legend>
    <table class="multi-label responsive-form"> <!-- multiple inputs for one label -->
        <tr class="hidden-small">
          <th class="hidden-visually" scope="col">Hidden Header</th> <!-- hidden label -->
          <th id="never" scope="col">Never</th>
          <th id="some" scope="col">Sometimes</th>
          <th id="many" scope="col">Often</th>
          <th id="a_1" scope="row">Radio label 1</th>
          <td data-label="Never">
            <input id="a1" type="radio" name="a_1" aria-labelledby="a_1 never" value="Never">
            <label for="a1"></label>
          <td data-label="Sometimes">
            <input id="a2" type="radio" name="a_1" aria-labelledby="a_1 some" value="Sometimes">
            <label for="a2"></label>
          <td data-label="Often">
            <input id="a3" type="radio" name="a_1" aria-labelledby="a_1 many" value="Often">
            <label for="a3"></label>

          <th id="b_1" scope="row">Radio label 2</th>
          <td data-label="Never">
            <input id="b1" type="radio" name="b_1" aria-labelledby="b_1 never" value="Never">
            <label for="b1"></label>
          <td data-label="Sometimes">
            <input id="b2" type="radio" name="b_1" aria-labelledby="b_1 some" value="Sometimes">
            <label for="b2"></label>
          <td data-label="Often">
            <input id="b3" type="radio" name="b_1" aria-labelledby="b_1 many" value="Often">
            <label for="b3"></label>

          <th id="c_1" scope="row">Radio label 3</th>
          <td data-label="Never">
            <input id="c1" type="radio" name="c_1" aria-labelledby="c_1 never" value="Never">
            <label for="c1"></label>
          <td data-label="Sometimes">
            <input id="c2" type="radio" name="c_1" aria-labelledby="c_1 some" value="Sometimes">
            <label for="c2"></label>
          <td data-label="Often">
            <input id="c3" type="radio" name="c_1" aria-labelledby="c_1 many" value="Often">
            <label for="c3"></label>

  <input type="submit" name="submit" value="Submit" class="btn">

  <script src=''></script>
<script src=''></script>
<script src=''></script>
<script src=''></script>
<script src=''></script>


    <script  src="js/index.js"></script>



/*Downloaded from */
/* Content for screen readers only: */
.hidden-visually {

/* Form Validation Errors */
.ch-form input.error,
.ch-form select.error {
  background: #fcc;

.ch-form label.error {
  position: absolute;
  left: 1em;
  bottom: -1.75em;
  width: auto;
  font-weight: normal;
  text-align: left;
  background: #ffffff;
  padding: 0.25em 0.5em;
  border: 1px solid #d9d9d9;
  z-index: 1;
  box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.5);

.ch-form fieldset label.error {
  left: 1em;
  padding: 0.5em;

.ch-form label.error:before,
.ch-form label.error:after {
  content: '';
  position: absolute;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  bottom: 100%;
  left: 1.5em;
  margin-left: -10px;

.ch-form label.error:before {
  border-bottom: 10px solid #d9d9d9;

.ch-form label.error:after {
  border-bottom: 10px solid #fff;
  margin-bottom: -2px;
  z-index: 2;

/* */
@media screen and (max-width: 24em) {
  .responsive-form {
    border: 0;
    display: table;
    max-width: 100%;
  .responsive-form thead {
    display: none;
  .responsive-form tr {
    margin-bottom: 1em;
    display: block;
  .responsive-form tr.hidden-small {
    display: none;
  .ch-form .multi-label.responsive-form th {
    font-weight: bold;
  .ch-form .multi-label.responsive-form td  {
    display: block;
    text-align: left;
    position: relative;
  .responsive-form td:last-child {
    border-bottom: 0;
  .responsive-form td:after {
    content: attr(data-label);
    position: absolute;
    top: 0;
    left: 30px;
  .responsive-form label {
    width: 100%;
    height: 20px;

/* Presentation only, not needed for accessibility */
body {
  font-size: 16px;
  font-family: sans-serif;

abbr {
 text-decoration: none;

.contact-info label {
  display: block;

label + span {
  display: block;
  color: #666;
  font-size: 0.875em;
  padding: 0.25rem 0;

fieldset {
  margin: 2em 0;
  position: relative;

fieldset .ch-form-item {
  margin: 0;

/*Downloaded from */
// Auto formats phone number (instead of using a mask)
  'pattern': '({{999}}) {{999}}-{{9999}}',
  'persistent': false //show pattern when user starts entering data "(###) ###-####"

  'pattern': '{{99999}}',

// add cursor after opening "("
$('#phone').focus(function() {
  if($('#phone').val() === "") {

// clear contents if user didn't enter anything to allow for validation
$('#phone').focusout(function() {
  if ($('#phone').val() === "(") {

// Form validation -

// Autocomplete states (instead of really long dropdown select) -

$('#state').attr('required', '').attr('aria-required', 'true').parent().parent().show();

$( function() {
  var availableStates = [
    "New Hampshire",
    "New Jersey",
    "New Mexico",
    "New York",
    "North Carolina",
    "North Dakota",
    "Rhode Island",
    "South Carolina",
    "South Dakota",
    "West Virginia",
  $( "#state" ).autocomplete({
    source: availableStates
} );