Use the form component to collect user input to help users perform a certain task.
For example:
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.
A form component contains:
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:
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.
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.
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.
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.
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.
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.
For each invalid field in the form the following two pieces of information should be present in the specific error message:
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):
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
} %}
{% include '@form-item' with {
"label": "Select",
"for": "input_select",
"input_component": "input-select",
"type": "select",
"id": _self.name ~ "-input_select",
"name": _self.name ~ "-input_select",
"field_description": null,
"options": [
{
"label": "Option 1",
"name": _self.name ~ "-select",
"id": _self.name ~ "-select-option-1"
},
{
"label": "Option 2",
"name": _self.name ~ "-select",
"id": _self.name ~ "-select-option-2"
}
]
} %}
{% include '@form-item' with {
"label": "Disabled",
"for": "input_disabled",
"input_component": "input",
"type": "text",
"id": _self.name ~ "-input_disabled",
"name": _self.name ~ "-input_disabled",
"field_description": null,
"disabled": true
} %}
</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' %}
<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>
<div class="form-item ">
<label for="default-input_select">Select
</label>
<div class="form-columns">
<div class="form-item-column">
<select name="default-input_select" id="default-input_select">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
<div class="form-item-column">
</div>
</div>
</div>
<div class="form-item ">
<label for="default-input_disabled">Disabled
</label>
<div class="form-columns">
<div class="form-item-column">
<input type="text" id="default-input_disabled" name="default-input_disabled" class="text" disabled="disabled" />
</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>
{
"primary_button": "Submit",
"secondary_button": "Save for later",
"link": "Cancel"
}
form {
.form-actions {
margin-top: 1.6rem;
}
.privacy-disclaimer {
margin-top: -4rem;
padding-top: 6rem;
&:not(:target) {
display: none;
}
}
}