Component Library

← Back to gallery

Address form

Category: Forms · Tags: address, form, autocomplete, fieldset, checkout

Shipping address

5 digits, for example 94110

How to use it

Use for a single shipping or billing address in checkout or account settings. Do not use it to search for a location; use a search field with suggestions instead. Give every id a unique prefix if a page shows two addresses, add the countries you actually ship to, and keep the labels in sync with the script's hint table.

Accessibility

A <fieldset> with a <legend> groups the fields, every input and the country <select> has its own <label for>, and each carries the matching autocomplete token (name, country, address-line1, address-line2, address-level2, address-level1, postal-code) so browsers and password managers can fill it. The postcode hint is linked through aria-describedby, and its wording and the field label follow the chosen country. Labels and input text are #0f172a on white (17.85:1), the hint and the "(optional)" marker #475569 on white (7.58:1), and every control shows a 2px #1d4ed8 focus outline.

Source (copy-ready)

<fieldset class="dcl-address">
  <legend class="dcl-address__legend">Shipping address</legend>
  <div class="dcl-address__field">
    <label class="dcl-address__label" for="dcl-address-country">Country or region</label>
    <select class="dcl-address__control" id="dcl-address-country" name="country" autocomplete="country">
      <option value="US">United States</option>
      <option value="GB">United Kingdom</option>
      <option value="CA">Canada</option>
      <option value="DE">Germany</option>
      <option value="JP">Japan</option>
      <option value="AU">Australia</option>
    </select>
  </div>
  <div class="dcl-address__field">
    <label class="dcl-address__label" for="dcl-address-name">Full name</label>
    <input class="dcl-address__control" type="text" id="dcl-address-name" name="name" autocomplete="name" required>
  </div>
  <div class="dcl-address__field">
    <label class="dcl-address__label" for="dcl-address-line1">Street address</label>
    <input class="dcl-address__control" type="text" id="dcl-address-line1" name="address-line1" autocomplete="address-line1" required>
  </div>
  <div class="dcl-address__field">
    <label class="dcl-address__label" for="dcl-address-line2">Apartment, suite or unit <span class="dcl-address__optional">(optional)</span></label>
    <input class="dcl-address__control" type="text" id="dcl-address-line2" name="address-line2" autocomplete="address-line2">
  </div>
  <div class="dcl-address__row">
    <div class="dcl-address__field">
      <label class="dcl-address__label" for="dcl-address-city">City or town</label>
      <input class="dcl-address__control" type="text" id="dcl-address-city" name="address-level2" autocomplete="address-level2" required>
    </div>
    <div class="dcl-address__field">
      <label class="dcl-address__label" for="dcl-address-region">State, province or region</label>
      <input class="dcl-address__control" type="text" id="dcl-address-region" name="address-level1" autocomplete="address-level1">
    </div>
    <div class="dcl-address__field">
      <label class="dcl-address__label dcl-address__postcode-label" for="dcl-address-postcode">ZIP code</label>
      <input class="dcl-address__control" type="text" id="dcl-address-postcode" name="postal-code" autocomplete="postal-code" aria-describedby="dcl-address-postcode-hint" required>
      <p class="dcl-address__hint" id="dcl-address-postcode-hint">5 digits, for example 94110</p>
    </div>
  </div>
</fieldset>
<style>
.dcl-address { display: flex; flex-direction: column; gap: 0.9rem; max-width: 36rem; min-width: 0; margin: 0; padding: 1rem; border: 1px solid #cbd5e1; border-radius: 0.5rem; font: inherit; color: #0f172a; background-color: #ffffff; }
.dcl-address__legend { padding: 0 0.35rem; font-weight: 700; color: #0f172a; background-color: #ffffff; }
.dcl-address__row { display: flex; flex-wrap: wrap; gap: 0.9rem; }
.dcl-address__field { display: flex; flex-direction: column; gap: 0.3rem; flex: 1 1 9rem; min-width: 0; }
.dcl-address__label { font-weight: 600; color: #0f172a; background-color: #ffffff; }
.dcl-address__optional { font-weight: 400; color: #475569; background-color: #ffffff; }
.dcl-address__control { box-sizing: border-box; width: 100%; max-width: 100%; padding: 0.5rem 0.65rem; border: 1px solid #64748b; border-radius: 6px; font: inherit; color: #0f172a; background-color: #ffffff; }
.dcl-address__control:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 1px; }
.dcl-address__hint { margin: 0; font-size: 0.85rem; color: #475569; background-color: #ffffff; }
</style>
<script>
(function () {
  var formats = {
    US: ["ZIP code", "5 digits, for example 94110"],
    GB: ["Postcode", "For example SW1A 1AA"],
    CA: ["Postal code", "For example K1A 0B1"],
    DE: ["Postcode", "5 digits, for example 10115"],
    JP: ["Postal code", "7 digits, for example 100-0001"],
    AU: ["Postcode", "4 digits, for example 2000"]
  };
  document.querySelectorAll(".dcl-address").forEach(function (root) {
    if (root.dataset.dclBound) { return; }
    root.dataset.dclBound = "true";
    var country = root.querySelector("select[autocomplete=\"country\"]");
    var label = root.querySelector(".dcl-address__postcode-label");
    var hint = root.querySelector(".dcl-address__hint");
    if (!country) { return; }
    function update() {
      var format = formats[country.value];
      if (!format) { return; }
      if (label) { label.textContent = format[0]; }
      if (hint) { hint.textContent = format[1]; }
    }
    country.addEventListener("change", update);
    update();
  });
})();
</script>

Was this useful? Send feedback.