Best way to arrange labels and inputs side-by-side
Solution 1
align-items: stretch
Flexbox has a feature commonly known as "equal height columns". This feature enables flex items in the same container to all be equal height.
This feature comes from two initial settings:
flex-direction: row
align-items: stretch
With a slight modification, this feature can become "equal width rows".
flex-direction: column
align-items: stretch
Now a column of flex items will have the width of the longest item.
Reference:
align-content: stretch
An initial setting on a flex container is align-content: stretch
. This setting will distribute rows or columns (depending on flex-direction
) across the length of the container.
In this case, in order to pack two columns to the start of the container, we need to override the default with align-content: flex-start
.
References:
- Remove space (gaps) between multiple lines of flex items when they wrap
- How does flex-wrap work with align-self, align-items and align-content?
flex-direction: column
, flex-wrap: wrap
and height
Since you have a preferred HTML structure (with all form elements logically ordered in one container), flex items will need to wrap in order to form a column of labels and a column of inputs.
So we need to override flex-wrap: nowrap
(the default) with wrap
.
Also, the container must have a fixed height so that items know where to wrap.
References:
- Is it possible for flex items to align tightly to the items above them?
- Make a div span two rows in a grid
The order
property
The order
property is needed to properly align labels and inputs across columns.
Reference:
.aligned_form {
display: flex;
flex-flow: column wrap;
align-content: flex-start;
height: 75px;
}
label[for='a'] { order: -3; }
label[for='b'] { order: -2; }
label[for='c'] { order: -1; }
label, input {
height: 25px;
padding: 5px;
box-sizing: border-box;
}
<!-- No changes to the HTML. -->
<div class='aligned_form'>
<label for='a'>short_label</label>
<input type='text' id='a' placeholder="short_label">
<label for='b'>medium_label</label>
<input type='text' id='b' placeholder="medium_label">
<label for='c'>very_extra_long_label</label>
<input type='text' id='c' placeholder="very_extra_long_label">
</div>
Changing the HTML Structure
If you can change the HTML, here's an alternative solution.
- One primary flex container with two flex item columns (labels and inputs)
- Add
flex: 1
to the inputs column so that it consumes all free space in the row and packs the labels column to the width of its longest item
form {
display: flex;
}
form > div {
display: flex;
flex-direction: column;
}
form > div:last-child {
flex: 1;
}
label, input {
height: 25px;
padding: 5px;
box-sizing: border-box;
}
<form>
<div>
<label for='a'>short_label</label>
<label for='b'>medium_label</label>
<label for='c'>very_extra_long_label</label>
</div>
<div>
<input type='text' id='a' placeholder="short_label">
<input type='text' id='b' placeholder="medium_label">
<input type='text' id='c' placeholder="very_extra_long_label">
</div>
</form>
Solution 2
I know you said you don't want CSS Grid layout, but I'm going to add it here for documentation purposes. As I think it's the cleanest of the solutions if you don't mind the support problems.
You can always set a fixed width for the inputs by replacing 1fr
with a pixel or other value. Also align-self
is centering the label and inputs vertically, if you'd prefer bottom aligned change center
to end
.
This keeps the HTML super clean too!
.container{
display: grid;
grid-gap: 10px;
grid-template-columns: [label] auto [input] 1fr;
}
input {
grid-column: input;
align-self: center;
}
label {
grid-column: label;
align-self: center;
}
<div class="container">
<label for='a'>short_label</label>
<input type='text' id='a'>
<label for='b'>medium_label</label>
<input type='text' id='b'>
<label for='c'>very_extra_long_label</label>
<input type='text' id='c'>
</div>
Solution 3
I think @Michael_B's solution is great, but another way would be to create a flex row with 2 columns, then make each column a flex column. Just set a height on the label/input
s so they're consistent, and adjust the margins.
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<style>
label,input {
height: 25px;
margin:0 0 .5em;
}
label {
margin-right: .5em;
}
</style>
<div class="container">
<div class="row">
<div class="d-flex flex-column">
<label for='a'>short_label</label>
<label for='b'>medium_label</label>
<label for='c'>very_extra_long_label</label>
</div>
<div class="d-flex flex-column">
<input type='text' id='a'>
<input type='text' id='b'>
<input type='text' id='c'>
</div>
</div>
</div>
![Michael Arrison](https://lh5.googleusercontent.com/-L6QtavaILSk/AAAAAAAAAAI/AAAAAAAAAlE/c2QrAj9cXFg/photo.jpg?sz=256)
Michael Arrison
BY DAY: Workin' BY NIGHT: Workin' FOR FUN: What's that?
Updated on June 16, 2022Comments
-
Michael Arrison about 2 years
Without the use of tables or
display:table
, is it possible to have a flexible width area for labels to the left of inputs? I would also like to avoid grid layout for browser compatibility reasons. I'd like css to take care of this:short_label input_box____________________ tiny_label input_box____________________ medium_label input_box____________________
And then also handle larger labels accordingly:
short_label input_box__________ medium_label input_box__________ very_extra_long_label input_box__________
But I do not want:
short_label input_box__________ tiny_label input_box__________ medium_label input_box__________
So the first column needs to have a flexible width, and the second column needs to grow to fill space. My html would ideally look something like this, but if necessary, "row" divs can be added. I feel like there is a flex answer, but maybe not since all the rows needs to be aligned.
<div class='aligned_form'> <label for='a'>short_label</label> <input type='text' id='a'> <label for='b'>medium_label</label> <input type='text' id='b'> <label for='c'>very_extra_long_label</label> <input type='text' id='c'> </div>