Best way to arrange labels and inputs side-by-side

13,057

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:


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:


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>

jsFiddle demo 1


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>

jsFiddle demo 2

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/inputs 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>
Share:
13,057
Michael Arrison
Author by

Michael Arrison

BY DAY: Workin' BY NIGHT: Workin' FOR FUN: What's that?

Updated on June 16, 2022

Comments

  • Michael Arrison
    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>