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-controlclass is used for input (not radio, checkbox, range, or file), textarea, select elements, - the
form-checkis used for radio and checkbox, - the
form-switchis used for switch-styled checkboxes, - the
form-rangeis used for range typed input, - the
form-fileis used for file typed 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>Use the form-file class on an input[type="file"] to natively style the file input.
- You can size the input with
form-file--smandform-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).
<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>Use the form-range class on an input[type="range"] to natively style the range input.
- You can size the input with
form-range--smandform-range--lg(fixed in v3: these classes were documented before but missing from the CSS).
<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>Use the form-control class on any textarea element to style it. To set its global height, 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>Use the form-control class on any select element to style it. Use the multiple attributes for multiple option selection.
<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>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-checkmodifier class to align the group vertically, - and the
form-group--horizontal-checkto align horizontally.
You can set two additional sizes with form-check--sm and form-check--lg modifier class.
<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><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>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 by setting the indeterminate property with JavaScript.
<fieldset>
<legend>Which ones do 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 the config('vertical-alignment', $form-check) variable, you can control the vertical alignment of the checkbox and radio inputs. With the .form-check--vertical-start and .form-check--vertical-center modifiers, you can do it individually.
<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 form-check--vertical-start">
<input class="form-check__control" 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>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.
Use role="switch" on the input so screen readers announce it as a switch (on/off) instead of a checkbox.
<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>You can use fieldset and legend elements to group more fields and set a vertical margin between them.
<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>New in v3. Add the form-label--required modifier to the label to show a required marker (* by default, in the required form color). Keep the required attribute on the input: the marker is visual only.
<div class="form-group">
<label class="form-label form-label--required" for="required-email">Email</label>
<input class="form-control" id="required-email" type="email" required/>
</div>Change the marker with the required-content and required-gap keys of the $form-label map, for example 'required-content': '(required)'.
New in v3. Read-only controls (readonly attribute) can have their own background with the background-readonly form color (null, so the same as the regular background, by default).
@use 'sprucecss/scss/spruce' with (
$colors: (
'form': (
'background-readonly': hsl(210deg 40% 97%),
),
),
);Use the form-description class to create a description text under an input element.
<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>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--validfor valid state,field-feedback--invalidfor invalid state.
Using the disabled attribute, you can create disabled state on inputs.
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 uses@container, so you also need a wrapper with theform-group-containerclass, like in the example.form-group--stacked: to stack the inputs beside each other.
<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 purposes</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><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>Add custom prefix or suffix "labels" to a form group (probably you want to use this with .form-group--stacked).
<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>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>Looking for more? Spruce UI has ready-made form components built on these styles: password field, OTP input, tag input, rating, segmented control, combobox and more.


