PedanticDesign System · aCo

Forms

Pedantic form rules are exact: the label sits above the field, the help text sits directly under the label, and button labels are Title Case. Nothing is left to convention.

The pattern

  1. Label above the field — visible, bold, and bound with for/id. Required fields carry an asterisk in the label; the label carries it, so help text never talks about “required”.
  2. Help text under the label — one short line that says what to enter and why. It sits between the label and the field, and the field points to it with aria-describedby.
  3. The field — full width up to 34rem, 2px border, visible focus ring. Native types (email, tel, date) so mobile keyboards and autofill work.
  4. Title Case buttons — the submit button names the outcome: “Send Message”, not “Submit”.

Live example — contact form

Your first and last name, as you sign it.

We reply to this address about your message.

Choose the closest match.

A few sentences is plenty.

Preferred Contact Method

How we should reply to you.

<form action="/contact" method="post" novalidate>
  <div class="pds-field">
    <label for="full-name">Full Name <span class="pds-required" aria-hidden="true">*</span></label>
    <p class="pds-help" id="full-name-help">Your first and last name, as you sign it.</p>
    <input class="pds-input" id="full-name" name="full-name" type="text"
      autocomplete="name" required aria-describedby="full-name-help">
  </div>

  <div class="pds-field">
    <label for="email">Email Address <span class="pds-required" aria-hidden="true">*</span></label>
    <p class="pds-help" id="email-help">We reply to this address about your message.</p>
    <input class="pds-input" id="email" name="email" type="email"
      autocomplete="email" required aria-describedby="email-help">
  </div>

  <div class="pds-field">
    <label for="topic">Topic</label>
    <p class="pds-help" id="topic-help">Choose the closest match.</p>
    <select class="pds-select" id="topic" name="topic" aria-describedby="topic-help">
      <option>General Question</option>
      <option>Project Inquiry</option>
      <option>Support</option>
    </select>
  </div>

  <div class="pds-field">
    <label for="message">Message <span class="pds-required" aria-hidden="true">*</span></label>
    <p class="pds-help" id="message-help">A few sentences is plenty.</p>
    <textarea class="pds-textarea" id="message" name="message" required
      aria-describedby="message-help"></textarea>
  </div>

  <fieldset class="pds-fieldset pds-field">
    <legend>Preferred Contact Method</legend>
    <p class="pds-help">How we should reply to you.</p>
    <div class="pds-check">
      <input id="contact-email" name="contact-method" type="radio" value="email" checked>
      <label for="contact-email">Email</label>
    </div>
    <div class="pds-check">
      <input id="contact-phone" name="contact-method" type="radio" value="phone">
      <label for="contact-phone">Phone</label>
    </div>
  </fieldset>

  <div class="pds-check">
    <input id="newsletter" name="newsletter" type="checkbox" value="yes">
    <label for="newsletter">Send me the aCo newsletter</label>
  </div>

  <p><button class="pds-btn pds-btn--primary" type="submit">Send Message</button></p>
</form>

Error state

Errors are text, placed under the field, wired in with aria-describedby, and flagged with aria-invalid="true". The message says what to fix and shows the expected shape — it never just says “invalid”. On submit, focus moves to an error summary at the top of the form that lists each problem and links to its field.

Live example — field with an error

We reply to this address about your message.

Enter a complete email address, like name@example.com.

<div class="pds-field">
  <label for="email-error">Email Address <span class="pds-required" aria-hidden="true">*</span></label>
  <p class="pds-help" id="email-error-help">We reply to this address about your message.</p>
  <input class="pds-input" id="email-error" name="email" type="email" autocomplete="email"
    required aria-invalid="true" aria-describedby="email-error-help email-error-msg"
    value="name@example">
  <p class="pds-error" id="email-error-msg">Enter a complete email address, like name@example.com.</p>
</div>

Rules

Do

Keep help text to one short line: what to enter, and why it is needed.

Use autocomplete attributes for names, emails, addresses, and phone numbers.

Group related choices in a fieldset with a legend that asks the question.

Don’t

Don’t put help text below the field, inside the field as a placeholder, or in a tooltip that touch users can’t open.

Don’t mark fields “optional” one by one — required fields carry the asterisk, and that is the whole system.

Don’t split a single question across two fields when one field with the right type will do.

Accessibility notes