# Part 4: Forms and User Input

## Introduction

Forms are the primary way users send data to a server. Every login page, search box, settings panel, and data entry screen is built on HTML form elements. Getting forms right — semantically correct labels, proper input types, built-in validation — reduces the amount of JavaScript needed and makes pages more accessible by default.

This part covers every form element I use regularly, along with the patterns that make forms work correctly without relying entirely on custom JavaScript.

***

## The `<form>` Element

```html
<form action="/submit" method="post">
  <!-- form fields -->
  <button type="submit">Submit</button>
</form>
```

Key attributes:

| Attribute      | Values                                                                   | Purpose                                                                                 |
| -------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| `action`       | URL                                                                      | Where to send the form data. Defaults to current URL if omitted.                        |
| `method`       | `get`, `post`                                                            | HTTP method. `get` appends data to URL; `post` sends in body.                           |
| `enctype`      | `application/x-www-form-urlencoded`, `multipart/form-data`, `text/plain` | How to encode form data. Required `multipart/form-data` for file uploads.               |
| `novalidate`   | boolean                                                                  | Disables browser's built-in validation (useful when handling validation in JavaScript). |
| `autocomplete` | `on`, `off`                                                              | Whether to allow browser autocomplete.                                                  |

**GET vs POST:**

* `GET` — data goes in the URL query string. Use for search forms, filtering, anything that should be bookmarkable.
* `POST` — data goes in the request body, not visible in the URL. Use for logins, data submissions, anything that modifies server state.

***

## Labels

Labels are the most important accessibility element in a form. Every interactive form field needs a label.

### Explicit Label

```html
<label for="username">Username</label>
<input type="text" id="username" name="username" />
```

The `for` attribute matches the `id` of the input. Clicking the label focuses the input — this is especially important on mobile where tap targets are small.

### Implicit Label (Wrapped)

```html
<label>
  Username
  <input type="text" name="username" />
</label>
```

When the input is inside the `<label>`, the `for`/`id` linkage is implicit. Both styles are valid.

**Never use placeholder text as a substitute for a label.** Placeholders disappear when typing starts and do not work as accessible labels. They are appropriate as examples of expected format, not as the field's purpose.

***

## The `<input>` Element

`<input>` is the most versatile form element. The `type` attribute controls what kind of input it renders.

### Text Inputs

```html
<!-- Single-line text -->
<input type="text" name="username" id="username" placeholder="e.g. htunn" />

<!-- Email — browser validates email format -->
<input type="email" name="email" id="email" />

<!-- Password — masks input -->
<input type="password" name="password" id="password" />

<!-- URL — browser validates URL format -->
<input type="url" name="website" id="website" />

<!-- Search — hint to browser for search-specific UI -->
<input type="search" name="q" id="q" />

<!-- Telephone — numeric keyboard on mobile -->
<input type="tel" name="phone" id="phone" />
```

Using the correct `type` does several things without any JavaScript:

* Triggers the right keyboard on mobile (numeric for `tel`, `@` key for `email`)
* Enables browser-native format validation
* Enables browser autocomplete for the correct category of data

### Number Inputs

```html
<input type="number" name="port" id="port" min="1" max="65535" step="1" value="8080" />
```

### Date and Time Inputs

```html
<input type="date" name="start-date" id="start-date" />
<input type="time" name="meeting-time" id="meeting-time" />
<input type="datetime-local" name="scheduled-at" id="scheduled-at" />
<input type="month" name="billing-month" id="billing-month" />
```

Date inputs render a date picker natively in most browsers, removing the need for a JavaScript date picker library for basic use.

### Checkbox

```html
<label>
  <input type="checkbox" name="agree" id="agree" value="yes" />
  I agree to the terms of service
</label>
```

For multiple independent options:

```html
<fieldset>
  <legend>Notification preferences</legend>
  <label><input type="checkbox" name="notify" value="email" /> Email</label>
  <label><input type="checkbox" name="notify" value="sms" checked /> SMS</label>
  <label><input type="checkbox" name="notify" value="push" /> Push notification</label>
</fieldset>
```

`checked` pre-checks the box.

### Radio Buttons

Radio buttons form a group where only one can be selected. Group them with the same `name`:

```html
<fieldset>
  <legend>Scan mode</legend>
  <label><input type="radio" name="mode" value="passive" checked /> Passive</label>
  <label><input type="radio" name="mode" value="active" /> Active</label>
  <label><input type="radio" name="mode" value="aggressive" /> Aggressive</label>
</fieldset>
```

### File Input

```html
<label for="report-upload">Upload scan report</label>
<input type="file" id="report-upload" name="report" accept=".json,.txt,.pdf" />
```

For multiple files:

```html
<input type="file" name="logs" multiple accept=".log,.txt" />
```

The form must use `enctype="multipart/form-data"` for file uploads to work.

### Hidden Input

```html
<input type="hidden" name="csrf_token" value="abc123xyz" />
```

Hidden inputs submit data with the form without being visible to the user. Commonly used for CSRF tokens, pre-filled values, or state management.

### Range Slider

```html
<label for="timeout">Request timeout: <span id="timeout-value">30</span>s</label>
<input type="range" id="timeout" name="timeout" min="5" max="120" step="5" value="30" />
```

### Color Picker

```html
<label for="highlight-color">Highlight color</label>
<input type="color" id="highlight-color" name="highlight-color" value="#ff6b35" />
```

***

## `<textarea>`

Multi-line text input:

```html
<label for="notes">Notes</label>
<textarea id="notes" name="notes" rows="6" cols="50" placeholder="Optional notes..."></textarea>
```

`rows` and `cols` set the default visible size. `resize: none` in CSS if you want to prevent user resizing.

***

## `<select>` and `<option>`

Dropdown menu:

```html
<label for="region">Region</label>
<select id="region" name="region">
  <option value="">Select a region…</option>
  <option value="us-east-1">US East (N. Virginia)</option>
  <option value="ap-southeast-1">Asia Pacific (Singapore)</option>
  <option value="eu-west-1">Europe (Ireland)</option>
</select>
```

Pre-select an option with `selected`:

```html
<option value="ap-southeast-1" selected>Asia Pacific (Singapore)</option>
```

### Option Groups

```html
<select id="runtime" name="runtime">
  <optgroup label="Python">
    <option value="python3.12">Python 3.12</option>
    <option value="python3.11">Python 3.11</option>
  </optgroup>
  <optgroup label="Rust">
    <option value="rust-stable">Stable</option>
    <option value="rust-nightly">Nightly</option>
  </optgroup>
</select>
```

### Multiple Select

```html
<select id="tags" name="tags" multiple size="4">
  <option value="security">security</option>
  <option value="rust">rust</option>
  <option value="automation">automation</option>
  <option value="cli">cli</option>
</select>
```

***

## `<fieldset>` and `<legend>`

Group related inputs visually and semantically:

```html
<fieldset>
  <legend>Target Configuration</legend>

  <label for="target-url">Target URL</label>
  <input type="url" id="target-url" name="target_url" required />

  <label for="scan-depth">Scan depth</label>
  <input type="number" id="scan-depth" name="scan_depth" min="1" max="10" value="3" />
</fieldset>

<fieldset>
  <legend>Authentication</legend>

  <label for="api-key">API Key</label>
  <input type="password" id="api-key" name="api_key" autocomplete="current-password" />
</fieldset>
```

Screen readers announce the `<legend>` text as context for the fields within the `<fieldset>`.

***

## Buttons

```html
<!-- Submit the form -->
<button type="submit">Submit</button>

<!-- Reset all fields to default values -->
<button type="reset">Reset</button>

<!-- Button with no default form behaviour (trigger JS) -->
<button type="button" id="preview-btn">Preview</button>
```

Always specify `type`. Without it, the default is `submit`, which means buttons inside a form will submit it unless explicitly typed otherwise. This catches many developers by surprise.

***

## Built-in Validation Attributes

HTML provides validation attributes that work without JavaScript:

| Attribute         | Purpose                             |
| ----------------- | ----------------------------------- |
| `required`        | Field must have a value             |
| `minlength="n"`   | Minimum character count             |
| `maxlength="n"`   | Maximum character count             |
| `min="n"`         | Minimum value (number/date inputs)  |
| `max="n"`         | Maximum value (number/date inputs)  |
| `pattern="regex"` | Value must match regular expression |
| `type="email"`    | Browser validates email format      |
| `type="url"`      | Browser validates URL format        |

```html
<label for="api-url">API Endpoint</label>
<input
  type="url"
  id="api-url"
  name="api_url"
  required
  placeholder="https://api.example.com"
/>

<label for="token">Access Token</label>
<input
  type="text"
  id="token"
  name="token"
  required
  minlength="32"
  maxlength="256"
  pattern="[A-Za-z0-9_\-]+"
  autocomplete="off"
/>
```

Use the `:valid` and `:invalid` CSS pseudo-classes to style fields based on validation state:

```css
input:invalid {
  border-color: #e53e3e;
}

input:valid {
  border-color: #38a169;
}
```

***

## Accessibility Best Practices for Forms

1. **Every input has a visible label** — do not rely on placeholder text alone
2. **Group related fields with `<fieldset>` and `<legend>`**
3. **Provide error messages** — don't just highlight in red, say what's wrong in text
4. **Associate error messages with inputs** using `aria-describedby`:

```html
<label for="email">Email</label>
<input
  type="email"
  id="email"
  name="email"
  aria-describedby="email-error"
  required
/>
<p id="email-error" role="alert" class="error-message">
  Please enter a valid email address.
</p>
```

5. **Use `autocomplete` attributes** to help password managers and browsers fill fields correctly:

```html
<input type="text" name="name" autocomplete="name" />
<input type="email" name="email" autocomplete="email" />
<input type="password" name="current-password" autocomplete="current-password" />
<input type="password" name="new-password" autocomplete="new-password" />
```

***

## A Complete Form Example

```html
<form action="/scan" method="post">
  <fieldset>
    <legend>Scan Configuration</legend>

    <div class="field">
      <label for="target">Target URL</label>
      <input
        type="url"
        id="target"
        name="target"
        placeholder="https://example.com"
        required
      />
    </div>

    <div class="field">
      <label for="timeout">Request Timeout (seconds)</label>
      <input
        type="number"
        id="timeout"
        name="timeout"
        min="5"
        max="120"
        value="30"
      />
    </div>

    <fieldset>
      <legend>Scan Mode</legend>
      <label><input type="radio" name="mode" value="passive" checked /> Passive</label>
      <label><input type="radio" name="mode" value="active" /> Active</label>
    </fieldset>

    <div class="field">
      <label for="payload-file">Custom Payload File (optional)</label>
      <input type="file" id="payload-file" name="payload_file" accept=".json" />
    </div>
  </fieldset>

  <fieldset>
    <legend>Output Options</legend>

    <label>
      <input type="checkbox" name="verbose" value="1" />
      Verbose output
    </label>

    <label for="output-format">Output Format</label>
    <select id="output-format" name="output_format">
      <option value="terminal">Terminal</option>
      <option value="json">JSON</option>
      <option value="csv">CSV</option>
    </select>
  </fieldset>

  <button type="submit">Start Scan</button>
  <button type="reset">Reset</button>
</form>
```

***

## Summary

| Element                             | Purpose                             |
| ----------------------------------- | ----------------------------------- |
| `<form action method>`              | Container for all form fields       |
| `<label for>`                       | Accessible label linked to an input |
| \`\<input type="text                | email                               |
| \`\<input type="checkbox            | radio">\`                           |
| `<input type="file">`               | File uploads                        |
| `<input type="hidden">`             | Hidden data (CSRF tokens etc.)      |
| `<textarea>`                        | Multi-line text input               |
| `<select>` / `<option>`             | Dropdown menus                      |
| `<fieldset>` / `<legend>`           | Grouping related fields             |
| \`\<button type="submit             | reset                               |
| `required`, `pattern`, `min`, `max` | Built-in validation                 |
| `autocomplete`                      | Hint for browser/password manager   |
| `aria-describedby`                  | Link error messages to inputs       |

***

## Up Next

[Part 5](https://blog.htunnthuthu.com/getting-started/programming/html-101/html-101-part-5) covers accessibility, SEO fundamentals, and HTML5 features that make pages more robust and discoverable.
