How to Style the Easy-to-Style Widgets

The easier widgets to style are <form>, <input> (but not "search"), <label>, <output>, and a few others. By styling these when creating your form, you make the form easier for the user and ensure the data is correct by using form validation in the next section. This has a great example of styling a "postcard" contact form from scratch. Download the starter file and follow along to make your own styled form.

Styling simple form widgets

The "easy-to-style" widgets in the previous section may be styled using techniques from the articles Your first form and CSS building blocks. There are also special selectors - UI pseudo-classes - that enable styling based on the current state of the UI.

We'll walk through an example at the end of this article - but first, here are some special aspects of form styling that are worth knowing about.


Fonts and text

CSS font and text features can be used easily with any widget (and yes, you can use @font-face with form widgets). However, browser behavior is often inconsistent. By default, some widgets do not inherit font-family and font-size from their parents. Many browsers use the system's default appearance instead. To make your forms' appearance consistent with the rest of your content, you can add the following rules to your stylesheet:

button,
input,
select,
textarea {
  font-family: inherit;
  font-size: 100%;
}

The inherit property value causes the property value to match the computed value of the property of its parent element; inheriting the value of the parent.

The screenshots below show the difference. On the left is the default rendering of an <input type="text">, <input type="date">, <select>, <textarea>, <input type="submit">, and a <button> in Chrome on macOS, with the platform's default font style in use. On the right are the same elements, with our above style rule applied.

Screenshot of how different browsers present the same form

The defaults differed in a number of ways. Inheriting should change their fonts to that of the parent's font family - in this case, the default serif font of the parent container. They all do, with a strange exception - <input type="submit"> does not inherit from the parent paragraph in Chrome. Rather, it uses the font-family: system-ui. This is another reason to use <button> elements over their equivalent input types!

There's a lot of debate as to whether forms look better using the system default styles, or customized styles designed to match your content. This decision is yours to make, as the designer of your site, or web application.


Box sizing

All text fields have complete support for every property related to the CSS box model, such as width, height, padding, margin, and border. As before, however, browsers rely on the system default styles when displaying these widgets. It's up to you to define how you wish to blend them into your content. If you want to keep the native look and feel of the widgets, you'll face a little difficulty if you want to give them a consistent size.

This is because each widget has its own rules for border, padding, and margin. To give the same size to several different widgets, you can use the box-sizing property along with some consistent values for other properties:

input,
textarea,
select,
button {
  width: 150px;
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

In the screenshot below, the left column shows the default rendering of an <input type="radio">, <input type="checkbox">, <input type="range">, <input type="text">, <input type="date"> input, <select>, <textarea>,<input type="submit">, and <button>. The right column on the other hand shows the same elements with our above rule applied to them. Notice how this lets us ensure that all of the elements occupy the same amount of space, despite the platform's default rules for each kind of widget.

Diagram showing 2 columns with form elements: radio buttons, checkboxes, sliders, text fields, date pickers, dropdowns, etc.

What may not be apparent via the screenshot is that the radio and checkbox controls still look the same, but they are centered in the 150px of horizontal space provided by the width property. Other browsers may not center the widgets, but they do adhere to the space allotted.


Legend placement

The <legend> element is okay to style, but it can be a bit tricky to control the placement of it. By default, it is always positioned over the top border of its <fieldset> parent, near the top left corner. To position it somewhere else, for example inside the fieldset somewhere, or near the bottom left corner, you need to rely on the positioning.

Take the following example:

To position the legend in this manner, we used the following CSS (other declarations removed for brevity):

fieldset {
  position: relative;
}

legend {
  position: absolute;
  bottom: 0;
  right: 0;
}

The <fieldset> needs to be positioned too, so that the <legend> is positioned relative to it (otherwise the <legend> would be positioned relative to the <body>).

The <legend> element is very important for accessibility - it will be spoken by assistive technologies as part of the label of each form element inside the fieldset - but using a technique like the one above is fine. The legend contents will still be spoken in the same way; it is just the visual position that has changed.

Note: You could also use the transform property to help you with positioning your <legend>. However, when you position it with for example a transform: translateY();, it moves but leaves an ugly gap in the <fieldset> border, which is not easy to get rid of.