Form design is a fundamental yet frustrating part of web design and development. Ask anyone who's ever tried to style a <select>
box or align a label consistently in all browsers.
In 2016, I wrote Make Forms Fun with Flexbox which identified how several form difficulties could be solved with Flexbox. A key benefit was HTML source order consistency with the <label>
always following its associated field tag in a container:
<div>
<input id="name" name="name" type="text" />
<label for="name">name</label>
</div>
<div>
<select id="experience" name="experience"><!-- options --></select>
<label for="experience">experience</label>
</div>
<div>
<input id="html" name="html" type="checkbox" />
<label for="html">HTML</label>
</div>
Flexbox could then be used to:
It also became possible to style labels based on the state of their field using adjacent sibling selectors, e.g. apply bold to a label when its associated checkbox is checked:
input:checked + label {
font-weight: bold;
}
Unfortunately, there are a number of problems using Flexbox to layout a form. Flexbox creates a one-dimensional layout where each item follows another and wraps to a new line when necessary. Field/label pairs must be placed in a container elements with display: flex;
applied to guarantee each appears on a new row.
It was also necessary to define a fixed label width, such as 10em
. If a long label required more room, its text would either overflow or resize the element and push the field out of alignment with others.
Finally, forms are normally laid out in a grid. Shouldn't we be using CSS Grid now it's fully supported in all mainstream browsers? Absolutely!
Most CSS Grid articles demonstrate the concepts and may provide graceful degradation fallbacks for older browsers. That approach is ideal when the layout is mostly decorative, e.g. positioning page content, headers, footers and menus. It rarely matters when oldBrowserX shows linear blocks in an unusual order because the page content remains usable.
Form layout is more critical: a misaligned label could lead the user to enter information in the wrong box. For this reason, this tutorial takes a progressive enhancement approach:
The examples below contain very few CSS classes and styling is applied directly to HTML elements. That is not the BEM way, but it is intentional to keep the code clean and understandable without distractions.
You could consider using similar code as the base for all forms on your site.
A typical HTML form can be kept clean since there is no need for containing (<div>
) elements around field/label pairs:
<form action="get">
<fieldset>
<legend>Your web development skillset</legend>
<div class="formgrid">
<input id="name" name="name" type="text" />
<label for="name">name</label>
<select id="experience" name="experience">
<option value="1">1 year or less</option>
<option value="2">2 years</option>
<option value="3">3 - 4 years</option>
<option value="5">5 years or more</option>
</select>
<label for="experience">experience</label>
<input id="html" name="html" type="checkbox" />
<label for="html">HTML</label>
<input id="css" name="css" type="checkbox" />
<label for="css">CSS</label>
<input id="javascript" name="javascript" type="checkbox" />
<label for="javascript">JavaScript</label>
<textarea id="skills" name="skills" rows="5" cols="20"></textarea>
<label for="skills">other skills</label>
<button type="submit">SUBMIT</button>
</div>
</fieldset>
</form>
The only additional element is <div class="formgrid">
. Browsers cannot apply display: grid
or display: flex
to fieldset
elements. That may eventually be fixed but an outer container is currently required.
After some initial font and color styling, the float layout will allocate:
/* fallback 30%/70% float layout */
input, output, textarea, select, button {
clear: both;
float: right;
width: 70%;
}
label {
float: left;
width: 30%;
text-align: right;
padding: 0.25em 1em 0 0;
}
Checkbox and radio buttons are positioned before the label and floated left. Their intrinsic width can be used (width:auto
) but a left margin of 30% is required to align correctly:
button, input[type="checkbox"], input[type="radio"] {
width: auto;
float: left;
margin: 0.5em 0.5em 0 30%;
}
input[type="checkbox"] + label, input[type="radio"] + label {
width: auto;
text-align: left;
}
The layout works in all browsers including IE8+: See the Pen form grid 1: float layout.
A less conscientious developer would go home for the day but this layout has several problems:
The Grid module adds 18 new CSS properties in order to create a layout with rows and columns. Elements within the grid can placed in any row/column, span multiple rows and/or columns, overlap other elements, and be aligned horizontally and/or vertically. There are similarities to Flexbox, but:
It is possibly better to compare CSS Grid with table-based layouts but they're considerably more flexible and require less markup. It has a steeper learning curve than other CSS concepts but you're unlikely to require all the properties and the minimum is demonstrated here. The most basic grid is defined on a containing element:
.container {
display: grid;
}
More practically, layouts also require the number of columns, their sizes, and the gap between rows and columns, e.g.
.container {
display: grid;
grid-template-columns: 10% 1fr 2fr 12em;
grid-gap: 0.3em 0.6em;
}
This defines four columns. Any measurement unit can be used as well as the fr
fractional unit. This calculates the remaining space in a grid and distributes accordingly. The example above defines total of 3fr
on columns two and three. If 600 pixels of horizontal space was available:
1fr
equates to (1fr / 3fr) * 600px
= 200px
2fr
equates to (2fr / 3fr) * 600px
= 400px
A gap of 0.3em
is defined between rows and 0.6em
between columns.
All child elements of the .container
are now grid items. By default, the first child element will appear at row 1, column 1. The second in row 1, column 2, and the sixth in row 2, column 2. It's possible to size rows using a property such as grid-template-rows
but heights will be inferred by the content.
Grid support is excellent. It's not available in Opera Mini but even IE11 offers an older implementation of the specification. In most cases, fallbacks are simple:
display:table
layouts. All Grid properties are ignored.Grid tools and resources:
Firefox and Chrome-based browsers have excellent DevTool Grid layout and visualisation tools.
To progressively enhance the existing form, Grid code will be placed inside an @supports
declaration:
/* grid layout */
@supports (display: grid) {
...
}
This is rarely necessary in most grid layouts. However, this example resets all float paddings and margins; that must only occur when a CSS Grid is being applied.
The layout itself will use a three column design:
where:
The outer container and child field properties:
.formgrid {
display: grid;
grid-template-columns: 1fr 1em 2fr;
grid-gap: 0.3em 0.6em;
grid-auto-flow: dense;
align-items: center;
}
input, output, textarea, select, button {
grid-column: 2 / 4;
width: auto;
margin: 0;
}
grid-column
defines the starting and ending grid tracks. Tracks are the edges between cells so the three-column layout has four tracks:
grid-column: 2 / 4;
positions all fields between tracks 2 and 4 - or inside columns two and three.
The first HTML element is the name <input>
. It spans columns two and three which means column one (track 1 / 2) is empty on that row. By default, the name
label would therefore drop to row 2, column 1. However, by setting grid-auto-flow: dense;
in the container, the browser will attempt to fill empty cells earlier in the grid before progressing to a new row.
Checkboxes and radio buttons can now be set to span tracks 1 to 3 (columns one and two) but align themselves to the right-hand edge using justify-self: end
:
input[type="checkbox"], input[type="radio"] {
grid-column: 1 / 3;
justify-self: end;
margin: 0;
}
Labels on the grid will handle themselves and fit into whichever row cell is empty. However, the default widths and spacing from the float layout are now unnecessary:
label, input[type="checkbox"] + label, input[type="radio"] + label {
width: auto;
padding: 0;
margin: 0;
}
Finally, <textarea>
labels can be vertically positioned at the top of the cell rather than centered:
textarea + label {
align-self: start;
}
The final grid layout: See the Pen form grid 2: grid applied
Unlike floats, the design will not break at small dimensions or require tweaking when different fonts, sizes or labels are added.
It's taken several years to become viable, but CSS Grid is well supported and offers layout possibilities which would have been difficult with floats or flexbox. Forms are an ideal use-case and the resulting CSS is short yet robust.
If you're looking to learn another CSS technique, Grid should be at the top of your list.
18.119.125.7