Theming Form Elements with Sass

Dennis Gaebel
Dennis Gaebel
Share

Form inputs without a doubt encompass a sizable portion of the Web. Since form controls can and will be encountered by users daily, it only seems fitting to lend our attention to a few suspects by furnishing each one with Sass’ strength to help us theme our project’s input’s swiftly.

Placeholders

A placeholder is a hint to users as to what information can be entered within the corresponding control. It applies when the value of the type attribute is set as text, search
, tel, url or email; otherwise it’s ignored. Unfortunately to style the placeholder requires the appropriate vendor prefixes associated with each pseudo required so authors can deliver coverage across the major browser vendors such as IE, Firefox, Safari, Chrome and Opera.

A Mixin Helper

This placeholder @mixin
can be used within a variety of contexts such as applying it alone or combined with a selector of your choosing. I’ve also taken the liberty of constructing a Sass map that contains all the properties you’re allowed to style. index.html
<label for="username">Name</label>
<input type="text" name="username" id="username" placeholder="first, last">
_placeholder-mixin.scss The @at-root
directive used by these mixins works by switching context where the nested call in your Sass is placed and pulls the declaration to the top level of the selector chain. Note: At the time of this post node-sass is unable to compile this placeholder mixin due to interpolation issues with the line @at-root #{&}#{$pseudo}.
$pseudo-phprefix: "::-webkit-input-placeholder" "::-moz-placeholder" ":-ms-input-placeholder" "::placeholder";

$ph-styles: (
font-family: sans-serif,
font-size: medium,
font-style: normal,
font-weight: normal,
color: inherit,
letter-spacing : normal,
line-height: normal,
text-align: inherit,
text-decoration: inherit,
padding: 0
);

@mixin placeholder-theme($styles) {
@each $pseudo in $pseudo-phprefix {

@at-root #{&}#{$pseudo} {
@each $key, $value in $styles {
#{$key}: #{$value};
}
}

}
}

@mixin placeholder {
@each $pseudo in $pseudo-phprefix {

@at-root #{&}#{$pseudo} {
@content
}

}
}
With the required @mixin
defined we can make it’s call as a standalone statement that will universally style placeholders with the properties and values you pass. The second method takes advantage of using a targeted selector to avoid globally styling certain and unwanted placeholders in your system. _placeholder-mixin.scss
@include placeholder { color: red; }

input {
@include placeholder-theme($ph-styles);
}
CSS Output
::-webkit-input-placeholder {
color: red;
}
::-moz-placeholder {
color: red;
}
:-ms-input-placeholder {
color: red;
}
::placeholder {
color: red;
}
input::-webkit-input-placeholder {
font-family: sans-serif;
font-size: medium;
font-style: normal;
font-weight: normal;
color: inherit;
letter-spacing: normal;
line-height: normal;
text-align: inherit;
text-decoration: inherit;
padding: 0;
}
input::-moz-placeholder {
font-family: sans-serif;
font-size: medium;
font-style: normal;
font-weight: normal;
color: inherit;
letter-spacing: normal;
line-height: normal;
text-align: inherit;
text-decoration: inherit;
padding: 0;
}
input:-ms-input-placeholder {
font-family: sans-serif;
font-size: medium;
font-style: normal;
font-weight: normal;
color: inherit;
letter-spacing: normal;
line-height: normal;
text-align: inherit;
text-decoration: inherit;
padding: 0;
}
input::placeholder {
font-family: sans-serif;
font-size: medium;
font-style: normal;
font-weight: normal;
color: inherit;
letter-spacing: normal;
line-height: normal;
text-align: inherit;
text-decoration: inherit;
padding: 0;
}
Words of Caution
: Avoid using the placeholder attribute in place of a label. Both vary in purpose as the label attribute describes the role of the form element; that is, it indicates what kind of information is expected. The placeholder attribute is a hint about the format the content should take. There are cases in which the placeholder attribute is never displayed to the user, so the form must be understandable without it.

Jumping Placeholder Effect

This is a pretty cool effect to style and theme, but only reacts in WebKit/Blink. Some also refer to this as the “Floating Label” pattern when upon the time of it’s debut caused fascinating discussions regarding the need for such a pattern.

See the Pen Jumping Placeholder by SitePoint (@SitePoint) on CodePen.

index.html
<label for="phone">Cell Phone</label>
<input type="tel" name="phone" class="jpinput" id="phone" placeholder="(555) 555-5555">
The magic happens due to the pseudo selector ::-webkit-input-placeholder[style*=hidden]
that allows authors to style the placeholder’s state once a user begins to enter his/her information. I would love to find a Mozilla equivalent to the aforementioned selector, but I’m coming up empty handed in my searches. David Bushell has a great demo I’ve linked to below that takes advantage of pseudo validity selectors, but of course the method does have a few cons. _jp-input.scss
$jpinput-height: 40px;
$jpinput-radius: 4px;
$jpinput-padding: 10px 16px;
$jpinput-bg: #8DAA91;
$jpinput-color: #4F4137;
$jpinput-ph-color: $jpinput-color;
$jpinput-phide-color: $jpinput-bg;

input {
appearance: none;
box-sizing: border-box;
border-radius: $jpinput-radius;
display: inline-block;
outline: 0;
width: 100%;
}

.jpinput {
height: $jpinput-height;
padding: $jpinput-padding;
transition: transform 225ms ease-in-out;
background: $jpinput-bg;
color: $jpinput-color;

@include placeholder {
position: relative;
top: 0;
left: 0;
transition: all 300ms ease-in-out;
color: rgba($jpinput-ph-color, .5);
}
}

.jpinput::-webkit-input-placeholder[style*=hidden] {
transform: translateY(-$jpinput-height);
opacity: 1;
visibility: visible !important;
color: $jpinput-phide-color;
}
There are certainly other approaches that I invite you to dive into further in order to identify the pros and cons. Just let it be known that many of the options that exist at the time of this writing are usually succumbing to JavaScript for support.

Labels

The HTML label
represents a caption for an item within the interface and can be associated with it’s correlating control. It also helps to indicate what kind of information is to be expected.

Secure Input

We can use the label to act as a sliding door in order to hide entered information when the input has it’s focus removed. It’s a slick way to conceal text that is intended to be private such as a credit card number or social security number. This example also takes advantage of the Sass placeholder @mixin I previously shared. The label may be positioned before, after or around the associated control in case you’re curious about this pattern.

See the Pen Secure Input by SitePoint (@SitePoint) on CodePen.

index.html
<div class="slabel">
<input id="credit-card" type="text" pattern="[0-9]{13,16}" placeholder="xxxx-xxxx-xxxx-xxxx">
<label for="credit-card">Credit Card</label>
</div>
_slabel-input.scss
$slabel-theme: (
border: 0,
radius: 0,
padding: 0,
font: inherit,
bg: white,
label-bg: white,
label-color: inherit,
error-color: #E82C0C,
placeholder-color: #B9E0D6,
success-color: #5C832F
);
Checkout these awesome patterns for labels by Jordano Aragão on CodePen that have quite a bit of personality and delight.

Inputs

The input element is used to create interactive controls with forms and accept data from a user’s input. Inputs usually come in the form of text, email, number, radios, checkboxes and even buttons.

Select

Here’s a clean way to style a select input without using a workaround or dirty technique to change the appearance. In most solutions that are leveraging JavaScript the native select element is set to display: none and churned into a set of divs. Clients love it, but authors despise it. I think the approach that follows is a win for everyone until a more native solution with CSS can be achieved. It allows authors to style the browsers that behave and show a solid fallback for those that don’t.

See the Pen Select Choice by SitePoint (@SitePoint) on CodePen.

index.html
<select>
<option>Property Closing*</option>
<option>open</option>
<option>closed</option>
</select>
_select-input.scss
$select-arrow: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4IiB3aWR0aD0iNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE0LjgzIDE2LjQybDkuMTcgOS4xNyA5LjE3LTkuMTcgMi44MyAyLjgzLTEyIDEyLTEyLTEyeiIvPjxwYXRoIGQ9Ik0wLS43NWg0OHY0OGgtNDh6IiBmaWxsPSJub25lIi8+PC9zdmc+';
$select-padding: 0;

select {
border-radius: 0;
-webkit-appearance: none; // autoprefixer won't add this and we still need it
appearance: none;
cursor: pointer;
padding: $select-padding;
width: 100%;

@media screen and (-webkit-min-device-pixel-ratio: 0) {
background-image: url(#{$select-arrow});
background-position: right 0 top 50%;
background-repeat: no-repeat;
}
}
Read the original article by Lea Verou discussing this approach or checkout these amazing Select Inspirations by Tympanus.

Radio

A radio represents a selection of one item from a list of items. This input is usually used in situations where a user has a single choice he/she can choose from whereas a checkbox scenario allows multiple choices to be selected. This custom radio that follows is a Sass based approach and refinement to the original version posted by Mark Otto on WTF, forms? that also includes some updated positioning techniques using CSS transforms for the inner circle plus it scales really well and super simplistic to get started customizing.

See the Pen Radio Control by SitePoint (@SitePoint) on CodePen.

index.html
<label for="radioa" class="input-control radio">
<input type="radio" id="radioa" name="radio" value="radio-value">
<span class="input-control__indicator"></span> Send Me Stuff!
</label>

<label for="radiob" class="input-control radio">
<input type="radio" id="radiob" name="radio" value="radio-value">
<span class="input-control__indicator"></span> Don't send a friggin' thing.
</label>
_input-radio.scss
$input-radius: 0 !default;
$input-unit: 16px !default; // accepts px, em, rem
$input-spacing: $input-unit * 1.5 !default; // adjust depending on font-family
$input-font: sans-serif !default;

$radio-bg: white !default;
$radio-txt-color: #AACCFF !default;

$radio-checked-custom: (
background: #0081D0
) !default;
$radio-checked: #222233 !default;
$radio-checked-focus: white !default;
Take a moment to gander at these awesome Animated Radio Inputs by Samurai for further exploration of ideas and inspiration.

Checkbox

The input element with a type attribute value of “checkbox” represents a state or option that can be toggled.

SVG Checkbox

This approach to follow uses an SVG technique originally demonstrated by Sara Soueidan
to create the custom checkbox and the x shape using the element. This element uses the stroke-dasharray and stroke-dashoffset SVG properties available to us authors in CSS to animate the selection (the X). This example builds upon the original demo I mentioned and adds the :focus state along with a few other goodies to help size and customize this input to fit your needs.

See the Pen SVG Checkbox by SitePoint (@SitePoint) on CodePen.

index.html
<input type="checkbox" id="optiona" name="optiona">
<label for="optiona">Click Me
<svg viewBox="0 0 100 100" xmlns="https://www.w3.org/2000/svg">
<path d="M 10 10 L 90 90"></path>
<path d="M 90 10 L 10 90"></path>
</svg>
</label>
_checkbox-input.scss
$font-size: 2em; // adjust accordingly
$ratio: 1; // adjust accordingly
$size: $ratio + em;
$ratio: $ratio;
$gutter: 5px; // adjust accordingly
$stroke-dash: 270;
$stroke: (
thickness: $gutter/3,
style: solid,
color: #fff
);
$mark-ischecked: (
stroke-dashoffset: 0
); // define css properties
$label-ischecked: (); // define css properties
$svg-ischecked: (); // define css properties

Slider Checkbox

Here’s a checkbox I use on the Transformicons project that resembles a slider. It’s primarily built with Sass, but it’s very reusable and possesses minimal code to customize and style. Take note that this custom input pattern also allows accessibility in terms of keyboard navigation. Once the user places focus on the input via the tab key a color indication will notify the user. Once this notification takes place the user can take action with their spacebar to set the choice.

See the Pen Transformicons Checkbox by SitePoint (@SitePoint) on CodePen.

index.html
<div class="slider-checkbox">
<input type="checkbox" id="option" aria-checked="false" role="checkbox">
<label for="option">Label Text</label>
</div>
The setup provided through the Sass configuration lets authors decide on the style of the input by accepting the values ’rounded’ or ‘null’. The ‘null’ value will turn off the border-radius
for the thumbslider as well as the track it sits within. This will essentially just make the input boxy. _slider-checkbox.scss
$checkbox-style: rounded; // accepts 'rounded' or 'null'
$checkbox-sizing-unit: 6rem; // adjust this value for sizing
$checkbox-height: $checkbox-sizing-unit / 1/4;
$checkbox-position-unit: $checkbox-sizing-unit * 3/4;
$checkbox-speed: 150ms;
$checkbox-off-bg: #E8DFE7;
$checkbox-on-bg: #4fbe79;
$checkbox-btn-bg: #FFFFFD;

Validity States

Validity pseudo-selectors are used to style interactive elements based on an evaluation of the user’s input such as :invalid and :valid
. Here’s a Sass @mixin to help theme out those particular cases where you want to be all fancy like. A nice little note to be aware of is that :invalid styles apply when the input has the required attribute (even if the input is empty).
@mixin valid {
&:valid,
&:empty:valid,
&:focus:empty:valid {
@content;
}
}

@mixin invalid {
&:invalid,
&:focus:invalid {
@content;
}
}
Here’s a few more psuedos to make your head hurt in terms of forgotten states when it comes to inputs and their control states.
:required
:optional
:enabled
:disabled
:not(:checked)
:in-range
:out-of-range
:user-error
:empty
:blank

Conclusion

If you’d like to go further into the land of styling native form elements here’s a whole bunch of cross browser specific pseudo-elements to knock your socks off trying. If you’d like to learn more about validity states you can set aside some time and read the W3C specification at your leisure. Happy form styling everyone!

Frequently Asked Questions on Theming Form Elements with SASS

How can I use SASS to style form elements?

SASS, which stands for Syntactically Awesome Stylesheets, is a CSS preprocessor that allows you to use variables, nested rules, mixins, functions, and more, all with a fully CSS-compatible syntax. To style form elements using SASS, you first need to define your styles in a .scss file. For example, you can define a style for a text input field like this:

input[type="text"] {
border: 1px solid #ccc;
padding: 10px;
font-size: 16px;
}

Then, you can compile this .scss file into a .css file using a SASS compiler. The resulting CSS can be linked to your HTML file to style the form elements.

What are the benefits of using SASS for theming form elements?

SASS offers several benefits for theming form elements. First, it allows you to use variables, which can be very useful for maintaining consistent styles across your website. For example, you can define a variable for your primary color and use it throughout your styles. If you decide to change this color later, you only need to update the variable, and the change will be applied everywhere the variable is used.

Second, SASS supports nested rules, which can make your styles more readable and easier to maintain. For example, you can nest styles for different states of a form element inside the style for the element itself.

Third, SASS provides mixins and functions, which can help you to avoid repeating code. For example, you can define a mixin for a common style pattern and reuse it in different parts of your styles.

How can I use SASS variables in theming form elements?

SASS variables allow you to store values that you want to reuse throughout your stylesheet. You can use them to keep your styles consistent and easy to maintain. For example, you can define a variable for your primary color like this:

$primary-color: #336699;

Then, you can use this variable in your styles like this:

input[type="text"] {
border: 1px solid $primary-color;
}

If you decide to change your primary color later, you only need to update the variable, and the change will be applied everywhere the variable is used.

How can I use SASS nested rules in theming form elements?

SASS nested rules allow you to nest styles inside other styles, which can make your styles more readable and easier to maintain. For example, you can nest styles for different states of a form element inside the style for the element itself like this:

input[type="text"] {
border: 1px solid #ccc;

&:focus {
border-color: #336699;
}

&:disabled {
background-color: #eee;
}
}

In this example, the &:focus and &:disabled styles are nested inside the input[type="text"] style. The & character refers to the parent selector, so &:focus is equivalent to input[type="text"]:focus.

How can I use SASS mixins in theming form elements?

SASS mixins allow you to define styles that can be reused throughout your stylesheet. You can use them to avoid repeating code. For example, you can define a mixin for a common style pattern like this:

@mixin form-element {
border: 1px solid #ccc;
padding: 10px;
font-size: 16px;
}

Then, you can include this mixin in your styles like this:

input[type="text"] {
@include form-element;
}

In this example, the @include form-element; line includes the styles defined in the form-element mixin.

How can I use SASS functions in theming form elements?

SASS functions allow you to define complex operations that you can reuse throughout your stylesheet. For example, you can define a function to calculate the contrast color for a given color like this:

@function contrast-color($color) {
// Calculate the contrast color...
}

Then, you can use this function in your styles like this:

input[type="text"] {
background-color: $primary-color;
color: contrast-color($primary-color);
}

In this example, the contrast-color($primary-color); line calls the contrast-color function with the $primary-color variable as an argument.

How can I use SASS partials in theming form elements?

SASS partials allow you to create modular styles that can be imported into other stylesheets. For example, you can define a partial for your form element styles like this:

// _form-elements.scss

input[type="text"] {
@include form-element;
}

Then, you can import this partial into your main stylesheet like this:

// main.scss

@import 'form-elements';

In this example, the @import 'form-elements'; line imports the styles defined in the _form-elements.scss partial.

How can I use SASS inheritance in theming form elements?

SASS inheritance allows you to share a set of CSS properties from one selector to another. For example, you can define a base style for form elements like this:

%form-element {
border: 1px solid #ccc;
padding: 10px;
font-size: 16px;
}

Then, you can extend this base style in your specific form element styles like this:

input[type="text"] {
@extend %form-element;
}

In this example, the @extend %form-element; line includes the styles defined in the %form-element base style.

How can I use SASS operators in theming form elements?

SASS supports standard mathematical operators like +, -, *, /, and %. You can use them to calculate values in your styles. For example, you can define a style for a text input field with a calculated padding like this:

input[type="text"] {
padding: 10px * 2;
}

In this example, the 10px * 2 expression calculates the padding value.

How can I use SASS control directives in theming form elements?

SASS control directives allow you to use if/else conditions and for/each/while loops in your styles. For example, you can define a style for a text input field with a conditional color like this:

$input-type: 'text';

input[type="#{$input-type}"] {
@if $input-type == 'text' {
color: #000;
} @else {
color: #fff;
}
}

In this example, the @if $input-type == 'text' line checks if the $input-type variable is equal to ‘text’. If it is, the color is set to #000. Otherwise, the color is set to #fff.