Photo by vivek kumar  on Unsplash

Forms

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

Accessibility Spotlight

Form elements include a consistent visible focus for keyboard users.

Include accessible field labels implicitly or explicitly—it's up to you.

intuitively mark required fields. When doing so, Natura11y adds required and data-required attributes automatically.

Checkbox and radio groups use semantic fieldset and legend tags.


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
<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
Line #Details
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-17

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).

19-21

The .form-entry__help child selector is not required.

Its id equals its associated aria-describedby attribute.


Select Element

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

Form entry help text
<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 2
Line #Details
5-7

The .form-entry__field__label grandchild selector is required.

The for attribute equals its associated <select> tags id.

9-21

The .form-entry__field__select grandchild selector is required.

It holds the actual <select> tag.

The <select> tag's id equals its associated for attribute.


Text Area

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

Form entry help text
<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="textInputExample"
                id="text-input-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 3
Line #Details
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 4 below.

Upload a Single File
Form entry help text
<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-uploads
                <span class="file-uploadsp">
                    <span class="file-uploadsp__text">Drag and Drop</span>
                </span>

                <input
                    class="file-uploadsut"
                    type="file"
                    name="fileUploadExample"
                    id="file-uploadsple"
                    accept="image/*" />

                <span class="button button--outline button--has-icon file-uploadston">
                    <span class="icon icon-upload"></span>
                    <span class="button__text">Browse for a File</span>
                </span>

            </label>

        </div>

    </div>

    <small class="form-entry__help" id="help-file-uploadsple">
        Form entry help text
    </small>

</div>
Figure 4

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 5 provides the markup required to create a checkbox group.

Form Entry Label
<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 5
Line #Details
9

The .form-entry__option child selector is required.

It holds the options markup for the <fieldset>.

11

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-22

The .file-uploadsut child selector is required.

It includes the type="file" attribute.

18-20

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 6, line 5). Then associate it with aria-labeledby placed on the surrounding <label> tag (Figure 6, lines 13).

Form Entry Label
<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-labeledby="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 6

Checkbox Switch

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

First, change the .form-entry__option__check to .form-entry__option__switch (Figure 7, line 11). Then, above the switch label, add an empty <span> with a .switch__slider class (Figure 7, line 19).

Form Entry Label
<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-labeledby="switch-option-label">
                    <input
                        type="checkbox"
                        name="singleOption"
                        id="switch-option"
                        value="option">
                        <span class="switch__slider"></span>
                        <span class="option__label">
                            Recieve Notifications
                        </span>
                </label>

            </div>

        </div>

    </div>

</div>
Figure 7

Radios

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

Form Entry Label
<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 8
Line #Details
9

The .form-entry__option child selector is required.

It holds each .form-entry__option__radio grandchild selector.

11

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-22

The .file-uploadsut child selector is required.

It includes the type="file" attribute.

18-20

The .option__label child selector is required.

It holds the actual text label for the checkbox.


Required Fields

Natura11y makes it easy to mark required fields. Add data-required="true" to the parent .form-group element of the required field. On the same .form-group 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
<div
    class="form-entry"
    data-required="true"
    data-error-message="Custom error message">

    <div class="form-entry__field">
        ...
    </div>

</div>
Figure 9

The two data attributes mentioned above are shown in Figure 9, lines 3 and 4. When present on the .form group selector, Naturally's Javascript adds the necessary required and data-required attributes to the form entry's child &lt;input&gt; element 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.

Include first and last name
Example: janeDoe@email.com
Example: 999-999-9999
Contact Preferences
<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 10

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 11, line 6).


<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 11

Related CSS Variables

Further customize Forms by redefining any of their related CSS variables as shown below.

:root {
    --form-field-padding-x: var(--spacer-3);
    --form-field-padding-y: var(--button-padding-y);
    --form-field-border-radius: 0.25em;
}