Example in Action: How to Structure a Web Form

Common HTML structures used with forms

Beyond the structures specific to web forms, it's good to remember that form markup is just HTML. This means that you can use all the power of HTML to structure a web form.

As you can see in the examples, it's common practice to wrap a label and its widget with a <li> element within a <ul> or <ol> list. <p> and <div> elements are also commonly used. Lists are recommended for structuring multiple checkboxes or radio buttons.

In addition to the <fieldset> element, it's also common practice to use HTML titles (e.g. h1, h2) and sectioning (e.g. <section>) to structure complex forms.

Above all, it is up to you to find a comfortable coding style that results in accessible, usable forms. Each separate section of functionality should be contained in a separate <section> element, with <fieldset> elements to contain radio buttons.


Active learning: building a form structure

Let's put these ideas into practice and build a slightly more involved form - a payment form. This form will contain a number of control types that you may not yet understand. Don't worry about this for now; you'll find out how they work in the next article (Basic native form controls). For now, read the descriptions carefully as you follow the below instructions, and start to form an appreciation of which wrapper elements we are using to structure the form, and why.

  1. To start with, make a local copy of our blank template file and the CSS for our payment form in a new directory on your computer.
  2. Apply the CSS to the HTML by adding the following line inside the HTML <head>:
  3. <link href="payment-form.css" rel="stylesheet" />
  4. Next, create your form by adding a <form> element:
  5. <form>
    ...
    </form>
  6. Inside the <form> element, add a heading and paragraph to inform users how required fields are marked:
  7. <h1>Payment form</h1>
    <p>
      Required fields are followed by
      <strong><span aria-label="required">*</span></strong>.
    </p>
  8. Next, we'll add a larger section of code into the form, below our previous entry. Here you'll see that we are wrapping the contact information fields inside a distinct <section> element. Moreover, we have a set of three radio buttons, each of which we are putting inside its own list (<li>) element. We also have two standard text <input>s and their associated <label> elements, each contained inside a <p>, and a password input for entering a password. Add this code to your form:
  9. <section>
      <h2>Contact information</h2>
      <fieldset>
        <legend>Title</legend>
        <ul>
          <li>
            <label for="title_1">
              <input type="radio" id="title_1" name="title" value="K" />
              King
            </label>
          </li>
          <li>
            <label for="title_2">
              <input type="radio" id="title_2" name="title" value="Q" />
              Queen
            </label>
          </li>
          <li>
            <label for="title_3">
              <input type="radio" id="title_3" name="title" value="J" />
              Joker
            </label>
          </li>
        </ul>
      </fieldset>
      <p>
        <label for="name">
          <span>Name: </span>
          <strong><span aria-label="required">*</span></strong>
        </label>
        <input type="text" id="name" name="username" required />
      </p>
      <p>
        <label for="mail">
          <span>Email: </span>
          <strong><span aria-label="required">*</span></strong>
        </label>
        <input type="email" id="mail" name="usermail" required />
      </p>
      <p>
        <label for="pwd">
          <span>Password: </span>
          <strong><span aria-label="required">*</span></strong>
        </label>
        <input type="password" id="pwd" name="password" required />
      </p>
    </section>
  10. The second <section> of our form is the payment information. We have three distinct controls along with their labels, each contained inside a <p>. The first is a drop-down menu (<select>) for selecting credit card type. The second is an <input> element of type tel, for entering a credit card number; while we could have used the number type, we don't want the number's spinner UI. The last one is an <input> element of type text, for entering the expiration date of the card; this includes a placeholder attribute indicating the correct format, and a pattern that tests that the entered date has the correct format. These newer input types are reintroduced in The HTML5 input types. Enter the following below the previous section:
  11. <section>
      <h2>Payment information</h2>
      <p>
        <label for="card">
          <span>Card type:</span>
        </label>
        <select id="card" name="usercard">
          <option value="visa">Visa</option>
          <option value="mc">Mastercard</option>
          <option value="amex">American Express</option>
        </select>
      </p>
      <p>
        <label for="number">
          <span>Card number:</span>
          <strong><span aria-label="required">*</span></strong>
        </label>
        <input type="tel" id="number" name="cardnumber" required />
      </p>
      <p>
        <label for="expiration">
          <span>Expiration date:</span>
          <strong><span aria-label="required">*</span></strong>
        </label>
        <input
          type="text"
          id="expiration"
          required="true"
          placeholder="MM/YY"
          pattern="^(0[1-9]|1[0-2])\/([0-9]{2})$" />
      </p>
    </section>
  12. The last section we'll add is a lot simpler, containing only a <button> of type submit, for submitting the form data. Add this to the bottom of your form now:
  13. <section>
      <p>
        <button type="submit">Validate the payment</button>
      </p>
    </section>
  14. Finally, complete your form by adding the outer <form> closing tag:
  15. </form>

You can see the finished payment-form.html source on GitHub and running live.