Forms Using Angular

Clarity has created a set of directives to help manage forms with minimal effort by developers. The structure is more condensed and easier to implement, so it is the recommended approach to use the following if you are using Angular. More form controls are being added regularly.

Basic form

Then declare a form start by adding the clrForm directive to the form element. This will wire up some internals to manage the form itself.

<form clrForm>
    ... form controls
</form>

Layout options

If you wish to have a different layout, then you can use the clrLayout directive to set the desired layout. The appropriate grid classes will be applied to enable automatic layout switching for mobile viewports.

<form clrForm clrLayout="horizontal">
    ... form controls
</form>

Basic structure

When you start to fill in your form controls, each will should be wrapped in a container like you see here in this text input example.

<form clrForm>
    <clr-input-container>
        <label>Field 1 label</label>
        <input clrInput type="text" [(ngModel)]="model" name="example" />
    </clr-input-container>
    <clr-input-container>
        <label>Field 2 label</label>
        <input clrInput type="text" [(ngModel)]="model" name="example" />
    </clr-input-container>
</form>

Helper and validation messages

These Angular components also support built in helper text and validation with error messages.

<form clrForm>
    <clr-input-container>
        <label>Field 1 label</label>
        <input clrInput type="text" [(ngModel)]="model" name="example" required />
        <clr-control-helper>Helper text that shows while it is pristine and valid</clr-control-helper>
        <clr-control-error>Error message that appears after focus is lost and control is invalid</clr-control-error>
    </clr-input-container>
</form>

All fields should be assumed to be required. Clarity does not support a required input treatment for labels (which often comes in the form of an * by the label). The recommendation is to focus your forms to include only required fields, and if a field is optional then you can describe it as such in the label like (Optional).

<form clrForm>
    <clr-input-container>
        <label>First name</label>
        <input clrInput type="text" [(ngModel)]="firstName" name="firstName" required />
        <clr-control-error>We need your first name for legal compliance</clr-control-error>
    </clr-input-container>
    <clr-input-container>
        <label>Middle name (Optional)</label>
        <input clrInput type="text" [(ngModel)]="midleName" name="midleName" />
    </clr-input-container>
    <clr-input-container>
        <label>Last name</label>
        <input clrInput type="text" [(ngModel)]="lastName" name="lastName" required />
        <clr-control-error>We need your last name for legal compliance</clr-control-error>
    </clr-input-container>
</form>
This pattern is more accesible and clear by writing the word explicitly for users (and screenreaders) to read, with research to back this up compared with the use of a red required asterisk (*). See this article from fusionbox provides some evidence for this rationale.

Multiple error messages

If you want to support multiple error messages, you can do this by defining an error message for each scenario using clrIfError. It is recommended that you create an error message for each validatior you specify. Use the validator name provided in the binding for *clrIfError="'errorName'", which might be your custom validator or a built in Angular one.

Reset and force validation

All Clarity form controls support resetting the validation state simply by calling the reset() method on the FormControl or FormGroup.

import {Component} from "@angular/core";
import {FormGroup, FormControl, Validators} from "@angular/forms";

@Component({
    template: `
    <form clrForm [formGroup]="exampleForm">
        <input clrInput formControlName="sample" />
        <button class="btn btn-primary" type="submit" (click)="submit()">Submit</button>
        <button class="btn" type="button" (click)="resetForm()">Reset</button>
    </form>
    `
})
export class ReactiveFormsDemo {
    exampleForm = new FormGroup({
        sample: new FormControl('', Validators.required),
    });

    resetForm() {
        this.exampleForm.reset();
    }

    submit() {
        // ...
    }
}

Normally, validation errors only appear after the control has been focused on by the user. In cases where you want to force validation errors to show (such as when the user tried to submit a form), you simply need to mark every control as dirty with Angular. To make this easier, we have provided an API ClrForm.markAsDirty() that will force all form controls inside of a form to be dirty, which will display the validation errors.

import {ViewChild, Component} from "@angular/core";
import {FormGroup, FormControl, Validators} from "@angular/forms";

@Component({
    template: `
    <form clrForm [formGroup]="exampleForm">
        <input clrInput formControlName="sample" />
        <button class="btn btn-primary" type="submit" (click)="submit()">Submit</button>
    </form>
    `
})
export class ReactiveFormsDemo {
    @ViewChild(ClrForm) clrForm;

    exampleForm = new FormGroup({
        sample: new FormControl('', Validators.required),
    });

    submit() {
        if (this.exampleForm.invalid) {
            this.clrForm.markAsDirty();
        } else {
            // ...
        }
    }
}

Overriding column widths

For horizontal layouts, you can override the default widths for labels (2 columns) and controls (10 columns). Even on horizontal layouts, the default behavior should still use a vertical layout for narrow mobile sizes. That means you should always include the class clr-col-12 on both the input and label. Ensure your override columns add up to 12 to use the full space.

<form clrForm>
    <clr-input-container>
        <label class="clr-col-12 clr-col-md-4">Field 1 label</label>
        <input class="clr-col-12 clr-col-md-8" clrInput type="text" [(ngModel)]="model" name="example" required />
        <clr-control-helper>Helper text that shows while it is pristine and valid</clr-control-helper>
        <clr-control-error>Error message that appears after focus is lost and control is invalid</clr-control-error>
    </clr-input-container>
</form>

Reactive Forms

Forms also work with reactive forms with the same support for validations.

import {Component} from "@angular/core";
import {FormGroup, FormControl, Validators} from "@angular/forms";

@Component({
    //...
})
export class ReactiveFormsDemo {
    exampleForm = new FormGroup({
        sample: new FormControl('', Validators.required),
    });
}
<form clrForm [formGroup]="exampleForm">
    <clr-input-container>
        <label>Field 1 label</label>
        <input clrInput type="text" formControlName="sample" />
        <clr-control-helper>Helper text that shows while it is pristine and valid</clr-control-helper>
        <clr-control-error>Error message that appears after focus is lost and control is invalid</clr-control-error>
    </clr-input-container>
</form>

Forms with CSS

If you want to use just the HTML/CSS for building your own form controls, this section describes how to use them. If you are interested in how to use it with Angular, please jump to the Angular section.

Basic form

A Clarity form first must include the .clr-form class, typically on the form element itself.

<form class="clr-form">
    ... form controls inside
</form>

Layout options

Forms support three layout options, horizontal, vertical (used if no classes are provided), and compact. You can change the layout by simply applying an additional class to the form.

<form class="clr-form"></form> // Vertical, class not necessary
<form class="clr-form clr-form-horizontal"></form> // Horizontal
<form class="clr-form clr-form-compact"></form> // Compact

Basic structure

Each control that you add inside has a similar structure, and they are each covered in their own documentation. However, the general structure is like this for a normal text input.

<form class="clr-form">
    <div class="clr-form-control">
        <label for="example" class="clr-control-label">Label</label>
        <div class="clr-control-container">
            <div class="clr-input-wrapper">
                <input type="text" id="example" placeholder="Example Input" class="clr-input">
                <clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
            </div>
            <span class="clr-subtext">Helper Text</span>
        </div>
    </div>
</form>

Layouts with grid

If you choose to use a layout other than vertical, you should also provide the corresponding grid classes to allow the form layout to dynamically switch to vertical for narrow width devices. Excluding this may cause experience issues with mobile or narrow widths.

<form class="clr-form clr-form-compact">
    <div class="clr-form-control clr-row">
        <label for="example" class="clr-control-label clr-col-12 clr-col-md-2">Label</label>
        <div class="clr-control-container clr-col-12 clr-col-md-10">
            <div class="clr-input-wrapper">
                <input type="text" id="example" placeholder="Example Input" class="clr-input">
                <clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
            </div>
            <span class="clr-subtext">Helper Text</span>
        </div>
    </div>
</form>

Error state

For error states, you simply need to add the clr-error class to the clr-control-container element. This will automatically display the error icon, turn the input outline to red, and change the color of the subtext to red. It is recommend that you change the content of the subtext for error messages, but that is up to the application to manage.

<form class="clr-form clr-form-compact">
    <div class="clr-form-control clr-row">
        <label for="example" class="clr-control-label clr-col-12 clr-col-md-2">Label</label>
        <div class="clr-control-container clr-col-12 clr-col-md-10">
            <div class="clr-input-wrapper">
                <input type="text" id="example" placeholder="Example Input" class="clr-input">
                <clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
            </div>
            <span class="clr-subtext">Helper Text</span>
        </div>
    </div>
</form>

View documentation for each form control to get specifics on their HTML structure.

Design Guidelines

Forms are comprised of text and input components such as checkboxes, dropdowns, input fields, radio buttons, and toggle switches. The easier users can complete forms, the better your response rates will typically be. Forms should be clear, concise, and relevant to its context.

Form Layouts

Clarity offers three types of forms: horizontal (our recommended default), vertical and compact.

Helper Text
Horizontal - Default

Horizontal formats are good for the quick scanning of labels, and can be used in cases of limited vertical space. The space between label and input however can slow users down.

Helper Text
Vertical

This option is better for scanning, mobile experiences, accessibility, and localization. While it offers better completion rates, it is less ideal for longer forms.

Helper Text
Compact

For cases with highly limited space, we provide a compact form layout.

Form Architecture

Columns

Forms should help users complete their goal as quickly as possible. Column structure can help make it easier for users to complete your form.

use a single column for forms layout
Do

Put forms in one column. Multiple columns disrupt users’ vertical rhythm and completion.

don't use multiple columns for forms
Don't

Create multiple columns of input fields per page, except for small, related inputs like name “first” and “last.”

Input Grouping

Grouping will make scanning easier. A form with more than 6 inputs will likely have inputs that can be grouped together, like “address”: street, city, zip, county, country, etc.

For the grouping of labels and their input fields, we recommend grouping labels closely with their respective input fields.

align inputs and labels
Do
don't have inputs misaligned
Do
don't right align inputs
Don't

Form Length

Forms should only be as long as absolutely necessary. We recommend reviewing each form input and asking whether you could obtain the information in another way, or at a later, more convenient time.

Form Elements

Radios, Checkboxes, and Select Box

For inputs with 3 or more options, you may use radios, checkboxes or select boxes. We recommend placing your options vertically in one column to make it easier to scan.

Radios and checkboxes are used when it is helpful to compare options within the context of the form, as all selections will be visible at all times. Select boxes typically have more than 7 options that do not need to be compared with each other.

Validation and Error Messaging

You also want to let users know when something is wrong with the information provided.

Error Message Styling

When showing error inputs, highlight the input field with red in some way, but also pair the red with another visual indicator, like an icon. This will help with accessibility.

put icons beside inputs
Do

Place icons outside the input field. This will also help avoid browser and third-party app collision conflicts.

don't put icons inside of inputs
Don't

Put icons inside the input field. If inside, account for changing form value space (increase characters).

Error Message Content

Humanize the error messaging as much as possible. The content should provide clear guidance on how to fix the error. Avoid unrecognizable system error messages like "code 500 error".

You may also consider using more than one error message when helpful. For example, if an email address input field has an error, consider either showing “please enter an email address” for a blank field, or “email address needs an ’@’ symbol followed by a domain” for an invalid symbol.

Error Message Location

We recommend displaying error messages within the same area where the error occurs.

Error Message Behavior

For most cases, validate when the user leaves the field (onblur). Invalidating fields while users are still typing can be frustrating.

There are some cases where real-time validation can be helpful, such as inputs with sensitive field value lengths (like tweets), or when users return to an error field and successfully edit the error field, or for password fields with visible password strength criteria.

The UX design guidelines documentation is currently a work in progress, and will be updated when they are ready.