The problem
The question
How should I style a form, so that I get my desired 3 column layout under the following constraints:- The layout works, even if the the html files are not static (dynamic number of checkboxes, ...)
- Decent browser support (bonus points if edgeHTML 15 supports the solution)
- The fieldsets stay in the DOM, because removing them would have side effects (radio buttons would not be grouped, checkboxes and radios would not be disabled anymore, ...)
- Prefered: No changes in html files needed
- Prefered: HTML and CSS only
If this is not possible with a 3 column design, a 2 column design would be a possible alternative.
The basics
So basically I have a form like this:
<form>
<h2>A title</h2>
<label>
Some text input
<input type="text">
</label>
<label>
Some more text
<textarea></textarea>
</label>
<fieldset>
<legend>Some options</legend>
<label>
<input type="checkbox">
1
</label>
<label>
<input type="checkbox">
2
</label>
<label>
<input type="checkbox">
3
</label>
</fieldset>
</form>
and would like to get a layout like this:
+-----------------+------------------+------------------+ | A title | +-----------------+------------------+------------------+ | Some text input | ( ) | +-----------------+------------------+------------------+ | Some more text | [ ] | +-----------------+------------------+------------------+ | Some options | [] 1 | [] 3 | + +------------------+------------------+ | | [] 2 | | +-----------------+------------------+------------------+
The approaches
So I came up with two main approaches because I don't want to change my HTML files just for styling purposes:
Apply the grid on the form
If I style my form with display: grid;
and my labels and fieldsets with display: contents;
, I would have one grid for all elements in the grid and can be sure, that no individual grid styles different, but I will encounter a bug in most browsers, which renders the content inaccessible.
+
- Only 1 grid for all elements
−
- Bug which renders the content inaccessible
- Styling as margins of fieldsets and labels are lost
- Need to take care of custom elements, because in this project, we have a lot of custom components, that could destroy the layout
So I would have something like
fieldset,
fieldset legend,
fieldset label,
fieldset input{
border: none;
margin: 0;
padding: 0;
}
form {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
form > h2 {
grid-column: 1 / 4;
}
form > label,
form > fieldset {
display: contents;
}
form > label > input[type="text"],
form > label > textarea {
grid-column: 2 / 4;
}
form > fieldset label:nth-of-type(odd) {
grid-column: 2;
}
form > fieldset label:nth-of-type(even) {
grid-column: 3;
}
<form>
<h2>A title</h2>
<label>
Some text input
<input type="text">
</label>
<label>
Some more text
<textarea></textarea>
</label>
<fieldset>
<legend>Some options</legend>
<label>
<input type="checkbox">
1
</label>
<label>
<input type="checkbox">
2
</label>
<label>
<input type="checkbox">
3
</label>
</fieldset>
</form>
Apply the grid on the labels and fieldsets
On the other side, I tried to apply the grid on the labels and fieldsets directly, so that I don't have to care about elements, that span an entire column, but I encounter a bug in chrome and new edge browsers, that display: grid;
doesn't work on fieldsets. As you can see, in the codepen above, I could get my desired layout working, with the mentioned workaround, but I had to change my HTML files.
+
- no need to care about nested elements
- Decent support
−
- Individual grids, which could potentially behave differently
- Need to make changes in HTML files, just for styling reasons
So I have something like
fieldset,
fieldset legend,
fieldset label,
fieldset input{
border: none;
margin: 0;
padding: 0;
}
form > fieldset,
form > label {
margin-top: 2 * $defaultSpacing;
}
form > fieldset,
form > label {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
form > fieldset legend {
grid-column: 1;
}
form > fieldset label:nth-of-type(odd) {
grid-column: 2;
}
form > fieldset label:nth-of-type(even) {
grid-column: 3;
}
form > label input[type="text"],
form > label textarea,
form > label * {
grid-column: 2 / 4;
}
<form>
<h2>A title</h2>
<label>
Some text input
<input type="text">
</label>
<label>
Some more text
<textarea></textarea>
</label>
<fieldset>
<legend>Some options</legend>
<label>
<input type="checkbox">
1
</label>
<label>
<input type="checkbox">
2
</label>
<label>
<input type="checkbox">
3
</label>
</fieldset>
</form>
Or the workaround:
fieldset,
fieldset legend,
fieldset label,
fieldset input{
border: none;
margin: 0;
padding: 0;
}
form > fieldset .fieldset,
form > label {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
form > fieldset legend {
grid-column: 1;
}
form > fieldset label:nth-of-type(odd) {
grid-column: 2;
}
form > fieldset label:nth-of-type(even) {
grid-column: 3;
}
form > label input[type="text"],
form > label textarea,
form > label * {
grid-column: 2 / 4;
}
<form>
<h2>A title</h2>
<label>
Some text input
<input type="text">
</label>
<label>
Some more text
<textarea></textarea>
</label>
<fieldset>
<div class="fieldset">
<legend>Some options</legend>
<label>
<input type="checkbox">
1
</label>
<label>
<input type="checkbox">
2
</label>
<label>
<input type="checkbox">
3
</label>
</div>
</fieldset>
</form>