Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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>
like image 724
Michael Arrison Avatar asked Jul 20 '17 20:07

Michael Arrison


2 Answers

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:

  • Equal Height Columns with Flexbox

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:

  • Is there a “previous sibling” CSS selector?

.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

like image 192
Michael Benjamin Avatar answered Sep 27 '22 18:09

Michael Benjamin


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>
like image 42
Don Avatar answered Sep 27 '22 18:09

Don