Alex Turpin
Alex Turpin

Reputation: 47776

Aligning element height across different columns

I have some framework generated HTML that I am trying to style.

.row {
  display: flex;
  flex-direction: row;
}

.field {
  display: flex;
  flex-direction: column;
}
<div class="row">
  <div class="field">
    <label>Label</label>
    <input type="text">
  </div>
  <div class="field">
    <label>Label<br>over two lines</label>
    <input type="text">
  </div>
  <div class="field">
    <label>Label</label>
    <input type="text">
    <span>error</span>
  </div>
</div>

I want the labels, who are always the first child of every field, to be aligned vertically and occupy the same height, as if they were in a table row. However, I cannot change the HTML structure into rows.

I can't just set the alignment to flex-end because I can have columns with more elements after the label, such as an error message.

Is this possible purely with CSS?

Upvotes: 0

Views: 685

Answers (3)

Ezra Siton
Ezra Siton

Reputation: 7741

If the structure is always:

  • label
  • input
  • span (0, 1 or more).

In my CSS I used position: absolute for the span "immediately after" the input.

Without JavaScript not DRY (No choice), but it is very easy to select and offset each span like this.

.field span:nth-of-type(1){ /* offset value 1 */ }
.field span:nth-of-type(2){ /* offset value 2 */ }

How to align the spans? (One or More)

For example: offset of (n) * 1.5 (For span of 1em font size and 1.5 line height).

Snippet

.row *{
  border: 1px solid lightgray;
}
.row {
  display: flex;
  flex-direction: row;
  
}

.row label{
  padding-bottom: 4px;
  font-weight: bold;
  display: block;
}

.row .field {
  position: relative;
  display: flex;
  flex-direction: column;
}

.row input{
  background: lightgray;
  margin-top: auto;
  border: 1px solid black;
}

.row .field span{
  position: absolute;
  right: 0px;
  left: 0px;
  /* type */
  line-height: 1.5;
  font-size: 1rem;
}

.row .field span:nth-of-type(1) {
  bottom: -1.5rem;
}

.row .field span:nth-of-type(2) {
  bottom: -3rem;
}

[error]{
  color: red;
  font-style: italic;
}
<div class="row">
  <div class="field">
    <label>Label</label>
    <input placeholder="name" type="text">
    <span error>error</span>
  </div>
  <div class="field">
    <label>Label<br>over two lines</label>
    <input placeholder="email" type="email">
  </div>
  <div class="field">
    <label>Label<br>over three <br>lines<br></label>
    <input placeholder="hello" type="text">
    <span error>error</span>
    <span>One more error</span>
  </div>
</div>

** position absolute could make overlap of items. On Mobile it is better to use position static (Anyway the fields not one next to each other).

Select any element after input

Without Javascript - Same logic. One way - use * selector:

input + *{ /* first node after input offset value */ }

input + * + *{ /* second node after input offset value */ }

Upvotes: 1

Ahron M. Galitzky
Ahron M. Galitzky

Reputation: 2261

They are theoretically equal height already. However, flex by default will be flex-start which means if something like in your case the center label overflows, it will keep all other flex children on the top.

To push them to the bottom instead which will auto align all inputs to the bottom you must set on the parent align-items: flex-end; which will push all children to the end.

.row {
  display: flex; 
  align-items: flex-end;
  gap:5px;
}

.field {
  display: flex;
  flex-direction: column; 
}

UPDATE! Since the original question updated, here's is an updated answer.

If this is the case I would change the field class to a grid instead of using flex. grid is more flexible for these kind of scenarios.

Change CSS as follows

.row {
  display: flex;
  flex-direction: row;
}

.field {
  display: grid;
  grid-template-rows:1fr auto 1fr ;    
  align-items: flex-end;
}

span {
  margin-bottom:auto;
}

Upvotes: 1

itaycode
itaycode

Reputation: 144

you can align the row

.row {
align-items: flex-end;
}

or you can try to align the field, like that

.field {
justify-content: flex-end;
}

check both of them.

Upvotes: 1

Related Questions