Get pro WordPress tips or your money back!

The Ultimate Guide to Styling Forms

Forms are a crucial part of the the web. We love collecting information from our site’s visitors and forms gives us a lot of different ways to accomplish that. Almost every site has a form of some kind, from a simple newsletter signup, all the way to forms that have hundreds of input fields like a job application. As patrons of the internet we’ve fallen under the spell of forms, and it’s a love that none of us can deny.

As designers and developers, we hate it when things look different in every different browser. No one starts designing a website with an end goal of having five different versions. Forms and input fields are infamous for varying from browser to browser.

This is our house and these are our rules, so for today’s Workbench post, we’re going to show you what we’ve done to standardize the process of styling forms for our client projects, and give you some fairly simple CSS form styling snippets that will help you do the same.

Bonus content: We’ve also included a tutorial for building a newsletter signup form, replete with animations, using the Gravity Forms WordPress plugin. 🎉

Getting started with HTML5 Reset

Before we can get into styling our input fields, I’d like to briefly touch on resetting your styles, often called HTML5 Reset. The reason we do this is to make sure our design is as cross-browser compliant as possible from the start, meaning we want our site to look the same on as many browsers as possible.

Occasionally in web development we need to take life advice from Miley Cyrus and 🎵 Come in like wrecking ball 🎵HTML5 Reset is the wrecking ball that lets us take control back from the browser. Without a reset in place, browsers are going to break our design. It’s that simple.

There are of variations of HTML5 Reset out there and you should find one that fits your needs the best. They all do the same thing. Some take different paths, but they all get us  to the end goal, which is all we really care about. A good place to start your reset research can be found at HTML5 Reset.

At WP Site Care we use Normalize.css. It’s the perfect blend of easy-to-use and exceedingly thorough.

General CSS Form Styling

Input fields will work without any CSS form styles. They’ll look terrible, but they WILL work.

Input Fields Without CSS Form Styling
Input fields will display and work without any styling.

Working is only half the battle. Leaving input fields in this state, without styles, would make us irresponsible developers. We can’t stop and we won’t stop.

Getting these styles to look nice can be a lot of work. The good news is we’ve done a lot of the heavy lifting for you. Take a look at the CSS below to get you started:

/* Forms
--------------------------------------------- */
input[type="color"],
input[type="date"],
input[type="datetime"],
input[type="datetime-local"],
input[type="email"],
input[type="month"],
input[type="number"],
input[type="password"],
input[type="search"],
input[type="tel"],
input[type="text"],
input[type="time"],
input[type="url"],
input[type="week"],
select,
textarea {
border-color: #e0e0e0;
-webkit-border-radius: 3px;
border-radius: 3px;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06);
border-width: 1px;
border-style: solid;
color: #474e57;
font-size: 16px;
font-weight: 400;
padding: 10px;
width: 100%;
}
input[type="color"]:hover,
input[type="date"]:hover,
input[type="datetime"]:hover,
input[type="datetime-local"]:hover,
input[type="email"]:hover,
input[type="month"]:hover,
input[type="number"]:hover,
input[type="password"]:hover,
input[type="search"]:hover,
input[type="tel"]:hover,
input[type="text"]:hover,
input[type="time"]:hover,
input[type="url"]:hover,
input[type="week"]:hover,
textarea:hover, select:hover {
border-color: #e0e0e0;
}
input[type="color"]:focus,
input[type="date"]:focus,
input[type="datetime"]:focus,
input[type="datetime-local"]:focus,
input[type="email"]:focus,
input[type="month"]:focus,
input[type="number"]:focus,
input[type="password"]:focus,
input[type="search"]:focus,
input[type="tel"]:focus,
input[type="text"]:focus,
input[type="time"]:focus,
input[type="url"]:focus,
input[type="week"]:focus,
textarea:focus, select:focus {
border-color: #adadad;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06), 0 0 5px rgba(160, 160, 160, 0.7);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06), 0 0 5px rgba(160, 160, 160, 0.7);
outline: none;
}
input[type="color"] {
padding: 5px;
}
input[type="checkbox"],
input[type="image"],
input[type="radio"] {
width: auto;
}
input::-webkit-input-placeholder,
textarea::-webkit-input-placeholder {
color: #c7c7c7;
opacity: 1;
font-weight: 400;
}
input:-moz-placeholder,
textarea:-moz-placeholder {
color: #c7c7c7;
opacity: 1;
font-weight: 400;
}
input::-moz-placeholder,
textarea::-moz-placeholder {
color: #c7c7c7;
opacity: 1;
font-weight: 400;
}
input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
color: #c7c7c7;
opacity: 1;
font-weight: 400;
}
input::placeholder,
textarea::placeholder {
color: #c7c7c7;
opacity: 1;
font-weight: 400;
}
view raw general-forms.css hosted with ❤ by GitHub

Taking a Shortcut

You’ll notice we’ve elected to add rules for the various input types. A shortcut to doing this, which we’ve seen in a lot of WordPress themes, would be to do something like the following:

input,
select,
textarea {
    background-color: #fff;
    border-color: #e0e0e0;
    -webkit-border-radius: 3px;
    border-radius: 3px;
    -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06);
    border-width: 1px;
    border-style: solid;
    color: #474e57;
    font-size: 16px;
    font-weight: 400;
    padding: 10px;
    width: 100%;
}

It’s our party and we can do what we want. It’s our party and we can style how we want. But if we don’t include the input type in the rule, we lose some of the granular control we’ll want later in the project. Obviously it’s less work to throw a few global styles on input fields to get us started, but later on we’re probably going to regret that decision.

The shortcut approach is going to leave us with a bunch of different input types that look weird later on, and that’s exactly what we’re trying to avoid. By being more specific and using input types in our CSS form styling, we have control over everything.

We run CSS. CSS don’t run us.

Focus and hover styles are not only good to have for accessibility purposes, but they’re just a nice touch overall. How many times have you heard that “details are everything”? The difference is small, but adding these kinds of styles and small touches ultimately makes the experience better for everyone.

The Climb: Styling Range, Select, Checkbox and Radio Fields

Believe it or not we’ve already covered most of the most commonly-used input types, but there’s always going to be another mountain, and we’re always going to want to make it move. There are a couple of other form fields that we can do a better job at styling rather than letting the browser style them for us.

It’s not as big of a deal anymore thanks to advances in modern web development tools, but I remember the first time I was given a project with custom-designed select, checkbox, radio and range inputs. I freaked out and I kept looking down into the ground. I st-st-stuttered when they asked me what I was thinking about. I felt like I couldn’t breathe and the designer asked me what was wrong with me. My best friend Leslie said, “Oh, he’s just being Ozzy”.

True story.

Range Field Styles: We’re Gonna Go All Night, Until We See the Sunlight

To be perfectly honest, styling range input fields is not fun at all. I’m still trying to wake myself up so I can flee from this nightmare of styling range fields. Range fields are probably the least standardized input field. It’s because there are quite a few moving parts and browsers love to attach their own vendor prefixes on them.

In any case, this is an “Ultimate Guide” and there’s no way we could call it that unless we helped you tackle this field as well.

input[type="range"] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
height: 28px;
margin: 10px 0;
width: 100%;
}
input[type="range"]:focus {
outline: none;
}
input[type="range"]:focus::-ms-fill-lower {
background: #adadad;
}
input[type="range"]:focus::-ms-fill-upper {
background: #adadad;
}
input[type="range"]:focus::-webkit-slider-runnable-track {
background: #adadad;
}
input[type="range"]::-webkit-slider-runnable-track {
background: #e0e0e0;
border: 0 none;
-webkit-border-radius: 3px;
border-radius: 3px;
cursor: pointer;
height: 3px;
width: 100%;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
background: #1c1e1f;
border: 0 none;
-webkit-border-radius: 100%;
border-radius: 100%;
cursor: pointer;
height: 28px;
margin-top: 14px;
width: 28px;
}
input[type="range"]::-moz-range-track {
background: #e0e0e0;
border-radius: 3px;
cursor: pointer;
height: 3px;
width: 100%;
}
input[type="range"]::-moz-range-thumb {
background: #1c1e1f;
border: 0 none;
border-radius: 100%;
cursor: pointer;
height: 28px;
width: 28px;
}
input[type="range"]::-ms-track {
background: transparent;
border-color: transparent;
color: transparent;
cursor: pointer;
width: 100%;
height: 3px;
}
input[type="range"]::-ms-fill-lower {
background: #e0e0e0;
border-radius: 3px;
}
input[type="range"]::-ms-fill-upper {
background: #e0e0e0;
border-radius: 3px;
}
input[type="range"]::-ms-thumb {
background: #1c1e1f;
border: 0 none;
border-radius: 100%;
cursor: pointer;
margin-top: 0;
height: 28px;
width: 28px;
}
view raw range.css hosted with ❤ by GitHub

If you notice lines 2-4 from the code above, you’ll see exactly what helps us to bend this field to our will. The magical appearance property.

The appearance property is used to display an element using a platform-native styling based on the users’ operating system’s theme.

CSS-Tricks

Although the point of the property is to make an element look and behave like something (using the browsers’ default styles), we used it to do the exact opposite. We’re telling the browser,

🎵 Can’t you see it’s we who own the night? Can’t you see it’s we who bout’ that life? 🎵

After using the appearance property, you have to concern yourself with the thumb (the part you move) and the track (the slider), and browsers use their own vendor prefixes. I like using range.css to start with, but you’ll need to test on different browsers and versions of them as things break and get out of hand very quickly!

Styling the Select Field

Select fields have been throwing a wrench into our whole design and cross-browser compatibility operation for years. There have been many solutions, including building entire Javascript libraries, but the easiest way I’ve found to style select fields is just a few lines of CSS.

Select fields as seen in different browsers
Select fields as seen in different browsers

Our starter theme Alpha has these modifications for styling the select field built into it, but if you drop this CSS snippet into your style sheet and download the source files for this article to get the image, that’s everything you’ll need for beautiful uniform select fields, regardless of the browser being used.

select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url(images/select-bg.svg);
background-repeat: no-repeat;
background-position: right 10px center;
-webkit-background-size: 14px 12px;
background-size: 14px 12px;
}
view raw select.css hosted with ❤ by GitHub

And there it is. We’re using the appearance property again. Different browsers will style that selector button differently. This causes visitors to have a different experience depending on which browser they’re using to view our site, which we definitely don’t want. Once that’s out of the way, we can add cool stuff like a neat background image that will act as the selector.

Using an SVG for the image (let us not forget visitors who zoom in or out on our site, or those that are on high pixel density displays (Retina)). An added bonus of using svg besides being fully scalable, is that they’re typically much smaller in file size than the usual JPG or PNG.

If you don’t have access to SVG images and you need to use a different format, you should check out this article on How to Resize an Image Without Losing Quality. There are lots of great tips on how to handle images and scale them without sacrificing quality or performance.

Custom Checkbox and Radio Input CSS

I adore custom checkboxes and radio fields. When they say they love me, they know I love them more. You can really step up your form game with the styling of these.

The DOM tree and how these input fields are built is super important here. You may have to change either the CSS rules or your DOM structure in order to get this to work correctly, but this level of attention to detail gets easily noticed and you won’t regret the extra effort.

input[type="checkbox"],
input[type="radio"] {
margin: 0;
opacity: 0;
padding: 0;
}
input[type="checkbox"] + label,
input[type="radio"] + label {
background: url(images/checkbox.svg) no-repeat scroll left center transparent;
-webkit-background-size: 15px 15px;
background-size: 15px 15px;
display: inline-block;
line-height: 15px;
margin-left: -18px;
padding-left: 22px;
}
input[type="checkbox"]:checked + label {
background-image: url(images/checked.svg);
-webkit-background-size: 16px 15px;
background-size: 16px 15px;
}
input[type="radio"] + label {
background-image: url(images/radio.svg);
}
input[type="radio"]:checked + label {
background-image: url(images/radio-select.svg);
}
view raw checkbox-radio.css hosted with ❤ by GitHub

As you can probably tell, we’re using more SVG images to display the checkboxes and radio inputs, as well as their different states. In the first few lines, by changing the padding, margin and opacity, we get rid of the browser’s default styles for these. In the following lines, we’re playing a trick on the browser by positioning the images where we need them.

🎵 We’re not a mistake and we’re not fake. It’s set in our DNA. We can’t be tamed. 🎵

Styling WordPress Form Plugins

We now have a foundation and an understanding of what it takes to style form fields. So far, we’ve only used them in an environment that we have total control over; static HTML. If you’re not ready to go home, and want to know how to style these forms within WordPress, can I get a “hell no”, and your attention for a few more minutes?

HELL NO!

We use plugins because creating forms in HTML is a painful process. I don’t think I’ve met one person who uses WordPress that prefers to hand code their forms rather than use a form plugin. These plugins generally come standard with their own styles. They do this because they want the forms to look as good as possible not just with different browsers, but the different themes that they’re used on. Let’s use a popular plugin like Gravity Forms as an example.

So we’ve added the styles for our custom styles to our form inputs and now we want to add Gravity Forms to our site, like I did here.

You’ll have to make a few edits, especially to select, checkbox and radio input fields for things to look more normal. Use your judgement and style accordingly, but here’s what I did:

.gform_wrapper .ginput_container_multiselect select {
background-image: none;
}
.gform_wrapper .gfield_checkbox li input + label,
.gform_wrapper .gfield_radio li input + label {
margin-left: 0;
padding-left: 22px;
}
view raw gravity-edits.css hosted with ❤ by GitHub

I just removed the background image that we’re using for the select input out of the multi-select field, as well as making some minor adjustments to the checkbox and radio fields. We no longer have control of the DOM tree because Gravity Forms does that for us, so some minor adjustments are necessary.

Another reason we’re having to make these edits is because we’re loading the styles that come with the form plugin. What if we decided not to do that and wanted to use all of our own custom styles? Every single night and every single day, I’mma do my thing, I’mma do my thing.

You can too.

Custom Gravity Forms CSS

The first thing we’ll need to do is disable Gravity Forms’ front end styles. Here’s a filter that you can put into your functions.php file:

<?php
// Do NOT include the opening php tag shown above. Copy the code shown below.
/**
* Disable GravityForms from using their CSS.
*
* Callback included in WordPress core.
*
* @see pre_option_rg_gforms_disable_css
*/
add_filter( 'pre_option_rg_gforms_disable_css', '__return_true' );

Then you start building your styles for your particular theme. This is a long and tedious process and yes, quite painful. We have a version that we built for use with our starter themes, but they’ll also work with other Sassy WordPress themes. Go check out our Gravity Forms SCSS files. If SCSS isn’t your thing, you can see the CSS version here.

Note: We used the flexbox property quite a bit in our gravity forms styles, so if you’re wanting to support IE 10 or below, you probably shouldn’t use them.

Styled Newsletter Signup Form

While I was in the middle of creating this article, Rob Neu sent me the following message in Slack:

I got my sights set on you, and I’m ready to wait. I have a form idea for you that will never be tamed.

Rob Neu, CTO at WP Site Care

Like you, this piqued my interest quite a bit. He thought it would be a good idea to show you how to style a Gravity Form based on the form you can find here, which has a real nice animation. Challenge accepted. BANGERZ!

Expanding Gravity Form
Look at that form dance!

To follow along with this part of the tutorial, you’ll need to purchase Gravity Forms and install it on your website. There’s a good possibility you can adapt this tutorial to other WordPress form plugins as well, but I based this tutorial on Gravity Forms.

The first thing we need to do is build out the form. We need a  form title, an email field, and a submit button. Go ahead and build the form, or if you’re too lazy, we’ve packed our sample form that you can download and import into your website.

If you build the form yourself, you’ll need to add a class of newsletter-signup to your form. I used this class to target this specific form in both the CSS and jQuery we’ll be using.

Here’s the CSS rules that we need to have in order for this to work:

.newsletter-signup_wrapper {
background-color: #fff;
color: #d5bba4;
height: 70px;
margin: 2rem auto;
overflow: hidden;
position: relative;
-webkit-transition: all 0.5s cubic-bezier(0.7, 0, 0.3, 1);
transition: all 0.5s cubic-bezier(0.7, 0, 0.3, 1);
}
.newsletter-signup_wrapper .gform_title {
background-color: #e85657;
border: 0 none;
color: #fff;
cursor: pointer;
font-family: Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 700;
line-height: 7rem;
letter-spacing: 1px;
overflow: hidden;
padding: 0 1rem;
text-align: center;
text-transform: uppercase;
}
.newsletter-signup_wrapper .button {
background: #ba997b;
display: block;
font-size: 2rem;
font-weight: 800;
letter-spacing: 1px;
padding: 1.5rem;
width: 100%;
}
.newsletter-signup_wrapper .button:focus, .newsletter-signup_wrapper .button:hover {
background: #a9896d;
}
.newsletter-signup_wrapper .gform_body,
.newsletter-signup_wrapper .gform_footer {
padding: 0 2rem 2rem;
}
.newsletter-signup_wrapper form {
font-size: 2.4rem;
-webkit-perspective: 1000px;
perspective: 1000px;
-webkit-transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
}
.newsletter-signup_wrapper label {
color: #d5bba4;
font-weight: 700;
font-size: 2rem;
padding: 1rem 0 0;
text-transform: uppercase;
}
.newsletter-signup_wrapper .gform_body,
.newsletter-signup_wrapper .gform_footer {
line-height: 1.75;
-webkit-transition: all 0.4s 0.2s;
transition: all 0.4s 0.2s;
-webkit-transform: rotateX(-45deg);
transform: rotateX(-45deg);
-webkit-transform-origin: center top;
-ms-transform-origin: center top;
transform-origin: center top;
opacity: 0;
}
.newsletter-signup_wrapper input {
-webkit-border-radius: 0;
border-radius: 0;
}
.newsletter-signup_wrapper.open .gform_title {
cursor: auto;
}
.newsletter-signup_wrapper.open .gform_body,
.newsletter-signup_wrapper.open .gform_footer {
opacity: 1;
-webkit-transform: rotateY(0deg);
transform: rotateY(0deg);
}
view raw newsletter-css.scss hosted with ❤ by GitHub

The form from the example we’re using uses a button at the top of the form to start the animation of the form, but we don’t have access to that. Gravity Forms does output a title, so we can use that and fake it ’till we make it which is why we styled the gform_title to look like a button. We’ve also set a line-height for this button and set it to the same as what we’re making the height of our pseudo button. This lets us hide the form until we’re ready to show it.

The next piece of the puzzle is jQuery:

(function( window, $, undefined ) {
'use strict';
var $document = $( document );
function newsletterSignUp() {
var $newsletterFormWrap = $( '.newsletter-signup_wrapper' ),
$newsletterTitle, $newsletterForm, $newsletterFormHeight;
if ( 0 === $newsletterFormWrap.length ) {
return;
}
$newsletterTitle = $newsletterFormWrap.find( '.gform_title' );
if ( 0 === $newsletterTitle.length ) {
return;
}
$newsletterForm = $newsletterFormWrap.find( 'form' );
$newsletterFormHeight = $newsletterForm.height();
$newsletterTitle.on( 'click', function() {
var $that = $( this );
$newsletterFormWrap.addClass( 'open' ).height( $newsletterFormHeight );
})
};
// Document ready.
$document.ready(function() {
newsletterSignUp();
});
})( this, jQuery );
view raw newsletter-js.js hosted with ❤ by GitHub

All that code does is check to see if we have this specific form on the page, then it checks to see if a title exists. If both of those are the case, then it just waits for someone to click on the title. Once the title is clicked, it adds a class of open to the form wrapper and sets the true height of it. The actual animation is done with the CSS rules we used earlier.

You can either add that to some existing scripts in your theme or just add that code to a new file. If you choose to go with the latter, you’ll need to enqueue the script in your theme, and here’s the code you’d need to do that. You can add this into your functions.php file:

<?php
// Don't add the opening <?php tag
// Register responsive menu script
add_action( 'wp_enqueue_scripts', 'prefix_enqueue_scripts' );
/**
* Enqueue responsive javascript.
* @todo Change 'prefix' to your theme's prefix
*/
function prefix_enqueue_scripts() {
wp_enqueue_script( 'newsletter-form', get_stylesheet_directory_uri() . '/js/newsletter-js.js', array( 'jquery' ), '1.0.0', true ); // Change 'prefix' to your theme's prefix
}

Want to see the form we just built in action? Of course you do! Check it out!

Custom Gravity Forms CSS After Animation
Our custom Gravity Form after animation

Party in the U.S.A

We don’t wanna call it a master class, but this is definitely an ultimate guide to styling forms. At this point, you have a solid foundation and lots of code examples to tweak and make your own. We’ve covered general things to consider with styling form inputs, to bending input fields to our will, and even building out a practical example of what you can do with forms using a bit of CSS and JavaScript.

🎵 You can throw your hands up, they’re playing your song, the butterflies fly away. You might be nodding your head like “Yeah!”, even moving your hips like “Yeah!”. 🎵

No matter how you choose to get down or celebrate, if you have any questions or have some amazing form styles that you’re super proud of, I’d love to hear about them in the comments below!

View Demo Download Source

Get pro WordPress tips or your money back

9 Comments

  1. Samantha

    See, this is why I like to just use a WordPress plugin to make forms. Looking at all the CSS you’ve gotta wrestle with to make a form look half decent pretty much makes my eyes roll back into my head. Haha. I’ll just stick to the stuff I’m actually good at and let plugins make it easy on me when they can 🙂

    1. Ozzy Rodriguez

      Hey Samantha,

      Thanks for your comment!

      You can definitely let the form plugin do all of the styling for you. I actually use Gravity Forms to create the forms in the article. It’s been my go-to form plugin for years!

      The article is more about the things you should consider if you’re wanting your forms to look the same across different browsers, which is a requirement with the clients and designers we work with. Most big name brands and high end designers want the visitor to have the same experience, no matter which browser they’re viewing it on. That’s why I went through the different things you’d need to think about, and even shared some CSS snippets that you could use, in order to help you get there faster.

      So basically, if you want to take your form styling to the next level, you should definitely come back and take advantage of the work we’ve already put in. 🙂

  2. Jesper

    Great read! Styling forms is always something I hate, I usually end with regular bootstrap styling hehe.

  3. Raaj Trambadia

    Hey Ozzy

    Quick question – why not reset ALL the properties of the input fields to their default states as assigned by W3, and then add our CSS after that? Wouldn’t that guarantee to remove everything (literally, everything) applied to the fields?

    There are some themes in the market who don’t play fair when it comes to styling the UI elements – and the reset method you’ve mentioned, although great, might not be able to overpower them!

    1. Ozzy Rodriguez

      The reset method is more an example of something you should do when building the styles for a theme from scratch.

      If you’re working with a pre-built theme, you’re right, there are some cases where you’d need to undo what they’ve done. Creating a child theme and performing your resets and new styles in there would probably be the best way to get started down that path.

  4. Jelm

    Thanks Guys, awesome, this saved me the trouble of adding another plugin for forms, I have created Mailchimp from from scratch (Html+css) but this came from mailchimp code, how can I change the gravity forms syntax to Mailchimp? Still very good tutorial. Good work

    1. Ozzy Rodriguez

      I don’t know what their forms look like or their formatting, but you you might want to start off with the normal HTML + CSS example as a basis and use those to match the form you want to use as they might be closer.

  5. Sasha-Shae

    Thanks for this..This method should most likely also work with other forms out there, like WPForms, Ninja Forms etc providing we change the classes ids to the correct ones, right?

    1. Ozzy Rodriguez

      HI Sasha-Shae!

      Thanks for reading!

      Yes, it should work. You may have to change a couple of things depending on how they structure their form output.

Leave a Reply

You have to agree to the comment policy.