CSS: taming form elements: common design patterns and strategies

CSS: taming form elements: common design patterns and strategies

CSS and form elements: common design patterns used by web developers to stylize these elements.

Before 1998, the birth year of CSS Level 2, form elements were already widely implemented in all major browsers. The CSS 2 specifications did not address the problem of how form elements should be presented to users. Since this kind of elements are part of the UI of every web document, CSS preferred to leave the visual layout of these elements to the default stylesheet of web browsers. Through the years, this lack of details in the CSS specifications forced web developers to produce a significant amount of tests and examples whose primary goal was to reduce form elements to a common visual denominator in order to get a cross-browser rendering of elements such as input, select, fieldset, legend and textarea. In this article we will cover some of the CSS patterns used by web developers to tame the visual layout of form elements.

Roger Johansson tests

Back in 2004, and later on in 2007, Roger Johansson created a complete test suite on form elements and CSS. These seminal tests, that can be found at http://www.456bereastreet.com/archive/200701/styling_form_controls_with_css_revisited/, lead to a frustrating conclusion that Roger summarizes with the following words:

So what does this experiment show? Like I already stated, it shows that using CSS to style form controls to look exactly the same across browsers and platforms is impossible. It also shows that most browsers ignore many CSS properties when they are applied to form controls.

Despite the underlying truth of these conclusions, web developers continued to extensively test CSS styles on form elements to find the Holy Grail or at least a reasonable compromise between browser's default rendering and author's styles.

The default model

CSS 2.1 specifications state in their proposed default stylesheet for HTML4 that form elements such as textarea, input and select are inline-block elements:


textarea,
input, select   { display: inline-block }

On the contrary, the form and fieldset elements are block-level elements:


fieldset, form { display: block }

The default model proposed by the CSS specifications stops here. All other visual aspects of form elements rely on the browser's default stylesheet. However, the above rules indicate that:

  • Inline-block elements can be stylized using an inline formatting context. This implies the use of CSS properties such as line-height and vertical-align to control the height of the box and its vertical alignment. Padding and margins can also be applied to define the outer and inner spacing of the affected box. Also, inline-block elements accept widths and heights because they share the block formatting model.
  • Block-level elements can be styled using the well-known block formatting context. However, problems arise with the fieldset and legend elements because legend entirely relies on the default styles provided by web browsers.

How web developers manage these problems?

Defining dimensions

Web developers soon noticed an odd handling of inline-block elements by web browsers when it comes to define dimensions. Defining an height often leads to unexpected results:


input, select {
	width: 120px;
	height: 32px;
}

Developers tried to fix this problem by turning these elements into block-level elements:


input, select {
	width: 120px;
	height: 32px;
	display: block;
}

Results are still poor except for the textarea element. A common pattern to solve this problem is to avoid the height property and use instead the font-size and padding properties.

Browsers do not use the same font family and size on these elements, so the first thing to do is to normalize them:


input, select {
	width: 120px;
	font: 1em Arial, sans-serif;
}

Once normalized the font in use, you can add some padding to add some inner spacing to the element's box:


input, select {
	width: 120px;
	font: 1em Arial, sans-serif;
	padding: 3px 6px;
}

input elements and textarea also show a border that affects their box model:


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

input elements of type button and submit have an additional padding set by web browsers, so a common practice is normalizing it:


input[type="button"],
input[type="submit"] {
	padding: 2px;
}

The problem with this approach is that web browsers also apply vendor-specific properties to these elements so that our padding is not always able to normalize this property.

Padding is also used on the fieldset and legend elements but with different results:

  • When applied to fieldset, zeroing padding resets the default indentation of the legend elements in some browsers (not in IE).
  • When applied to legend, zeroing padding has the effect to make this element shrink.

Select boxes, checkboxes and radio buttons can be normalized with good results only with a few properties, namely:

  • font-family
  • font-size
  • width (on select boxes)
  • padding

Trying to apply other properties to this group of elements often leads to inconsistent results across browsers.

Alignment

Form elements can be vertically or horizontally aligned. They can be laid out on the same line or as a group of boxes on multiple rows. To align them on the same line you can follow two approaches:

  1. Use floating
  2. Use the default inline-block context on some of these elements.

When you use floating, elements are automatically turned into block-level elements. This implies that now form elements are subject to the nine rules that govern floated elements.

With floats the main problem is to correctly achieve a good vertical alignment on the current line. In this case, using vertical margins or padding is a common practice:


input, select {
	width: 120px;
	float: left;
	margin-top: 0.4em;
}

This approach works when you do not have to align boxes with text, such as the content of a label element. In this case, you can use relative positioning, padding or margins on the element which contains solely text:


label {
	float: left;
	padding-top: 0.4em;
	width: 5em;
	margin-right: 1em;
}

Another problem arises with buttons. In this particular case, when you have a button whose dimensions are greater than other elements, you can force its vertical alignment with relative positioning:


input[type="submit"] {
	float: left;
	width: 90px;
	position: relative;
	top: 0.4em;
}

This approach with relative positioning also applies to checkboxes and radio buttons. Also, relative positioning can be applied to normalize the left indentation of the legend element within a fieldset. The only difference in this case is that you use the left property instead of top.

When you use an inline formatting context, you can rely on the vertical-align property to vertically align elements:


label, input[type="text"] {
	vertical-align: middle;
	margin-right: 1em;
}

Good results can be achieved when you combine this property with the line-height property. However, this property must be set on the parent element. If you set this property directly on the form elements, this will affect their computed height:


div.form-row {
	line-height: 1.4;
}

Using a declared height on the parent element is also effective when combined with the same value of line-height:


div.form-row {
	line-height: 1.8;
	height: 1.8em;
}

Also, in an inline formatting context you can use the text-align property on the parent element to align elements to right, left or center them.

File inputs

File inputs are a completely different problem. Historically, this kind of form elements has been always protected by the default algorithms of web browsers in order to prevent any possible security problem. Major problems arise when you try to change their default appearance.

Web developers usually wrap this element in a container and hide the input element by adjusting its opacity:


div.file {
	width: 150px;
	height: 50px;
	overflow: hidden;
	background: url(upload-btn.png) no-repeat;
}

div.file input[type="file"] {
	display: block;
	width: 150px;
	height: 50px;
	opacity: 0;
}

However, in some browsers setting dimensions on both elements produces different results. For example, the default layout provided by Gecko-based browsers also includes a text field where the name of the chosen file appears before the upload. Other browsers don't provide this feature and, consequently, button's dimensions are calculated differently.

Conclusions

Totally taming form elements is impossible due to the lack of details in the CSS specifications and the default styles applied by web browsers. However, by following some common practices is possible to reduce (but not eliminate) the differences and achieve good visual results.