Spruce CSS comes with overall form support for styling the different native elements. Also, it has some form layout helpers.

We differentiate the following groups for styling:

  • the form-control class is used for input (not radio, checkbox, range, or file), textarea, select elements,
  • the form-check is used for radio and checkbox,
  • the form-switch is used for switch-styled checkboxes,
  • the form-range is used for range typed input,
  • the form-file is used for file typed input.

Input

Use the form-control class on the input (that doesn’t have the type of radio, checkbox, or range) and form-label on the label elements to apply the formatting.

Wrap the label and the input into form-group elements to control the two’s margin.

You can set two additional sizes with form-control--sm and form-control--lg modifier class.

Form-control related variables.

<div class="form-group">
    <label class="form-label" for="your-name">Name</label>
    <input class="form-control" id="your-name" name="your-name" type="text"/>
</div>
<div class="form-group">
    <label class="form-label" for="your-email">Email</label>
    <input class="form-control" id="your-email" name="your-email" type="email"/>
</div>
<div class="form-group">
    <label class="form-label" for="your-birthday">Birthday</label>
    <input class="form-control" id="your-birthday" name="your-birthday" type="date"/>
</div>
<div class="form-group">
    <label class="form-label" for="your-color">Your favorite color</label>
    <input class="form-control" id="your-color" name="your-color" type="color" value="#6524d6"/>
</div>

File

Use the form-file class on an input[type="file"] to natively style the file input.

  • You can size the input with form-file--sm and form-file--lg.
  • The styling is the same as the .btn.
  • The focus state differs from the button because we can’t set box-shadow (it will be cut down).

Form-file related variables.

File Inputs
<fieldset>
    <legend>File Inputs</legend>
    <div class="form-group">
        <label class="form-label" for="avatar-lg">Avatar (Large)</label>
        <input class="form-file form-file--lg" type="file" id="avatar-lg" accept="image/png, image/jpeg"/>
    </div>
    <div class="form-group">
        <label class="form-label" for="avatar">Avatar</label>
        <input class="form-file" type="file" id="avatar" accept="image/png, image/jpeg"/>
    </div>
    <div class="form-group">
        <label class="form-label" for="files">Files</label>
        <input class="form-file form-file--sm" type="file" id="files" multiple="multiple" accept="image/png, image/jpeg"/>
    </div>
</fieldset>

Range

Use the form-range class on an input[type="range"] to natively style the range input.

  • You can size the input with form-range--sm and form-range--lg.

Form-range related variables.

Range
<fieldset>
    <legend>Range</legend>
    <div class="form-group">
        <label class="form-label" for="rating">Your Rating</label>
        <input class="form-range form-range--lg" type="range" id="rating" min="-10" max="10"/>
    </div>
</fieldset>

Textarea

Use the form-control class on any textarea element to style it. To set the global height of it use the config('textarea-block-size', $form-control) variable.

<div class="form-group">
    <label class="form-label" for="your-message">Your Message</label>
    <textarea class="form-control" id="your-message" name="your-message" placeholder="Write your message..." rows="4"></textarea>
</div>

Select

Use the form-control class on any select element to style it. Use the multiple attributes for multiple option selection.

Select related variables.

<div class="form-group">
    <label class="form-label" for="front-end-frameworks">Front-end Frameworks</label>
    <select class="form-control" id="front-end-frameworks">
        <option value="react">React</option>
        <option value="vue">Vue</option>
        <option value="svelte">Svelte</option>
        <option value="ember">Ember</option>
    </select>
</div>
<div class="form-group">
    <label class="form-label" for="front-end-frameworks-multiple">Front-end Frameworks (Multiple)</label>
    <select class="form-control" id="front-end-frameworks-multiple" multiple="multiple">
        <option value="react">React</option>
        <option value="vue">Vue</option>
        <option value="svelte">Svelte</option>
        <option value="ember">Ember</option>
    </select>
</div>

Radio

For styling radio elements, use the form-check class on the label element and place the input (with the form-check__control class) and span (with the form-check__label class) inside it.

  • Use the form-group--vertical-check modifier class to align the group vertically,
  • and the form-group--horizontal-check to align horizontally.

You can set two additional sizes with form-check--sm and form-check--lg modifier class.

Check related variables.

Radio (Vertical)
<fieldset>
    <legend>Radio (Vertical)</legend>
    <div class="form-group form-group--vertical-check">
        <label class="form-check">
            <input class="form-check__control" type="radio" value="react" name="radio-example-vertical"/>
            <span class="form-label form-check__label">React</span>
        </label>
        <label class="form-check">
            <input class="form-check__control" type="radio" value="vue" name="radio-example-vertical" disabled="disabled"/>
            <span class="form-label form-check__label">Vue</span>
        </label>
        <label class="form-check">
            <input class="form-check__control" type="radio" value="svelte" name="radio-example-vertical"/>
            <span class="form-label form-check__label">Svelte</span>
        </label>
        <label class="form-check">
            <input class="form-check__control" type="radio" value="ember" name="radio-example-vertical"/>
            <span class="form-label form-check__label">Ember</span>
        </label>
    </div>
</fieldset>
Radio (Horizontal)
<fieldset>
    <legend>Radio (Horizontal)</legend>
    <div class="form-group form-group--horizontal-check">
        <label class="form-check">
            <input class="form-check__control" type="radio" value="react" name="radio-example-horizontal"/>
            <span class="form-label form-check__label">React</span>
        </label>
        <label class="form-check">
            <input class="form-check__control" type="radio" value="vue" name="radio-example-horizontal"/>
            <span class="form-label form-check__label">Vue</span>
        </label>
        <label class="form-check">
            <input class="form-check__control" type="radio" value="svelte" name="radio-example-horizontal"/>
            <span class="form-label form-check__label">Svelte</span>
        </label>
        <label class="form-check">
            <input class="form-check__control" type="radio" value="ember" name="radio-example-horizontal"/>
            <span class="form-label form-check__label">Ember</span>
        </label>
    </div>
</fieldset>

Checkbox

Use the form-check class on the label element for styling checkbox elements and place the input (with the form-check__control class) and span (with the form-check__label class) inside it. You can also display an indeterminate state programmatically.

Checkbox
<fieldset>
    <legend>Which one of you like?</legend>
    <div class="form-group form-group--vertical-check">
        <label class="form-check">
            <input class="form-check__control" type="checkbox" value="own" name="property-ownership2"/>
            <span class="form-label form-check__label">Own</span>
        </label>
        <label class="form-check">
            <input class="form-check__control" type="checkbox" value="rent" name="property-ownership2" disabled="disabled"/>
            <span class="form-label form-check__label">Rent</span>
        </label>
        <label class="form-check">
            <input class="form-check__control" type="checkbox" value="misc" name="property-ownership2"/>
            <span class="form-label form-check__label">Misc</span>
        </label>
        <label class="form-check">
            <input class="form-check__control" id="indeterminate" type="checkbox" value="misc" name="property-ownership2"/>
            <span class="form-label form-check__label">Indeterminate</span>
        </label>
    </div>
</fieldset>

Using theconfig('vertical-alignment', $form-control) variable, you can control the checkbox and radio input's vertical alignment. Also, with the additional .form-check--vertical-start and .form-check--vertical--center you can do it individually.

Checkbox Alignment
<fieldset>
    <legend>Checkbox Alignment</legend>
    <div class="form-group form-group--vertical-check">
        <label class="form-check form-check--vertical-center">
            <input class="form-check__control" type="checkbox" id="indeterminate" value="long-1" name="checkbox-long"/>
            <span class="form-label form-check__label">Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent sit amet sapien libero. Aenean tristique sed ligula nec tempor. In turpis nisi, rhoncus at urna sed, imperdiet imperdiet orci.</span>
        </label>
        <label class="form-check">
            <input class="form-check__control form-check--vertical-start" type="checkbox" value="long-2" name="checkbox-long"/>
            <span class="form-label form-check__label">Pellentesque lectus risus, ornare a gravida in, egestas euismod urna. Nullam rhoncus nisi sit amet quam accumsan, blandit tempor sem tristique. Mauris tempor quam sagittis tristique facilisis. Donec aliquet ac nisi eget facilisis.</span>
        </label>
    </div>
</fieldset>

Switch

The switch is a styled checkbox. Use the form-switch class to display it.

You can set two additional sizes with form-switch--sm and form-switch--lg modifier class.

Switch related variables.

<div class="form-group form-group--vertical-check">
    <label class="form-switch form-switch--sm">
        <span class="form-label form-switch__label">Enable notifications</span>
        <input class="form-switch__control" type="checkbox" value="true"/>
    </label>
    <label class="form-switch form-switch--vertical-start">
        <input class="form-switch__control" type="checkbox" value="true"/>
        <span class="form-label form-switch__label">Nulla egestas dui ac quam vulputate, in iaculis elit semper. Nullam vel dapibus nisl. Integer efficitur leo quis tincidunt fermentum. Aliquam erat volutpat. Duis condimentum maximus mi ut porta. In luctus ac sapien eget rutrum.</span>
    </label>
    <label class="form-switch">
        <input class="form-switch__control" type="checkbox" value="true"/>
        <span class="form-label form-switch__label">Enable notifications</span>
    </label>
    <label class="form-switch form-switch--lg form-switch--block">
        <span class="form-label form-switch__label">Enable notifications</span>
        <input class="form-switch__control" type="checkbox" value="true"/>
    </label>
    <label class="form-switch form-switch--lg form-switch--block">
        <input class="form-switch__control" type="checkbox" value="true" disabled="disabled"/>
        <span class="form-label form-switch__label">Enable notifications</span>
    </label>
</div>

Fieldset

You can use fieldset and legend elements to group more fields and set a vertical margin between them.

Your Name
<fieldset>
    <legend>Your Name</legend>
    <div class="form-group">
        <label class="form-label" for="first-name">First Name</label>
        <input class="form-control" id="first-name" name="first-name" type="text"/>
    </div>
    <div class="form-group">
        <label class="form-label" for="last-name">Last Name</label>
        <input class="form-control" id="last-name" name="last-name" type="text"/>
    </div>
</fieldset>

Description

Use the form-description class to create a description text under an input element.

Just a help text for presentation purposes.
<div class="form-group">
    <label class="form-label" for="first-name-2">First Name</label>
    <input class="form-control" id="first-name-2" name="first-name-2" type="text" aria-describedby="first-name-description"/>
    <span id="first-name-description" class="form-description">Just a help text for presentation purposes.</span>
</div>

Validation

You can set valid and invalid states on form-control elements using the form-control--valid and form-control--invalid class modifiers.

Also, you can display separate messages using the field-feedback class and its modifiers:

  • field-feedback--valid for valid state,
  • field-feedback--invalid for invalid state.
Address
Looks fine
Not that fine

Disabled

Using the disabled attribute, you can create disabled state on inputs.

Form Group

Form-group is the generic container class for form elements. Besides the basic implementations, you can use the following:

  • form-group: to group a label and an input element.
  • form-group--vertical-check: to stack the radio, checkbox, and switches vertically.
  • form-group--horizontal-check: to stack the radio, checkbox, and switches horizontally.
  • form-group--row: to align the label to the left side of the group. This layout using @container so you also need a wrapper with the form-group-container class like in the example.
  • form-group--stacked: to stack the inputs beside each other.
Form Group Row
Just a help text for presentation purpose
<fieldset class="form-group-container">
    <legend>Form Group Row</legend>
    <div class="form-group--row">
        <label class="form-label" for="first-name-row">First Name</label>
        <input class="form-control" id="first-name-row" type="text"/>
        <span class="form-description">Just a help text for presentation purpose</span>
    </div>
    <div class="form-group--row">
        <label class="form-label" for="front-end-frameworks-row">Front-end Frameworks</label>
        <select class="form-control" id="front-end-frameworks-row">
            <option value="react">React</option>
            <option value="vue">Vue</option>
            <option value="svelte">Svelte</option>
            <option value="ember">Ember</option>
        </select>
    </div>
</fieldset>
Form Group Stacked
<fieldset>
    <legend>Form Group Stacked</legend>
    <div class="form-group--stacked">
        <input class="form-control" type="text" aria-label="First Name"/>
        <input class="form-control" type="text" aria-label="Last Name"/>
    </div>
    <div class="form-group--stacked">
        <input class="form-control" type="text" aria-label="First Name"/>
        <input class="form-control" type="text" aria-label="Last Name"/>
        <button class="btn btn--primary">Submit</button>
    </div>
</fieldset>

Group Label

Add custom prefix or suffix "labels" to a form group (probably you want to use this with .form-group--stacked).

$
.00
<div class="form-group--row">
    <label class="form-label" for="product-price">Price</label>
    <div class="form-group--stacked">
        <div class="form-group-label">$</div>
        <input class="form-control" id="product-price" name="product-price" type="text">
        <div class="form-group-label">.00</div>
    </div>
</div>

Row

You can create a row stack using form-row--mixed class. It uses flexbox and automatically sets the columns based on the --inline-size custom property.

<div class="form-row--mixed">
    <div class="form-group" style="--inline-size: 30ch">
        <label class="form-label" for="city">City</label>
        <input class="form-control" id="city" name="city" type="text"/>
    </div>
    <div class="form-group">
        <label class="form-label" for="state">State</label>
        <input class="form-control" id="state" name="state" type="text"/>
    </div>
    <div class="form-group">
        <label class="form-label" for="zip">Zip</label>
        <input class="form-control" id="zip" name="zip" type="text"/>
    </div>
</div>