Form

Form

When to use this component

Use the form component to collect user input to help users perform a certain task.

For example:

  • Get in touch
  • Send a message or ask a question
  • Give feedback
  • Enter search or filter criteria
  • Login to a website or web application
  • Send a simple request
  • Subscribe to a newsletter

The form component is used for simple or short forms. When the form becomes too complex or too long, or when splitting up the form into multiple, smaller steps makes it easier for user to fill in, the Multistep form component can be used instead.

How it works

A form component contains:

  • Form elements
  • Fieldsets
  • Form actions

Form elements

A form component contains form elements. Form elements are different types of input elements.

The following form elements can be used in the form component:

Fieldsets

In a form component, form elements can be grouped into one or more fieldsets.

A fieldset is used to group related form elements. Fieldsets have a visual indication of where the fieldset starts and where it ends. Fieldsets can also be nested. See the examples.

Form actions

Every form requires at least one form action. Form actions should be placed inside the form component.

The primary form action is the first action of the form and is typically the submit button of the form. A submit button is required for every form.

For more information, see the form actions component.

Layout

The container is divided in two virtual sides that each get 50% of the width of the container.

Form elements are shown at the left-hand side and are aligned to the left of the container. The width of form elements is 50% of the container.

The right-hand side is the space where error field messages (also see above) are shown on validation. Error field messages appear at the right of each form element and are aligned to the top of the form element.

On mobile or when the container is too small, the right-hand side is not used. In this case, there is only one virtual column. Form elements get 100% of the width of the container. Error field messages appear directly below each form element instead of at the right and are aligned to the top of the form element.

Help texts (normal field messages) are always shown inline with the form elements. They are aligned to the left of the container. The width of the messages is the same as the width of the form elements. They are shown between the label of the form element and the form element itself, in other words directly below the label of the form element and directly above the form element itself.

Fieldsets always get a width of 100% of the container.

Validation

The following principles and documentation about form validation is based on the book Inclusive Design Patterns by Heydon Pickering.

When the form is validated and one or more errors are found, there should appear 1 general error message and specific error message per field where an error was found.

The intention is to convey 2 important messages to the user during validation of the form. These should be separate messages.

The first message is that something is broken. This is purely about the fact that errors were found in the form. This message is conveyed by using 1 general error message.

The second message is what needs fixing. This is about wat will make the form valid. This message is conveyed by using a specific error message per field where an error was found.

General error message

When the user clicks the submit button of the form to try to submit the form, we need to check if there are any errors. If there are, we need to surpress form submission temporarily. At this point, all we want to communicate is the presence of errors and that they need attention.

  • The text of the general error message is fixed and should say something like: “Errors were found. Correct them to continue.”.
  • The general error message should appear right after checking if there are errors in the form after the user tried to submit the form.
  • The general error message should be placed as close as possible and just below the submit button.
  • For the general error message, the error status message is used.

Specific error message per field

Now that the user knows something is broken, we need to help the user with what needs fixing (what will make the form valid). We can savely move on to handling the invalid fields.

  • The text of the specific error message is composed out of the two pieces of information that are necessary (also see below), for example: “This is not a valid email address. Please enter an email address that meets the format example@gmail.com.”.
  • The specific error messages should appear at the same moment as the general error message, right after checking if there are errors in the form after the user tried to submit the form.
  • The specific error message should be placed as close as possible and directly at the right next to the fields.
    • In the responsive layout, when there is too little horizontal space, the specific error message move to directy below the fields.
  • For the specific error messages, the error field message is used.

What text to use for the specific error message per field?

For each invalid field in the form the following two pieces of information should be present in the specific error message:

  1. That the field is invalid.
  2. What would make the field valid.

Tips to write the tekst of the specific error message

  • Be clear. Error message are a conversation, with people. Use simple language, not ambiguous. Give the reason why something has gone wrong, say what has happened and why.
  • Be gentile and humble. Don’t blame the user. Remember that “the user is always right”. Imagine a situation where you would talk to the user in person.
  • Be useful. Help the user move forward. Tell the user what to do. Give the solution or a clue. Link the error message to the corresponding next action or step.
  • Avoid technical jargon.
  • Avoid negative words.

Privacy

For GDPR and privacy reasons, every form should include a specific GDPR privacy disclaimer.

The specific GDPR privacy disclaimer should be included as follows (also see the example):

  • Just before the form actions of the form, a line of text is shown “The City of Ghent treats the personal data you fill in with respect for your privacy.” where the word “privacy” is a link.
  • The link on the word “privacy” is an anchor link that, when clicked, shows the specific GDPR privacy disclaimer for that specific form. The specific GDPR privacy disclaimer is shown below the form actions.

The content of the GDPR privacy disclaimer should follow a certain structure. See the example.

{% block content %}
  <form action="#" method="#">

    {% include '@paragraph' with {
      'text': 'All fields are required, unless they are marked as optional.',
      'modifier': 'form-disclaimer'
    } %}

    {% include '@form-item' with {
      "label": "Some input field",
      "id": _self.name ~ "-input1_id",
      "input_component": "input",
      "type": "text",
      "name": _self.name ~ "-input1_name",
      "field_description": null
    } %}
    {% include '@form-item' with {
      "label": "Some more input",
      "input_component": "input",
      "type": "text",
      "id": _self.name ~ "-input2_id",
      "name": _self.name ~ "-input2_name",
      "field_description": null
    } %}
    {% include '@form-item' with {
      "label": "How do you feel about fieldsets?",
      "input_component": "input",
      "type": "text",
      "id": _self.name ~ "-input3_id",
      "name": _self.name ~ "-input3_name",
      "field_description": null
    } %}

    <fieldset>
      <legend>Contact details</legend>
      {% include '@form-item' with {
        "label": "Name",
        "id": _self.name ~ "-name_id",
        "input_component": "input",
        "type": "text",
        "name": _self.name ~ "-name_name",
        "field_description": null
      } %}
      {% include '@form-item' with {
        "label": "First name",
        "input_component": "input",
        "type": "text",
        "id": _self.name ~ "-firstname_id",
        "name": _self.name ~ "-firstname_name",
        "field_description": null
      } %}
      {% include '@form-item' with {
        "label": "E-mail address",
        "for": "email_id",
        "input_component": "input",
        "type": "email",
        "id": _self.name ~ "-email_id",
        "name": _self.name ~ "-email_name",
        "field_description": null
      } %}
      {% include '@form-item' with {
        "label": "Phone number",
        "for": "tel_id",
        "input_component": "input",
        "type": "tel",
        "id": _self.name ~ "-tel_id",
        "name": _self.name ~ "-tel_name",
        "field_description": null
      } %}
    </fieldset>

    {% include '@radios' with {
      "input_component": "input",
      "type": "radio",
      "label": "Date",
      "modifier": "error",
      "field_description": "You can add an optional field description here.",
      "field_message": "Lorem ipsum dolor sit amet.",
      "options": [
        {
          "label": "Wednesday 6 june 2018",
          "name": _self.name ~ "-radiogroup",
          "id": _self.name ~ "-radio-1"
        },
        {
          "label": "Thursday 7 june 2018",
          "name": _self.name ~ "-radiogroup",
          "id": _self.name ~ "-radio-2"
        },
        {
          "label": "Friday 8 june 2018",
          "name": _self.name ~ "-radiogroup",
          "id": _self.name ~ "-radio-3"
        }
      ]
    } %}

    {% include '@address' with {
      id: _self.name
    } %}

    <fieldset>
      <legend>Another fieldset</legend>
      <fieldset>
        <legend>Nested fieldset</legend>
        {% include '@form-item' with {
          "label": "Label textfield",
          "for": "textfield_id--2",
          "input_component": "input",
          "type": "text",
          "id": _self.name ~ "-textfield_id--2",
          "name": _self.name ~ "-textfield_name--2",
          "field_description": null
        } %}
        {% include '@form-item' with {
          "label": "Label textfield",
          "for": "textfield_id--3",
          "input_component": "input",
          "type": "text",
          "id": _self.name ~ "-textfield_id--3",
          "name": _self.name ~ "-textfield_name--3",
          "field_description": null
        } %}
      </fieldset>
      <fieldset>
        <legend>And another one</legend>
        {% include '@form-item' with {
          "label": "Label textfield",
          "for": "textfield_id",
          "label_optional": "Optional",
          "input_component": "input",
          "type": "text",
          "field_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec laoreet, urna sit amet convallis rhoncus, felis ex pellentesque neque, nec ultrices dui enim ut diam. Nam pellentesque velit pharetra, accumsan ante at, gravida turpis. Cras venenatis velit ut ipsum molestie pretium.",
          "field_error": "",
          "description": "Description textfield.",
          "id": _self.name ~ "-textfield_id",
          "name": _self.name ~ "-textfield_name",
          "modifier": "error",
          "field_message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec laoreet, urna sit amet convallis rhoncus, felis ex pellentesque neque, nec ultrices dui enim ut diam."
        } %}
        {% include '@form-item' with {
          "label": "Label textfield",
          "for": "textfield_id--1",
          "input_component": "input",
          "type": "text",
          "id": _self.name ~ "-textfield_id--1",
          "name": _self.name ~ "-textfield_name--1",
          "field_description": null
        } %}
      </fieldset>
    </fieldset>

    {% include '@checkboxes' with {
      "input_component": "input",
      "type": "checkbox",
      "label": "Newsletter",
      "id": _self.name ~ "-newsletter_id",
      "options": [
        {
          "label": "Subscribe to the newsletter",
          "name": _self.name ~ "-checkboxgroup",
          "id": _self.name ~ "-checkbox-1"
        }
      ]
    } %}

    {% render "@file-upload" with {
      id: _self.name ~ "-file"
    } %}

    {% set target %}
      <strong>
        The City of Ghent treats the personal data you fill in with respect for
        your
        {% include '@link' with {
          'link': '#privacy',
          'text': 'privacy'
        } %}.
      </strong>
    {% endset %}
    {% include '@paragraph' with {
      'text': target
    } %}

    {% include '@form-actions' with {
      primary_button: 'Submit',
      secondary_button: 'Save for later',
      link: 'Cancel'
    } %}

    <div class="privacy-disclaimer" id="privacy">
      {% include '@heading' with {
        'type': 'h2',
        'heading_text': 'With respect for your privacy'
      } %}
      {% set GDPR %}
        <strong>
          The City of Ghent treats the personal data you fill in with respect
          for your privacy. For this purpose, we follow the
          {% include '@link' with {
            'link': 'https://#',
            'text': 'General Data Protection Ordinance'
          } %}.
        </strong>
      {% endset %}
      {% include '@paragraph' with {
        'text': GDPR
      } %}
      {% include '@heading' with {
        'type': 'h3',
        'heading_text': 'For what, with whom and for how long?'
      } %}
      {% include '@paragraph' with {
        'text': 'This is a short text describing what the personal data entered in the form above are for, with whom they are shared and for how long they are kept. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent sodales nisi in mi fringilla malesuada. Donec sapien nibh, facilisis id enim a, malesuada lacinia magna. Aenean at sagittis ipsum, nec condimentum quam. Etiam interdum convallis justo, eu sodales erat.'
      } %}
      {% include '@heading' with {
        'type': 'h3',
        'heading_text': 'Your rights'
      } %}
      {% include '@paragraph' with {
        'text': 'This is a short text describing the rights. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent sodales nisi in mi fringilla malesuada. Donec sapien nibh, facilisis id enim a, malesuada lacinia magna. Aenean at sagittis ipsum, nec condimentum quam.'
      } %}
      {% set reportText %}
        Do you suspect someone is using your personal information illegally? Report it to us at
        {% include '@link' with {
          'link': 'mailto:#',
          'text': 'privacy@stad.gent'
        } %}.
        You also have the right to complain to the
        {% include '@link' with {
          'link': 'https://#',
          'text': 'Flemish Supervisory Commission for the processing of personal data'
        } %}.
      {% endset %}
      {% include '@paragraph' with {
        'text': reportText
      } %}
      {% set link %}
        {% include '@link' with {
          'link': '#',
          'text': 'Learn more about your rights and privacy'
        } %}
      {% endset %}
      {% include '@paragraph' with {
        'text': link
      } %}
    </div>
  </form>
{% endblock %}
<form action="#" method="#">

      <p class="form-disclaimer">All fields are required, unless they are marked as optional.</p>

      <div class="form-item ">
          <label for="default-input1_id">Some input field
          </label>
          <div class="form-columns">
              <div class="form-item-column">

                  <input type="text" id="default-input1_id" name="default-input1_name" class="text" />
              </div>
              <div class="form-item-column">
              </div>
          </div>
      </div>
      <div class="form-item ">
          <label for="default-input2_id">Some more input
          </label>
          <div class="form-columns">
              <div class="form-item-column">

                  <input type="text" id="default-input2_id" name="default-input2_name" class="text" />
              </div>
              <div class="form-item-column">
              </div>
          </div>
      </div>
      <div class="form-item ">
          <label for="default-input3_id">How do you feel about fieldsets?
          </label>
          <div class="form-columns">
              <div class="form-item-column">

                  <input type="text" id="default-input3_id" name="default-input3_name" class="text" />
              </div>
              <div class="form-item-column">
              </div>
          </div>
      </div>

      <fieldset>
          <legend>Contact details</legend>
          <div class="form-item ">
              <label for="default-name_id">Name
              </label>
              <div class="form-columns">
                  <div class="form-item-column">

                      <input type="text" id="default-name_id" name="default-name_name" class="text" />
                  </div>
                  <div class="form-item-column">
                  </div>
              </div>
          </div>
          <div class="form-item ">
              <label for="default-firstname_id">First name
              </label>
              <div class="form-columns">
                  <div class="form-item-column">

                      <input type="text" id="default-firstname_id" name="default-firstname_name" class="text" />
                  </div>
                  <div class="form-item-column">
                  </div>
              </div>
          </div>
          <div class="form-item ">
              <label for="default-email_id">E-mail address
              </label>
              <div class="form-columns">
                  <div class="form-item-column">

                      <input type="email" id="default-email_id" name="default-email_name" class="email" />
                  </div>
                  <div class="form-item-column">
                  </div>
              </div>
          </div>
          <div class="form-item ">
              <label for="default-tel_id">Phone number
              </label>
              <div class="form-columns">
                  <div class="form-item-column">

                      <input type="tel" id="default-tel_id" name="default-tel_name" class="tel" />
                  </div>
                  <div class="form-item-column">
                  </div>
              </div>
          </div>
      </fieldset>

      <fieldset class="form-item error">
          <legend>
              Date
          </legend>
          <div class="form-item">
              <div class="field-message " id="-description">
                  You can add an optional field description here.
                  <div class="accolade "></div>
              </div>
              <div class="form-columns">
                  <div class="form-item-column">
                      <div class="radio">

                          <input type="radio" id="input--error-default-radio-1" name="default-radiogroup" class="radio error" />
                          <label for="input--error-default-radio-1">Wednesday 6 june 2018</label>
                      </div>
                      <div class="radio">

                          <input type="radio" id="input--error-default-radio-2" name="default-radiogroup" class="radio error" />
                          <label for="input--error-default-radio-2">Thursday 7 june 2018</label>
                      </div>
                      <div class="radio">

                          <input type="radio" id="input--error-default-radio-3" name="default-radiogroup" class="radio error" />
                          <label for="input--error-default-radio-3">Friday 8 june 2018</label>
                      </div>
                  </div>
                  <div class="form-item-column">
                      <div class="field-message error" role="alert" id="-validation">
                          Lorem ipsum dolor sit amet.
                          <div class="accolade "></div>
                      </div>

                  </div>
              </div>
          </div>
      </fieldset>

      <fieldset>
          <legend>Address details</legend>
          <div class="form-item ">
              <label for="default-street_id">Street
              </label>
              <div class="form-columns">
                  <div class="form-item-column">

                      <input type="text" id="default-street_id" name="default-street_name" class="text" />
                  </div>
                  <div class="form-item-column">
                  </div>
              </div>
          </div>
          <div class='form-row'>
              <div class="form-item  error stacked">
                  <label for="number_id">Number
                  </label>
                  <div class="form-columns">
                      <div class="form-item-column">

                          <input type="text" id="number_id" aria-describedby="number_id-message" name="number_name" class="text error" aria-invalid="true" />
                      </div>
                      <div class="form-item-column">
                          <div class="field-message error" role="alert" id="number_id-message">
                              Lorem ipsum dolor sit amet.
                              <div class="accolade "></div>
                          </div>
                      </div>
                  </div>
              </div>
              <div class="form-item  stacked">
                  <label for="mailbox_id">Mailbox
                      <span class="label-optional">
                          (Optional)
                      </span>
                  </label>
                  <div class="form-columns">
                      <div class="form-item-column">

                          <input type="text" id="mailbox_id" name="mailbox_name" class="text" />
                      </div>
                      <div class="form-item-column">
                      </div>
                  </div>
              </div>
          </div>
          <div class='form-row'>
              <div class="form-item  stacked">
                  <label for="postal-code_id">Postal code
                  </label>
                  <div class="form-columns">
                      <div class="form-item-column">

                          <input type="text" id="postal-code_id" name="postal-code_name" class="text" />
                      </div>
                      <div class="form-item-column">
                      </div>
                  </div>
              </div>
              <div class="form-item  stacked">
                  <label for="municipality_id">Municipality
                  </label>
                  <div class="form-columns">
                      <div class="form-item-column">

                          <input type="text" id="municipality_id" name="municipality_name" class="text" />
                      </div>
                      <div class="form-item-column">
                      </div>
                  </div>
              </div>
          </div>
          <div class="form-item ">
              <label for="select">Country
              </label>
              <div class="form-columns">
                  <div class="form-item-column">
                      <select name="select" id="select">

                          <option>Option 1</option>

                          <option>Option 2</option>

                      </select>
                  </div>
                  <div class="form-item-column">
                  </div>
              </div>
          </div>
      </fieldset>

      <fieldset>
          <legend>Another fieldset</legend>
          <fieldset>
              <legend>Nested fieldset</legend>
              <div class="form-item ">
                  <label for="default-textfield_id--2">Label textfield
                  </label>
                  <div class="form-columns">
                      <div class="form-item-column">

                          <input type="text" id="default-textfield_id--2" name="default-textfield_name--2" class="text" />
                      </div>
                      <div class="form-item-column">
                      </div>
                  </div>
              </div>
              <div class="form-item ">
                  <label for="default-textfield_id--3">Label textfield
                  </label>
                  <div class="form-columns">
                      <div class="form-item-column">

                          <input type="text" id="default-textfield_id--3" name="default-textfield_name--3" class="text" />
                      </div>
                      <div class="form-item-column">
                      </div>
                  </div>
              </div>
          </fieldset>
          <fieldset>
              <legend>And another one</legend>
              <div class="form-item  error">
                  <label for="default-textfield_id">Label textfield
                      <span class="label-optional">
                          (Optional)
                      </span>
                  </label>
                  <div class="field-message " id="default-textfield_id-description">
                      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec laoreet, urna sit amet convallis rhoncus, felis ex pellentesque neque, nec ultrices dui enim ut diam. Nam pellentesque velit pharetra, accumsan ante at, gravida turpis. Cras venenatis velit ut ipsum molestie pretium.
                      <div class="accolade "></div>
                  </div>
                  <div class="form-columns">
                      <div class="form-item-column">

                          <input type="text" id="default-textfield_id" aria-describedby="default-textfield_id-message" name="default-textfield_name" class="text error" aria-invalid="true" />
                      </div>
                      <div class="form-item-column">
                          <div class="field-message error" role="alert" id="default-textfield_id-message">
                              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec laoreet, urna sit amet convallis rhoncus, felis ex pellentesque neque, nec ultrices dui enim ut diam.
                              <div class="accolade "></div>
                          </div>
                      </div>
                  </div>
              </div>
              <div class="form-item ">
                  <label for="default-textfield_id--1">Label textfield
                  </label>
                  <div class="form-columns">
                      <div class="form-item-column">

                          <input type="text" id="default-textfield_id--1" name="default-textfield_name--1" class="text" />
                      </div>
                      <div class="form-item-column">
                      </div>
                  </div>
              </div>
          </fieldset>
      </fieldset>

      <fieldset class="form-item ">
          <legend>
              Newsletter
          </legend>
          <div class="form-item">
              <div class="form-columns">
                  <div class="form-item-column">
                      <div class="checkbox">

                          <input type="checkbox" id="input-default-newsletter_id--default-checkbox-1" name="default-checkboxgroup" class="checkbox" />
                          <label for="input-default-newsletter_id--default-checkbox-1">Subscribe to the newsletter</label>
                      </div>
                  </div>
                  <div class="form-item-column">

                  </div>
              </div>
          </div>
      </fieldset>

      <section class="file-upload">
          <div class="form-item">
              <label for="default-file--">Attachment
              </label>
              <div class="form-columns">
                  <div class="form-item-column">
                      <div class="file" data-file="No file chosen.">

                          <input type="file" id="default-file--" aria-describedby="default-file---description" name="--default-file--" />

                          <span id="default-file---description" class="file__button">Select your files here to upload</span>
                      </div>

                      <span class="help-text">Allowed file formats: jpg, jpeg, png and gif. Maximum file size: 2 MB. Images must be larger than 744x465 pixels.</span>
                  </div>
                  <div class="form-item-column"></div>
              </div>
          </div>
      </section>

      <p class=""> <strong>
              The City of Ghent treats the personal data you fill in with respect for
              your
              <a href='#privacy'>
                  privacy </a>
              .
          </strong>
      </p>

      <div class="form-actions">
          <input type="submit" value="Submit" class="" />

          <button type="button" class="button button-secondary  ">
              Save for later
          </button>

          <a href='#' class="standalone-link back">
              Cancel </a>

      </div>

      <div class="privacy-disclaimer" id="privacy">
          <h2>With respect for your privacy</h2>
          <p class=""> <strong>
                  The City of Ghent treats the personal data you fill in with respect
                  for your privacy. For this purpose, we follow the
                  <a href='https://#'>
                      General Data Protection Ordinance </a>
                  .
              </strong>
          </p>
          <h3>For what, with whom and for how long?</h3>
          <p class="">This is a short text describing what the personal data entered in the form above are for, with whom they are shared and for how long they are kept. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent sodales nisi in mi fringilla malesuada. Donec sapien nibh, facilisis id enim a, malesuada lacinia magna. Aenean at sagittis ipsum, nec condimentum quam. Etiam interdum convallis justo, eu sodales erat.</p>
          <h3>Your rights</h3>
          <p class="">This is a short text describing the rights. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent sodales nisi in mi fringilla malesuada. Donec sapien nibh, facilisis id enim a, malesuada lacinia magna. Aenean at sagittis ipsum, nec condimentum quam.</p>
          <p class=""> Do you suspect someone is using your personal information illegally? Report it to us at
              <a href='mailto:#'>
                  privacy@stad.gent </a>
              .
              You also have the right to complain to the
              <a href='https://#'>
                  Flemish Supervisory Commission for the processing of personal data </a>
              .
          </p>
          <p class=""> <a href='#'>
                  Learn more about your rights and privacy </a>
          </p>
      </div>
  </form>
/* No context defined for this component. */
  • Content:
    form {
      .form-actions {
        margin-top: 1.6rem;
      }
    
      .privacy-disclaimer {
        margin-top: -4rem;
        padding-top: 6rem;
    
        &:not(:target) {
          display: none;
        }
      }
    }
    
  • URL: /components/raw/form/_form.scss
  • Filesystem Path: components/41-organisms/form/_form.scss
  • Size: 179 Bytes