Pistachio the front end framework and pattern library for graze.com

contents:


    forms

    We've tried to stay close to default form styling to keep our form UI as familiar to users as possible whilst offering various tweaks to make them easier to use and accessible. Forms are for the user to submit information to us and should therefore be functional and useable above all else.

    usage notes

    heads up!

    Form elements are not styled by default. Please pay attention to different form classes and modifiers in order to style a form element.

    The goal of a form is for a user to provide information. Therefore, we should make sure that the the user is guided as clearly as possible towards this goal. Above all else keep it simple!

    1. Use succinct, unambiguous messaging to guide the user via combinations of labels, placeholders, contextual help messages, and validation.
    2. Always ensure you have enough room for your fields. The user should be able to see what they have entered into a field at smaller viewport sizes. You can use bootstrap column classes in your form to have multiple inputs per row but remember useability is the most important concern here and you should not sacrifice that for the sake of fitting everything into a smaller space.
    3. Labels should always be present; do not rely entirely on a placeholder. Once an input is focused the placeholder disappears and the user has lost context for the input.
    4. Labels should nearly always be directly above the input. This is learnt and expected behaviour and greatly improves clarity and engagement, especially on mobile. Some exceptions can be made for a simple single line form but in general labels above inputs work best.
    5. Placeholders should only be used sparingly when they can provide a meaningful suggestion as to the expected structure of the content. They should not simply repeat the label. Acceptable uses of placeholder:
      • "e.g. Sam Smith" for name inputs
      • "example@address.com" for email inputs
      • "3 digits on back of card" for security number
    6. Fieldsets are recommended for accessiblity but are unstyled and have no visible border. In addition, the legend tag is rendered the same as a label. Fieldsets are especially recommended for use with groups of radio buttons and checkboxes.
    7. Dropdowns (or select elements) are to be used sparingly because they have known usability issues. As a rule of thumb, if you have less than 5 options, radio buttons are preferred. For longer lists of options, consider using a regular input instead if possible.
    8. Avoid using the size attribute on individual inputs as you get more inconsistent results. Instead, use the various classes as defined in the example forms below.

    example form

    A .form-group element should be used to wrap input/label group to ensure consistent spacing. The .form-group class can be applied to a div or a fieldset.

    All inputs should use the .form-control class which makes them fill the full width of their parent element. Ideally, most inputs would take up an entire row for clarity, but where inputs make sense to be grouped (as with the first and last name example below), you can use the bootstrap grid classes to achieve this. Inspect the markup below for an example.

    Help text for guidance should be positioned below the input by use of the .form-group__help-block class preferably using a <small> tag.

    We will use this to validate your account.
    your title
    back
    <form method="get" action="javascript:void(0);">
        <div class="form-group">
            <label for="email">email</label>
            <input type="email" class="form-control" id="email" name="email" required placeholder="example@address.com">
            <small class="form-group__help-block">We will use this to validate your account.</small>
        </div>
    
        <div class="row">
            <div class="form-group col-sm-6">
                <label for="first-name">first name</label>
                <input type="text" class="form-control" id="first-name" name="first-name" required>
            </div>
            <div class="form-group col-sm-6">
                <label for="last-name">last name</label>
                <input type="text" class="form-control" id="last-name" name="last-name">
            </div>
        </div>
    
        <fieldset class="form-group">
            <legend>your title</legend>
    
            <div class="form-select" data-p-module="formField">
                <label class="form-select__item">
                    <input name="title-inline" type="radio" value="1" checked>Mr
                </label>
    
                <label class="form-select__item">
                    <input name="title-inline" type="radio" value="2">Mrs
                </label>
    
                <label class="form-select__item">
                    <input name="title-inline" type="radio" value="3">Other
                </label>
            </div>
        </fieldset>
    
        <a class="button" href="#0">back</a>
        <button class="button button--primary button--continue" type="submit">continue</button>
    </form>

    example short form

    Some forms are only small and suitable for a single line. Inputs line up with buttons for that reason. Use the bootstrap grid classes to achieve this. Inspect the example markup below for an example.

    <form method="get" action="javascript:void(0);">
        <div class="row">
            <div class="col-xxs-12">
                <label for="email-short">signup to our newsletter</label>
            </div>
            <div class="form-group col-xs-8">
                <input type="email" class="form-control" id="email-short" name="email" required placeholder="example@address.com">
            </div>
            <div class="col-xs-4">
                <button class="button button--primary button--continue button--block" type="submit">sign up</button>
            </div>
        </div>
    </form>

    example inline form

    Inline forms are only to be used when a form has a single field via the use of the .form-group--inline class. Inputs in an inline form will be sized to the browser's default width. To enforce input size, you can use an additional sizing class such as form-control--sm, form-control--md or form-control--lg.

    <form method="get" action="javascript:void(0);">
        <div class="row">
            <div class="col-xs-12">
                <div class="form-group form-group--inline">
                    <label for="line-quantity">quantity</label>
                    <input type="number" class="form-control form-control--center form-control--sm" id="line-quantity" name="line-quantity" min="1" value="1">
                    <button class="button button--primary" type="submit">add to cart</button>
                </div>
            </div>
        </div>
    </form>

    example responsive inline form

    It's possible to make an inline form full width below certain breakpoints by using the .form-group--block-sm modifier on a .form-group--inline in combination with responsive block buttons

    <form method="get" action="javascript:void(0);">
        <div class="row">
            <div class="col-xs-12">
                <div class="form-group form-group--inline form-group--block-xs">
                    <label for="line-quantity">form-group--block-xs</label>
                    <input type="number" class="form-control form-control--center form-control--sm" id="line-quantity" name="line-quantity" min="1" value="1">
                    <button class="button button--primary button--block-xs" type="submit">add to cart</button>
                </div>
            </div>
        </div>
    </form>
    
    <form method="get" action="javascript:void(0);">
        <div class="row">
            <div class="col-xs-12">
                <div class="form-group form-group--inline form-group--block-sm">
                    <label for="line-quantity">form-group--block-sm</label>
                    <input type="number" class="form-control form-control--center form-control--sm" id="line-quantity" name="line-quantity" min="1" value="1">
                    <button class="button button--primary button--block-sm" type="submit">add to cart</button>
                </div>
            </div>
        </div>
    </form>

    example input/button group

    More intricate UI is possible by combining .form-group--inline with button groups

    <form method="get" action="javascript:void(0);">
        <label for="group-quantity">quantity</label>
        <div class="form-group form-group--inline button-group">
            <button class="button"><span class="fa fa-minus-circle" aria-hidden="true"></span><span class="sr-only">remove</span></button>
            <input type="number" class="form-control form-control--center form-control--sm" id="group-quantity" name="group-quantity" min="1" value="1">
            <button class="button"><span class="fa fa-plus-circle" aria-hidden="true"></span><span class="sr-only">add</span></button>
        </div>
    </form>

    Search form

    An accessible search form which uses the .form-control__button class on a button to position it over the top of the input. Appropriate padding on the input is applied by the .form-group--has-feedback

    <form method="get" action="javascript:void(0);">
        <label for="search">search</label>
        <div class="form-group form-group--has-feedback">
            <input type="text" class="form-control" id="search" name="search" placeholder="search the shop..." required>
            <button class="button form-control__button" type="submit"><i class="fa fa-search"></i><span class="sr-only">search</span></button>
        </div>
    </form>

    inputs, textareas & dropdowns

    These form elements are controlled by a master .form-control class.

    text input

    <div class="form-group">
        <label for="name">your name</label>
        <input id="name" type="text" name="name" placeholder="Nyan Cat" class="form-control">
    </div>

    disabled input

    <div class="form-group">
        <label for="uid">unique identifier</label>
        <input id="uid" type="text" name="uid" value="01802efy0hu" disabled class="form-control">
    </div>

    textarea

    <div class="form-group">
        <label for="comment">comments</label>
        <textarea id="comment" name="comment" rows="6" class="form-control"></textarea>
    </div>

    Quick Note: Use the rows="" attribute to control textarea height. 6 rows should be considered the minimum size for a textarea. Textareas have a corresponding min-height to ensure they do not fall below this minimum size.

    dropdowns

    <div class="form-group">
        <label for="option">select an option</label>
        <select id="option" name="option" class="form-control">
            <option value="1">option 1</option>
            <option value="2">option 2</option>
            <option value="3">option 3</option>
        </select>
    </div>

    Dropdowns (or select elements) are to be used sparingly because they have known usability issues. As a rule of thumb, if you have less than 5 options, radio buttons are preferred. For longer lists of options, consider using a regular input instead if possible.

    checkboxes and radio buttons

    Checkboxes and radio buttons should be used to allow users to select from a list of options with ease. We have a basic style for checkboxes and radio buttons normalized across multiple browsers.

    basic checkboxes

    ingredient checklist
    <fieldset class="form-group">
        <legend>ingredient checklist</legend>
    
        <div class="checkbox">
            <label>
                <input name="checkbox" type="checkbox" value="raisins" checked>raisins
            </label>
        </div>
    
        <div class="checkbox">
            <label>
                <input name="checkbox" type="checkbox" value="chocolate">chocolate
            </label>
        </div>
    
        <div class="checkbox">
            <label>
                <input name="checkbox" type="checkbox" value="blueberries">blueberries
            </label>
        </div>
    </fieldset>

    basic radio buttons

    your favourite snack
    <fieldset class="form-group">
        <legend>your favourite snack</legend>
    
        <div class="radio">
            <label>
                <input name="favourite-snack" type="radio" value="1" checked>chocolate pretzel dipper
            </label>
        </div>
    
        <div class="radio">
            <label>
                <input name="favourite-snack" type="radio" value="2">punchy protein nuts
            </label>
        </div>
    
        <div class="radio">
            <label>
                <input name="favourite-snack" type="radio" value="3">none of the above
            </label>
        </div>
    </fieldset>

    enhanced inline checkboxes and radio buttons

    Relies on the use of pistachio.js and strict markup as shown here for the active states.

    ingredient checklist
    <fieldset class="form-group">
        <legend>ingredient checklist</legend>
    
        <div class="form-select" data-p-module="formField">
            <label class="form-select__item">
                <input name="checkbox-inline" type="checkbox" value="raisins" checked>raisins
            </label>
            <label class="form-select__item">
                <input name="checkbox-inline" type="checkbox" value="chocolate">chocolate
            </label>
            <label class="form-select__item">
                <input name="checkbox-inline" type="checkbox" value="blueberries">blueberries
            </label>
        </div>
    </fieldset>
    your title
    <fieldset class="form-group">
        <legend>your title</legend>
    
        <div class="form-select" data-p-module="formField">
            <label class="form-select__item">
                <input name="title-inline" type="radio" value="1" checked>Mr
            </label>
    
            <label class="form-select__item">
                <input name="title-inline" type="radio" value="2">Mrs
            </label>
    
            <label class="form-select__item">
                <input name="title-inline" type="radio" value="3">Other
            </label>
        </div>
    </fieldset>

    with grid usage

    ingredient checklist
    <fieldset class="form-group">
        <legend>ingredient checklist</legend>
    
        <div class="form-select grid" data-p-module="formField">
            <label class="grid__col-auto form-select__item">
                <input name="checkbox-inline" type="checkbox" value="raisins" checked>raisins
            </label>
            <label class="grid__col-auto form-select__item">
                <input name="checkbox-inline" type="checkbox" value="chocolate">chocolate
            </label>
            <label class="grid__col-auto form-select__item">
                <input name="checkbox-inline" type="checkbox" value="blueberries">blueberries
            </label>
        </div>
    </fieldset>

    small size variant

    ingredient checklist
    <fieldset class="form-group">
        <legend>ingredient checklist</legend>
    
        <div class="form-select" data-p-module="formField">
            <label class="form-select__item form-select__item--sm">
                <input name="checkbox-inline" type="checkbox" value="raisins" checked>raisins
            </label>
            <label class="form-select__item form-select__item--sm">
                <input name="checkbox-inline" type="checkbox" value="chocolate">chocolate
            </label>
            <label class="form-select__item form-select__item--sm">
                <input name="checkbox-inline" type="checkbox" value="blueberries">blueberries
            </label>
        </div>
    </fieldset>

    enhanced block checkboxes and radio buttons

    A block variation of the enhanced checkboxes and radio buttons.

    What snacks do you like?
    <fieldset class="form-group">
        <legend>What snacks do you like?</legend>
    
        <div class="form-select form-select--block" data-p-module="formField">
            <label class="form-select__item">
                <input name="prefs" type="checkbox" value="1" checked>raisins
            </label>
    
            <label class="form-select__item">
                <input name="prefs" type="checkbox" value="2">chocolate
            </label>
    
            <label class="form-select__item">
                <input name="prefs" type="checkbox" value="3">blueberries
            </label>
        </div>
    </fieldset>
    select your delivery address
    <fieldset class="form-group">
        <legend>select your delivery address</legend>
    
        <div class="form-select form-select--block" data-p-module="formField">
            <label class="form-select__item">
                <input name="address-radio" type="radio" value="1" checked>Palm Court, 4 Heron Square, Richmond, Surrey, TW9 1EW
            </label>
    
            <label class="form-select__item">
                <input name="address-radio" type="radio" value="2">Half Way Pond, Georgetown, Grand Cayman, Cayman Islands, British West Indies
            </label>
    
            <label class="form-select__item">
                <input name="address-radio" type="radio" value="3">6762 33 Ave N, St. Petersburg, FL 33710
            </label>
        </div>
    </fieldset>

    validation states

    Validation states provide visual feedback for users during form use to guide their input and highlight erroneous fields.

    valid


    <div class="form-group form-group--has-validation form-group--has-feedback form-group--has-success">
        <label for="password">Success</label>
        <input type="password" class="form-control" id="password" name="password" value="blah blah" required>
        <i class="form-control-feedback fa fa-check-circle"></i>
    </div>

    invalid

    password is required and cannot be empty

    <div class="form-group form-group--has-validation form-group--has-feedback form-group--has-error">
        <label for="password-confirm">Error</label>
        <input type="password" class="form-control" id="password-confirm" name="password-confirm" required>
        <i class="form-control-feedback fa fa-exclamation-triangle"></i>
        <small class="form-group__help-block">password is required and cannot be empty</small>
    </div>

    validation approach

    Ideally, all forms should work without the use of JavaScript by default. The most robust forms use three levels of form validation:

    1) back end validation

    Firstly you should validate in the back end. We don't want anything unexpected hitting our databases so we should always handle validation ourselves in the end point that the form submits to. Any error states should be fed back to the view that is loaded and displayed with the corresponding validation state.

    2) HTML validation

    We also use HTML validation on the front end to aid in usability. We always try to guide the user to successfully completing the form by making it very clear exactly what we expect to be entered and HTML validation helps with this.

    type

    Always use the most appropriate input type. Using the correct input type will aid users on mobile devices especially by providing a context sensitive keyboard. For example using an email type input will give mobile users a keyboard with the @ symbol present and also allow the browser to validate the field itself.

    MDN has a useful list of available HTML5 input types for your reference.


    <form id="input-type" role="form" method="post" action="javascript:void(0);">
        <label for="email" class="control-label">Try submitting this incomplete email</label>
        <div class="form-group">
            <input type="email" class="form-control" id="email" name="email" value="invalid-email@">
        </div>
        <button type="submit" id="submit-button" class="button button--primary button--continue">validate</button>
    </form>

    pattern

    The pattern attribute allows you to define a valid field value via regex. When using a pattern it is not obvious to the user what pattern has been specified so we should always provide supporting information via some combination of labels, placeholders, and contextual help messages.

    HTML5pattern.com has a useful list of common patterns

    valid formats: #ccc, #cccccc

    <form id="input-type" role="form" method="post" action="javascript:void(0);">
        <div class="form-group">
            <label for="pattern-demo" class="control-label">Try submitting an invalid hex colour value:</label>
            <input type="text" class="form-control" id="pattern-demo" name="email" value="#invalid" pattern="^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$">
            <small class="form-group__help-block">valid formats: #ccc, #cccccc</small>
        </div>
        <button type="submit" id="submit-button" class="button button--primary button--continue">validate</button>
    </form>

    Required

    HTML5 capable browsers will flag inputs with this attribute as required and not allow the user to submit the form until the input has a value.


    <form id="input-type" role="form" method="post" action="javascript:void(0);">
        <label for="required" class="control-label">Try submitting this empty field</label>
        <div class="form-group">
            <input type="text" class="form-control" id="required" name="required" required>
        </div>
        <button type="submit" id="submit-button" class="button button--primary button--continue">validate</button>
    </form>

    3) JavaScript validation

    HTML validation is fairly widely supported but only works on form submission. It's therefore a good idea to use some kind of JavaScript validation on your forms to provide some extra polish.

    There are numerous libraries available to handle form validation but regardless of which one you use, or whether you roll your own, They should be used to duplicate your back end validation as much as possible and to provide useful feedback to the user while they are interacting with your form by adding the appropriate validation state to fields as the user interacts with them.

    good practices

    • Display success states as soon as the user has entered valid information to provide positive feedback and hopefully get them moving on to the next field
    • Correct error states as soon as they are fixed, do not wait for user to move focus out of the field before displaying a success state