Lesson 12 of 20

HTML Forms - Part 2

Input Types Do More Than Change the Box

<input> is one element that becomes many different controls depending on its type attribute. Choosing the right type is not cosmetic. It changes the on-screen keyboard a phone shows, whether the browser validates the value, whether autofill offers to complete it, and what a screen reader announces.

The on-screen keyboard alone justifies the effort. type="email" gives a mobile visitor a keyboard with the at sign and a full stop already on it. type="tel" gives a large numeric keypad. type="url" adds a slash key. Leave everything as type="text" and every field gets the ordinary keyboard, which means more taps and more mistakes for the majority of your visitors, who are on phones.

One type deserves a warning. type="number" is for genuine quantities you might do arithmetic on — a team size, a price, an age. It is the wrong choice for phone numbers, roll numbers, PIN codes and OTPs, even though they are made of digits. A number field strips leading zeros, rejects the plus sign in an international dialling code, and shows tiny spinner arrows that make no sense for an identifier. For those, use type="tel", or type="text" with inputmode="numeric", which asks for a numeric keypad without any of the number field's behaviour.

Example
<label for="email">Email</label>
<input type="email" id="email" name="email">

<label for="phone">Mobile number</label>
<input type="tel" id="phone" name="phone" inputmode="numeric">

<label for="site">Project website</label>
<input type="url" id="site" name="website">

<label for="pwd">Password</label>
<input type="password" id="pwd" name="password" minlength="8">

<label for="dob">Date of birth</label>
<input type="date" id="dob" name="dob">

<label for="size">Team size</label>
<input type="number" id="size" name="teamsize" min="1" max="4">

<label for="photo">Upload your project photo</label>
<input type="file" id="photo" name="photo" accept="image/*">

<!-- Wrong: a roll number is not a quantity -->
<input type="number" name="rollnumber">
  • text — anything short and unstructured
  • email, url, tel — better keyboards, and the first two are format-checked
  • password — characters are masked on screen; the value is not encrypted by this attribute
  • number — real quantities only, with min, max and step
  • date, time, datetime-local, month — date and time pickers
  • file — file upload; add accept to filter and multiple to allow several
  • range, color, search, hidden — sliders, colour pickers, search boxes and values the user never sees
Notes
  • A date input looks different in every browser, and on some it is a text box. What is consistent is the value it submits: always yyyy-mm-dd, regardless of how the picker displayed it. Write your server code against that format, not against what you see on screen.

Checkboxes and Radio Buttons

Checkboxes are for choices that are independent of each other — tick none, one, or all of them. Radio buttons are for one choice out of several, where selecting a new option clears the previous one. Getting these the wrong way round is a design decision more than a coding one: if the visitor could reasonably want two answers, use checkboxes.

Radio buttons only behave as a group because they share a name. That shared name is what makes the browser treat them as mutually exclusive, and it is also the key the server receives, with the chosen option's value as its value. Give each radio a different name by mistake and every one of them becomes independently tickable — the classic symptom of this bug.

The value attribute matters more here than on a text field. On a text input, the value is whatever the visitor typed. On a checkbox or radio, the visitor types nothing, so value is the only thing that gets submitted. Omit it and a ticked checkbox submits the string on, which tells your server nothing useful. And remember that an unticked checkbox sends nothing at all — your server code must treat "absent" as "not selected" rather than expecting a false value.

Add checked to pre-select an option. It takes no value; its presence is enough. Pre-selecting a sensible default saves the visitor work, but never pre-tick a consent checkbox — the answer has to be the visitor's.

Example
<!-- Independent choices: several may be ticked -->
<fieldset>
  <legend>Which sessions will you attend?</legend>

  <label>
    <input type="checkbox" name="sessions" value="soldering">
    Soldering basics
  </label>

  <label>
    <input type="checkbox" name="sessions" value="sensors" checked>
    Sensor wiring
  </label>

  <label>
    <input type="checkbox" name="sessions" value="coding">
    Arduino coding
  </label>
</fieldset>

<!-- One choice only: the shared name is what groups them -->
<fieldset>
  <legend>Your branch</legend>

  <label><input type="radio" name="branch" value="cse"> Computer Science</label>
  <label><input type="radio" name="branch" value="ece"> Electronics</label>
  <label><input type="radio" name="branch" value="mech"> Mechanical</label>
</fieldset>

<!-- Broken: different names, so all three can be selected at once -->
<input type="radio" name="branch1" value="cse">
<input type="radio" name="branch2" value="ece">
Notes
  • Once a radio group has been touched there is no way for the visitor to unselect every option. If "no answer" is a legitimate response, include it as an explicit choice such as "Prefer not to say" rather than expecting people to leave the group blank.

fieldset and legend: Grouping That Screen Readers Understand

In the examples above, the radio buttons sit inside a <fieldset> with a <legend>. That is not decoration. Each radio button's own label says "Computer Science", but that only makes sense if you already know the question. A screen reader user arriving at the third option in a long form hears "Mechanical, radio button, three of three" and has no idea what is being asked.

<legend> fixes exactly that. When controls are wrapped in a fieldset, assistive technology announces the legend along with each control, so the user hears the question and the option together. It is the difference between a form that can be completed without sight and one that cannot.

The rule for <legend> is that it must be the first child of the <fieldset>, exactly as <caption> must be the first child of a table. Use fieldsets for any set of related controls — a radio group, a set of checkboxes answering one question, or a block of address fields. Do not wrap every single input in its own fieldset; that adds noise without adding meaning.

Example
<fieldset>
  <legend>Delivery address</legend>

  <label for="line1">Street address</label>
  <input type="text" id="line1" name="address_line1" autocomplete="address-line1">

  <label for="city">City</label>
  <input type="text" id="city" name="city" autocomplete="address-level2">

  <label for="pin">PIN code</label>
  <input type="text" id="pin" name="pincode"
         inputmode="numeric" autocomplete="postal-code">
</fieldset>
Notes
  • Fieldsets are notoriously awkward to style, because their default border and the way the legend sits on it resist normal CSS. Set border: none and padding: 0 on the fieldset and style the legend as an ordinary heading — you keep all the accessibility benefit and lose the odd appearance.

textarea, select and datalist

<textarea> is a multi-line text box, and it is the one form control that does not use a value attribute. Its initial content goes between the opening and closing tags, exactly like a paragraph. Writing <textarea value="Hello"></textarea> produces an empty box and puzzles people for a while. Note also that whatever you type between those tags is preserved literally, including line breaks and indentation, so an indented empty textarea in your source arrives at the server full of spaces.

The rows and cols attributes set its size in lines and characters. They work, but CSS width and height give you far better control and adapt to the screen, so most projects set rows as a reasonable default and size the rest in the stylesheet.

<select> creates a dropdown from a set of <option> elements. Each option's value attribute is what gets submitted; if you leave value off, the option's visible text is submitted instead. That fallback is convenient but fragile — the day someone rewrites "Computer Science" as "Computer Science and Engineering", your stored data changes with it. Set an explicit short value and the display text becomes free to change. Use selected to choose a default, <optgroup> to organise a long list into labelled sections, and multiple if several choices are allowed — though a multiple-select is genuinely hard to use, and a set of checkboxes is usually kinder.

<datalist> sits between the two. It attaches a list of suggestions to an ordinary text input, so the visitor gets autocomplete help but may still type anything. Use a <select> when the answer must be one of your options, and a <datalist> when your options are only hints.

Example
<label for="about">Tell us about your project</label>
<textarea id="about" name="about" rows="5"
          maxlength="500"
          placeholder="What does it do, and what did you build it with?"></textarea>

<label for="branch">Branch</label>
<select id="branch" name="branch">
  <option value="">Choose your branch</option>
  <optgroup label="Engineering">
    <option value="cse">Computer Science</option>
    <option value="ece" selected>Electronics</option>
    <option value="mech">Mechanical</option>
  </optgroup>
  <optgroup label="Sciences">
    <option value="phy">Physics</option>
  </optgroup>
</select>

<!-- Suggestions, but any value is allowed -->
<label for="board">Which board are you using?</label>
<input type="text" id="board" name="board" list="boards">
<datalist id="boards">
  <option value="Arduino Uno">
  <option value="Arduino Nano">
  <option value="ESP32">
  <option value="Raspberry Pi Pico">
</datalist>
Notes
  • The first option in the branch dropdown has an empty value and reads "Choose your branch". That pattern matters: without it, the first real option is pre-selected, so a visitor who never touches the dropdown silently submits an answer they did not choose. With an empty value, required on the select will also refuse it.

Making a Long Form Bearable

The difference between a form people finish and a form they abandon is mostly small details. The autocomplete attribute is the biggest single win: it lets the browser fill in a name, email, phone number or address that the visitor has already entered elsewhere. It takes standard values such as name, email, tel and postal-code, and using them turns a two-minute form into a two-tap one. On a phone, this matters more than anything else on this page.

The order of your fields is the tab order, so keep the markup in the order people will fill it in. autofocus puts the cursor in the first field on load — useful on a page whose only purpose is the form, irritating on a page with content above it, because it scrolls the visitor past what they were reading.

Finally, group related fields, ask for as little as you actually need, and mark optional fields rather than required ones if most of them are required. Every extra question is another reason to give up.

  • Add autocomplete to name, email, phone and address fields — it costs one attribute and saves real effort
  • Keep source order the same as the visual order, because that is the keyboard tab order
  • Use inputmode="numeric" for digit-only fields that are not quantities
  • Group related controls in a <fieldset> with a clear <legend>
  • Never rely on colour alone to mark an error; add text saying what is wrong and how to fix it
  • Ask for the minimum: every additional field measurably reduces the number of people who finish
A Complete Multi-Control Form
HTML
<h2>Techfest — Project Submission</h2>

<form action="/submit" method="post">

  <fieldset>
    <legend>Your details</legend>

    <p>
      <label for="name">Full name</label><br>
      <input type="text" id="name" name="fullname"
             autocomplete="name" required>
    </p>

    <p>
      <label for="email">Email</label><br>
      <input type="email" id="email" name="email"
             autocomplete="email" placeholder="you@college.edu" required>
    </p>

    <p>
      <label for="phone">Mobile number</label><br>
      <input type="tel" id="phone" name="phone"
             inputmode="numeric" autocomplete="tel">
    </p>
  </fieldset>

  <fieldset>
    <legend>Your project</legend>

    <p>
      <label for="branch">Branch</label><br>
      <select id="branch" name="branch" required>
        <option value="">Choose your branch</option>
        <option value="cse">Computer Science</option>
        <option value="ece">Electronics</option>
        <option value="mech">Mechanical</option>
      </select>
    </p>

    <p>
      <label for="size">Team size</label><br>
      <input type="number" id="size" name="teamsize"
             min="1" max="4" value="1">
    </p>

    <p>
      <label for="about">Project description</label><br>
      <textarea id="about" name="about" rows="4" maxlength="500"
                placeholder="What does it do, and what did you build it with?"></textarea>
    </p>
  </fieldset>

  <fieldset>
    <legend>Category</legend>
    <label><input type="radio" name="category" value="hardware"> Hardware</label><br>
    <label><input type="radio" name="category" value="software"> Software</label><br>
    <label><input type="radio" name="category" value="both"> Both</label>
  </fieldset>

  <p>
    <label>
      <input type="checkbox" name="rules" value="agreed" required>
      I have read the competition rules
    </label>
  </p>

  <p><button type="submit">Submit project</button></p>

</form>
CSS
body {
  font-family: system-ui, Arial, sans-serif;
  line-height: 1.6;
  padding: 24px;
  max-width: 560px;
}
fieldset {
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 16px 20px;
  margin: 0 0 20px;
}
legend {
  font-weight: 700;
  padding: 0 6px;
}
label {
  font-size: 0.95rem;
}
input[type="text"],
input[type="email"],
input[type="tel"],
input[type="number"],
select,
textarea {
  width: 100%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 6px;
  font: inherit;
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid #7a0060;
  outline-offset: 2px;
}
button {
  padding: 12px 22px;
  border: none;
  border-radius: 6px;
  background: #7a0060;
  color: #fff;
  font-size: 1rem;
  cursor: pointer;
}
Notes
  • Try filling this form using only the keyboard: Tab to move forward, Shift+Tab to go back, Space to tick a checkbox, arrow keys to move within a radio group. If you can complete it without touching the mouse, so can everyone else — and if you cannot, neither can a visitor who relies on the keyboard.
Ask AI