Form

Introduction

Forms are notoriously challenging to style and make accessible. Natura11y provides basic style and behavior for building user friendly, accessible forms.


Form Entries

The form entry parent selector (.form-entry) wraps each entry within a form. Child elements within form entries vary depending on their input types. Such types include text inputs, textareas, selects, checkboxes, and radios.

In Figure 1, the .form-entry selector contains an <input> field with the type="text" attribute.

Form entry help text
HTML
<div class="form-entry">
<label class="form-entry__field">
<span class="form-entry__field__label">
Form Entry Label
</span>
<span class="form-entry__field__input">
<input
type="text"
name="textInputExample"
id="text-input-example"
aria-describedby="help-text-input-example"
/>
</span>
</label>
<small class="form-entry__help" id="help-text-input-example">
Form entry help text
</small>
</div>
Figure 1
Figure 1 notes
LinesDetails
1

The .form-entry parent selector is required.

It contains each entries elements, including help text and feedback messages.

When more than one .form-entry selector is present, spacing between them occurs automatically.

3-18

The .form-entry__field child selector is required.

It holds the field’s label and input elements.

The .form-entry__field__label grandchild selector is required.

It holds the actual label text, and required indicator (when present).

The .form-entry__field__input grandchild selector is required.

It holds the field’s actual input elements.

When using help text, aria-describedby is present on the input tag for assistive technology. It equals the help text’s id attribute (below).

20-22

The .form-entry__help child selector is not required.

Its id equals its associated aria-describedby attribute.


Search Entry

Use the .form-entry--search modifier on the .form-entry element to create an accessible search field. A [data-search-clear] button inside .form-entry__field__input is shown automatically by Natura11y’s JavaScript when the field has a value, and hidden when it is empty.

The label can be visually hidden with .screen-reader-only — a visible label is not required when a leading search icon makes the purpose clear.

HTML
<div class="form-entry form-entry--search">
<label class="form-entry__field">
<span class="form-entry__field__label screen-reader-only">
Search
</span>
<span class="form-entry__field__input">
<span class="icon icon-search opacity-50" aria-hidden="true"></span>
<input
type="search"
id="search-example"
name="searchExample"
/>
<button
type="button"
class="button button--icon-only"
data-search-clear
aria-label="Clear search"
aria-controls="search-example">
<span class="icon icon-clear" aria-hidden="true"></span>
</button>
<button type="submit" class="button theme-primary">
Search
</button>
</span>
</label>
</div>
Figure 2
Figure 2 notes
LinesDetails
1

The .form-entry--search modifier on .form-entry is required.

It suppresses the browser’s native clear button on <input type="search"> so the custom [data-search-clear] button is used instead.

10

The leading icon is optional. When present, the input’s padding-inline-start is removed automatically so the icon and text align cleanly.

17-24

The [data-search-clear] button is required. Natura11y’s JavaScript hides it by default and shows it only when the field has a value.

The aria-controls attribute must equal the id of the associated <input>.

26-28

The submit button is optional. Use type="submit" when the search field is inside a <form> element.

Pre-populated Search (Search Results Page)

When linking to a search results page, pre-apply the has-value class to the .form-entry--search element and set the value attribute on the input to reflect the active query. This ensures the clear button is visible on page load — no JavaScript required for that initial state. It’s a small detail that makes a meaningful difference in usability.

The Search Results Template demonstrates this pattern in a full page layout, including a refined search field, filter sidebar, results list, and pagination.


Select Element

With the example in Figure 3, the same .form-entry container holds a <select> element.

Form entry help text
HTML
<div class="form-entry">
<label class="form-entry__field">
<span class="form-entry__field__label">
Form Entry Label
</span>
<span class="form-entry__field__select">
<select
id="select-example"
name="selectExample"
aria-describedby="help-select-example">
<option value="">Select</option>
<option value="Option One">Option One</option>
<option value="Option Two">Option Two</option>
<option value="Option Three">Option Three</option>
<option value="Option Four">Option Four</option>
<option value="Option Five">Option Five</option>
</select>
</span>
</label>
<small class="form-entry__help" id="help-select-example">
Form entry help text
</small>
</div>
Figure 3
Figure 3 notes
LinesDetails
5-7

The .form-entry__field__label grandchild selector is required.

The label is associated implicitly because the <label> element wraps the <select> element.

9-21

The .form-entry__field__select grandchild selector is required.

It holds the actual <select> tag.

The <select> tag’s id can be used by help text, validation, and custom scripting.


Text Area

Following the previous example’s pattern, Figure 4 shows the .form-entry parent selector with a <textarea> element.

Form entry help text
HTML
<div class="form-entry">
<label class="form-entry__field">
<span class="form-entry__field__label">
Form Entry Label
</span>
<span class="form-entry__field__input">
<textarea
rows="8"
name="textAreaExample"
id="text-area-example"
aria-describedby="help-textarea-example"
></textarea>
</span>
</label>
<small class="form-entry__help" id="help-textarea-example">
Form entry help text
</small>
</div>
Figure 4
Figure 4 notes
LinesDetails
5-7

The .form-entry__field__label grandchild selector is required.

9-16

The .form-entry__field__input grandchild selector is required.

It holds the actual <textarea> tag.


File Upload

Natura11y offers a simple file upload component, as shown in Figure 5 below.

Upload a Single File
Form entry help text
HTML
<div class="form-entry">
<div class="form-entry__field">
<span class="form-entry__field__label">
Upload a Single File
</span>
<div class="form-entry__field__input">
<label class="file-upload">
<span class="file-upload__drop">
<span class="file-upload__drop__text">Drag and Drop</span>
</span>
<input class="file-upload__input" type="file" name="fileUploadExample" id="file-upload-example" accept="image/*">
<span class="button button--outline file-upload__button">
<span class="icon icon-upload" aria-hidden="true"></span>
<span class="text">Browse for a File</span>
</span>
</label>
</div>
</div>
<small class="form-entry__help" id="help-file-upload-example">
Form entry help text
</small>
</div>
Figure 5

Checkboxes

Checkbox (and radio) groups use semantic <fieldset> and <legend> tags. They stack vertically, with a healthy amount of padding. This helps to ensure an accessible target size.

Figure 6 provides the markup required to create a checkbox group.

Form Entry Label
HTML
<div class="form-entry">
<fieldset class="form-entry__field">
<legend class="form-entry__field__label">
Form Entry Label
</legend>
<div class="form-entry__option">
<div class="form-entry__option__check">
<label>
<input
type="checkbox"
name="checkboxGroupExample"
id="check-option-one"
value="optionOne">
<span class="option__label">
Option One
</span>
</label>
</div>
<div class="form-entry__option__check">
<label>
<input
type="checkbox"
name="checkboxGroupExample"
id="check-option-two"
value="optionTwo">
<span class="option__label">
Option Two
</span>
</label>
</div>
<div class="form-entry__option__check">
<label>
<input
type="checkbox"
name="checkboxGroupExample"
id="check-option-three"
value="optionThree">
<span class="option__label">
Option Three
</span>
</label>
</div>
</div>
</fieldset>
</div>
Figure 6
Figure 6 notes
LinesDetails
9

The .form-entry__option child selector is required.

It holds the options markup for the <fieldset>.

10

The .form-entry__option__check grandchild selector is required.

It holds the actual <label> and <input> tags.

The .form-entry__option__check selector is repeatable for multiple options.

17-19

The .option__label child selector is required.

It holds the actual text label for the checkbox.

Single Checkbox

For a single checkbox, do not use a <fieldset> and <legend>. Instead, provide an id on the .form-entry__field__label selector (Figure 7, line 5). Then associate it with aria-labelledby placed on the surrounding <label> tag (Figure 7, line 11).

Form Entry Label
HTML
<div class="form-entry">
<div class="form-entry__field">
<span class="form-entry__field__label" id="single-option-label">
Form Entry Label
</span>
<div class="form-entry__option">
<div class="form-entry__option__check">
<label aria-labelledby="single-option-label">
<input
type="checkbox"
name="singleOption"
id="single-option"
value="option">
<span class="option__label">
Option
</span>
</label>
</div>
</div>
</div>
</div>
Figure 7

Checkbox Switch

Checkboxes can transform into switches with just a couple of of changes to the markup.

First, change .form-entry__option__check to .form-entry__option__switch (Figure 8, line 10). Then, above the option label, add an empty <span> with the .switch__slider class (Figure 8, line 17).

Form Entry Label
HTML
<div class="form-entry">
<div class="form-entry__field">
<span class="form-entry__field__label" id="switch-option-label">
Form Entry Label
</span>
<div class="form-entry__option">
<div class="form-entry__option__switch">
<label aria-labelledby="switch-option-label">
<input
type="checkbox"
name="singleOption"
id="switch-option"
value="option">
<span class="switch__slider"></span>
<span class="option__label">
Receive Notifications
</span>
</label>
</div>
</div>
</div>
</div>
Figure 8

Radios

Like checkboxes, radio groups use semantic <fieldset> and <legend> tags.

Form Entry Label
HTML
<div class="form-entry">
<fieldset class="form-entry__field">
<legend class="form-entry__field__label">
Form Entry Label
</legend>
<div class="form-entry__option">
<div class="form-entry__option__radio">
<label>
<input
type="radio"
name="radioGroupExample"
id="radio-option-one"
value="optionOne">
<span class="option__label">
Option One
</span>
</label>
</div>
<div class="form-entry__option__radio">
<label>
<input
type="radio"
name="radioGroupExample"
id="radio-option-two"
value="optionTwo">
<span class="option__label">
Option Two
</span>
</label>
</div>
<div class="form-entry__option__radio">
<label>
<input
type="radio"
name="radioGroupExample"
id="radio-option-three"
value="optionThree">
<span class="option__label">
Option Three
</span>
</label>
</div>
</div>
</fieldset>
</div>
Figure 9
Figure 9 notes
LinesDetails
9

The .form-entry__option child selector is required.

It holds each .form-entry__option__radio grandchild selector.

10

The .form-entry__option__radio grandchild selector is required.

It holds the actual <label> and <input> tags.

The .form-entry__option__radio selector is repeatable for multiple options.

17-19

The .option__label child selector is required.

It holds the actual text label for the radio.


Required Fields

Natura11y makes it easy to mark required fields. Add data-required="true" to the parent .form-entry element of the required field. On the same .form-entry element, include a custom error message using the data-error-message={x} attribute. Here x equals the message displayed to the user during the validation process.

Form entry help text
HTML
<div
class="form-entry"
data-required="true"
data-error-message="Custom error message">
<div class="form-entry__field">
...
</div>
</div>
Figure 10

The two data attributes mentioned above are shown in Figure 10, lines 3 and 4. When data-required="true" is present on the .form-entry selector, Natura11y’s JavaScript adds the required attribute to the form entry’s child field automatically.

With the previous example, notice the red asterisk placed before the label. This too, is added automatically to required fields (via Natura11y’s CSS file). The asterisk has become the standard identifier for required fields.


Form Validation

Natura11y provides superficial client-side form validation. Include the novalidate attribute on the <form> tag.

Enter your first and last name
Example: janeDoe@email.com
Enter a valid phone number
Contact Preference
HTML
<form data-validate novalidate>
<div
class="form-entry"
data-required="true"
data-error-message="Username is required!">
<label class="form-entry__field form-entry__field--float">
<span class="form-entry__field__label">
Username
</span>
<span class="form-entry__field__input">
<input type="text" id="account-username" name="accountUsername">
</span>
</label>
</div>
</form>
Figure 11

Floating Labels

For short forms (such as a sign in form) floating labels would be fine to use. Add the .form-entry__field--float modifier class to the .form-entry__field selector (see Figure 12, line 5).


HTML
<div
class="form-entry"
data-required="true"
data-error-message="Username is required!">
<label class="form-entry__field form-entry__field--float">
<span class="form-entry__field__label">
Username
</span>
<span class="form-entry__field__input">
<input type="text" id="account-username" name="accountUsername">
</span>
</label>
</div>
Figure 12