Set equal height of specific elements using CSS only? - css

I have 2 columns of data, in each column I have a paragraph and an image. I am wondering if there is a CSS-only method of making the paragraphs have the same height so that the images line up. I know I could loop through with JavaScript to find the tallest paragraph and set the height of the other to that, but I'm hoping to find a CSS solution if possible.
Additionally, I cannot modify the DOM structure.
.container {
display:flex;
}
.col {
padding:0 15px;
}
<div class="container">
<div class="col">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi fringilla ultrices nulla ac tempus.</p>
<img src="http://placehold.it/250x100" />
</div>
<div class="col">
<p>Nam mauris mi, eleifend et rutrum at, dignissim nec elit. Aliquam lacinia tincidunt leo, et pellentesque nibh ultricies ut. Etiam elit purus, blandit ac consequat a, dictum a dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<img src="http://placehold.it/250x100" />
</div>
</div>

I'm not sure Arup's example does what you expect - images still don't align. The answer has been updated.
Here's what should work for you:
.container {
display:flex;
}
.col {
padding:0 15px;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.col p {
flex: 1;
}
We set the main axis to be our y axis by using flex-direction and we justify the content such that everything in a col div sits at the bottom of this axis. Setting flex: 1; on the paragraph allows the paragraph to grow in order to occupy the free space in the div.

You can use flexbox for that, too. See the comments in CSS (borders added just for clarity):
.container {
display: flex;
}
.col {
padding:0 15px;
display: flex;
flex-direction: column; /* causes the image to go under the paragraph */
align-items: flex-start; /* images would stretch over the entire .col width without this */
border: 1px solid red;
}
.col p {
flex: 1; /* makes the paragraph grow to the tallest height available */
border: 1px solid blue;
}
<div class="container">
<div class="col">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi fringilla ultrices nulla ac tempus.</p>
<img src="http://placehold.it/250x100" />
</div>
<div class="col">
<p>Nam mauris mi, eleifend et rutrum at, dignissim nec elit. Aliquam lacinia tincidunt leo, et pellentesque nibh ultricies ut. Etiam elit purus, blandit ac consequat a, dictum a dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<img src="http://placehold.it/250x100" />
</div>
</div>

Add below styles.
.container {
display:flex;
}
.col {
padding: 5px 15px;
border: 1px solid #999;
display: flex;
flex-direction: column;
justify-content: space-between;
}
Live Example.

Related

Flexbox equal heights inside iframe not working?

I'm having a problem with a flexbox layout inside an iframe. The layout works fine in a normal view, but once placed inside an iframe (like here in this snippet or codepen etc.), the flex children of the second flex column doesn't have equal heights anymore.
I can't really show you the difference, because all snippets are automatically placed in iframes...
*,
*::before,
*::after {
box-sizing: border-box;
}
.b-block {
margin: 0 auto;
max-width: 1000px;
padding: 10px;
}
.b-block__wrap {
position: relative;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin: 0 -10px;
}
.b-block__item,
.b-block__item--large {
width: 100%;
flex: 0 0 100%;
max-width: 100%;
display: flex;
flex-direction: column;
padding: 0 10px;
margin-bottom: 10px;
justify-content: space-between;
}
#media (min-width: 992px) {
.b-block__item,
.b-block__item--large {
flex: 0 0 50%;
max-width: 50%;
margin-bottom: 0;
}
}
.b-article,
.b-article--large {
display: flex;
flex: 1; /* works in a div but not in an iframe */
/* flex: 0 1 auto; // works but without equal heights */
padding: 10px;
font-size: 16px;
background: #eee;
}
.b-article:not(:last-child) {
margin-bottom: 10px;
}
.b-article--large {
min-height: 300px;
}
<section class="b-block">
<div class="b-block__wrap">
<div class="b-block__item--large">
<article class="b-article--large">Vivamus erat sit habitasse nisi quam penatibus proin nascetur hac volutpat porttitor ad condimentum mauris aenean fames lectus tincidunt inceptos
</article>
</div>
<div class="b-block__item">
<article class="b-article">Convallis at posuere leo convallis. Sed blandit augue vitae augue scelerisque bibendum. Vivamus sit amet libero turpis, non venenatis urna.
</article>
<article class="b-article">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus quis lectus metus, at posuere neque. Sed pharetra nibh eget orci convallis at posuere leo convallis. Sed blandit augue vitae augue scelerisque bibendum. Vivamus sit amet libero turpis, non venenatis urna. In blandit, odio convallis suscipit venenatis, ante ipsum cursus augue.
</article>
<article class="b-article">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus quis lectus metus, at posuere neque.
</article>
</div>
</div>
</section>
Is there any workaround for this?
I already tried:
• Wrapping the b-article in the second b-block__item in an extra div
• Setting the flex property of the b-article in the second b-block__item to flex: 0 1 33%;
but no luck yet.
Is this even possible in an iframe?
Thanks!

How can I use text-align-last except when I have only one line of text?

I would like to align text to the center, but last line to the left:
section {
text-align: center;
text-align-last: left;
}
It works well, but what if there is only one line? In this case, I would like to align text to the center, which doesn't work, because the first line is also last line. Is there any selector for elements with only multiline text or selector for first line (not pseudo-element :first-line)?
Live Example:
section {
text-align: center;
text-align-last: left;
width: 300px;
}
<strong>This works:</strong>
<section>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam aliquam turpis consectetur diam fringilla, vel ultrices sapien bibendum. Maecenas pulvinar in nisl at mattis. Duis nisi risus, rhoncus sit amet ornare malesuada.</section>
<strong>Next line should be centered (something like text-align-first?):</strong>
<section>Lorem ipsum dolor sit amet.</section>
Finally, I invented another solution. I will set all the elements display: inline-block; and place them into the container with text-align: center;. If there are multiple lines, nothing will change. But if there is only one line, .child will have smaller width and is centered.
.container {
text-align: center;
width: 300px;
}
.child {
display: inline-block;
text-align: center;
text-align-last: left;
width: auto;
}
<section class="container">
<strong>This works:</strong>
<section class="child">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam aliquam turpis consectetur diam fringilla, vel ultrices sapien bibendum. Maecenas pulvinar in nisl at mattis. Duis nisi risus, rhoncus sit amet ornare malesuada.</section>
<strong>Next line should be centered</strong>
<section class="child">Lorem ipsum dolor sit amet.</section>
</section>
Overriding text-align-last on the desired element with the selector ::first-line does the job.
section {
text-align: center;
text-align-last: left;
width: 300px;
}
section::first-line {
text-align-last: center;
width: 300px;
}
<strong>This works:</strong>
<section>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam aliquam turpis consectetur diam fringilla, vel ultrices sapien bibendum. Maecenas pulvinar in nisl at mattis. Duis nisi risus, rhoncus sit amet ornare malesuada.</section>
<strong>Next line should be centered (something like text-align-first?):</strong>
<section>Lorem ipsum dolor sit amet.</section>
There does not appear to be a way to override text-align-last for when a block container has only one formatted line and text-align is center.
The spec says that text-align-last can be overridden, but only when text-align is start end. This does not seem to apply to any of the other values for text-align.
There are no selectors for elements with zero, one, or n formatted lines, nor is there a ::last-line counterpart to ::first-line. So it seems you're out of luck doing this with pure CSS. You're going to have to use a script to identify section elements with only one line (i.e. section elements whose text isn't wrapping), attach a class name to those elements, and apply text-align-last: center (or auto) to them.
I found using width: max-content, max-width:100%, and centering the element to work very well. If it's less than one line, the element will shrink to the size of the line and will be centered by a margin: 0 auto; If it extends beyond one line, it will stretch the element to the point where it hits the max-width:100% rule and then continue to the next line. Now that the block is at the max width, the text inside will follow the text-align rule.
.justify_unless_one_line{
text-align: justify;
margin: 0 auto;
width: max-content;
max-width: 100%;
}
.container{
padding: 10px;
width: 300px;
border: 1px solid black;
margin: 0 auto;
}
<section>
<div class='container'>
<h2>Single Line</h2>
<p class="justify_unless_one_line">
This is an example of one line
</p>
<h2>Many Lines</h2>
<p class="justify_unless_one_line">
This is an example of many lines. This is an example of many lines. This is an example of many lines. This is an example of many lines. This is an example of many lines. This is an example of many lines.
</p>
</div>
</section>
first line selector text-align: center;
section:nth(1){
text-align: center;
}

CSS flexbox: 3 columns into 2 + 1

I am currently converting an old page away from using a <table> layout and have been trying to implement the behaviour using flexbox. The aim is to have 3 columns, the first two centred horizontally and vertically, with the last column containing a lot of text, which is displaying ok.
I am trying to use a media query to wrap the third column under the first two when the browser width is small as follows:
The code snippet below achieves the correct layout but will obviously fail on small widths and just produce a single column:
I am guessing this needs flex-wrap somehow?
Are multiple rows also possible with a single container row? Or is it best to have one container per row?
.row {
display: flex;
justify-content: center;
padding: 5px;
margin: 2px;
border: 1px solid #000;
font-size: 0.7em;
}
.col1, .col2, .col3 {
background-color: #999;
padding: 8px;
margin: 2px;
}
.col1, .col2 {
flex: 1;
/* Horizontal centring */
text-align: center;
/* Add vertical centring */
display: flex;
flex-direction: column;
justify-content:center;
}
.col3 {
flex: 3;
background-color:#fff;
}
#media(max-width: 400px){
.row {display: block;}
}
<div class="row">
<div class="col1">
Column 1
</div>
<div class="col2">
Column 2
</div>
<div class="col3">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit tortor vel orci pulvinar, eu euismod sem maximus. Mauris tempus sem eget massa tristique, ut maximus tortor volutpat. In efficitur.
</div>
</div>
<div class="row">
<div class="col1">
Column 1
</div>
<div class="col2">
Column 2
</div>
<div class="col3">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit tortor vel orci pulvinar, eu euismod sem maximus. Mauris tempus sem eget massa tristique, ut maximus tortor volutpat. In efficitur.
</div>
</div>
You can get rid of the row element with some tricks, specially if you know beforehand the width of the col1 and col2 elements. Notice the width of the col3 element is quite artificial:
.row {
display: flex;
flex-wrap: wrap;
width: 95%;
border: solid 1px red;
}
.col1,
.col2 {
background-color: lightblue;
padding: 10px;
background-clip: content-box;
}
.col3 {
flex-basis: calc(100% - 194px);
padding: 5px;
}
.col1,
.col2,
.col3 {
margin: 10px 0px;
}
.col1 {
border: solid 1px black;
border-right-width: 0px;
margin-left: 10px;
}
.col2 {
border: solid 1px black;
border-left-width: 0px;
border-right-width: 0px;
}
.col3 {
border: solid 1px black;
border-left-width: 0px;
}
<div class="row">
<div class="col1">
Column 1
</div>
<div class="col2">
Column 2
</div>
<div class="col3">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit tortor vel orci pulvinar, eu euismod sem maximus. Mauris tempus sem eget massa tristique, ut maximus tortor volutpat. In efficitur.
</div>
<div class="col1">
Column 1
</div>
<div class="col2">
Column 2
</div>
<div class="col3">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit tortor vel orci pulvinar, eu euismod sem maximus. Mauris tempus sem eget massa tristique, ut maximus tortor volutpat. In efficitur.
</div>
<div class="col1">
Column 1
</div>
<div class="col2">
Column 2
</div>
<div class="col3">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit tortor vel orci pulvinar, eu euismod sem maximus. Mauris tempus sem eget massa tristique, ut maximus tortor volutpat. In efficitur.
</div>
<div class="col1">
Column 1
</div>
<div class="col2">
Column 2
</div>
<div class="col3">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit tortor vel orci pulvinar, eu euismod sem maximus. Mauris tempus sem eget massa tristique, ut maximus tortor volutpat. In efficitur.
</div>
<div class="col1">
Column 1
</div>
<div class="col2">
Column 2
</div>
<div class="col3">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit tortor vel orci pulvinar, eu euismod sem maximus. Mauris tempus sem eget massa tristique, ut maximus tortor volutpat. In efficitur.
</div>
<div class="col1">
Column 1
</div>
<div class="col2">
Column 2
</div>
<div class="col3">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit tortor vel orci pulvinar, eu euismod sem maximus. Mauris tempus sem eget massa tristique, ut maximus tortor volutpat. In efficitur.
</div>
</div>
Also, a solution for the normal layout, including 40% flex-basis for col1 and col2. They should be 50%, but since there are some margins and paddings, it's better to be on the low side and the the flex-grow compensate it
.row {
display: flex;
justify-content: center;
padding: 5px;
margin: 2px;
border: 1px solid #000;
font-size: 0.7em;
}
.col1,
.col2,
.col3 {
background-color: #999;
padding: 8px;
margin: 2px;
}
.col1,
.col2 {
flex: 1;
/* Horizontal centring */
text-align: center;
/* Add vertical centring */
display: flex;
flex-direction: column;
justify-content: center;
}
.col3 {
flex: 3;
background-color: #fff;
}
#media(max-width: 400px) {
.row {
flex-wrap: wrap;
}
.col1,
.col2 {
flex-basis: 40%;
}
.col3 {
flex-basis: 80%;
}
}
<div class="row">
<div class="col1">
Column 1
</div>
<div class="col2">
Column 2
</div>
<div class="col3">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit tortor vel orci pulvinar, eu euismod sem maximus. Mauris tempus sem eget massa tristique, ut maximus tortor volutpat. In efficitur.
</div>
</div>
<div class="row">
<div class="col1">
Column 1
</div>
<div class="col2">
Column 2
</div>
<div class="col3">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit tortor vel orci pulvinar, eu euismod sem maximus. Mauris tempus sem eget massa tristique, ut maximus tortor volutpat. In efficitur.
</div>
</div>
You would give your .row definition flex-wrap: wrap; and set a min-width value for column 3. When it gets below that width it will drop down as you have indicated above.

Is this flexbox layout possible without adding more to the HTML structure?

I was wondering if the layout I draw on the following image is possible using flexbox and this HTML structure:
<div class="box">
<img src="url..." alt="" />
<h5>Lorem Ipsum</h5>
<p>Lorem Ipsum Dolor Sit Amet...</p>
</div>
It would be great if this could be done with flexbox and without adding more boxes inside.
For fixed image width and height, it's possible. The main idea is in the following snippet.
.box {
display: flex;
flex-direction: column;
flex-wrap: wrap;
align-content: space-between;
height: 300px;
}
.img {
width: 300px; height: 300px;
}
h5, p {
/* 100% - image width - margin between */
width: calc(100% - 300px - 16px);
}
Since the height of the parent is the same as the height of the image, the content overflows and gets wrapped to the right. Then we have to manually set the width because it's otherwise going to 100% of the parent.
.box {
background-color: #ddd;
padding: 16px;
margin-bottom: 24px;
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 300px;
align-content: space-between;
}
.img {
background-color: #333;
color: #ddd;
width: 300px;
height: 300px;
}
h5, p {
padding: 0;
margin: 0;
background-color: #ccc;
/* 100% - image width - margin between */
width: calc(100% - 300px - 16px);
}
h5 {
font-size: 18px;
margin-bottom: 8px;
}
.box:nth-child(even) .img {
order: 3;
}
<div class="box">
<div class="img"><img></div>
<h5>Lorem Ipsum</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis cursus hendrerit. Suspendisse potenti. Aliquam posuere ex ut lacus euismod dictum. Proin et ligula posuere leo viverra tempor a in tellus</p>
</div>
<div class="box">
<div class="img"><img></div>
<h5>Lorem Ipsum</h5>
<p>Lorem Ipsum Dolor Sit Amet...</p>
</div>
<div class="box">
<div class="img"><img></div>
<h5>Lorem Ipsum</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis cursus hendrerit. Suspendisse potenti. Aliquam posuere ex ut lacus euismod dictum. Proin et ligula posuere leo viverra tempor a </p>
</div>
Full-height paragraph
You might want to extend your paragraph all the way to the bottom (maybe add some links there). It's easy to extend it so you can turn p into flexbox to snap something to the bottom, for example (this example not in the demo below, you can only notice this by the gray background).
.box {
/* ... */
justify-content: space-between;
}
p {
flex-grow: 1;
}
.box {
background-color: #ddd;
padding: 16px;
margin-bottom: 24px;
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 300px;
align-content: space-between;
justify-content: space-between;
}
.img {
background-color: #333;
color: #ddd;
width: 300px;
height: 300px;
}
h5, p {
padding: 0;
margin: 0;
background-color: #ccc;
/* 100% - image width - margin between */
width: calc(100% - 300px - 16px);
}
h5 {
font-size: 18px;
margin-bottom: 8px;
}
p {
flex-grow: 1;
}
.box:nth-child(even) .img {
order: 3;
}
<div class="box">
<div class="img"><img></div>
<h5>Version 2</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis cursus hendrerit. Suspendisse potenti. Aliquam posuere ex ut lacus euismod dictum. Proin et ligula posuere leo viverra tempor a in tellus</p>
</div>
<div class="box">
<div class="img"><img></div>
<h5>Lorem Ipsum</h5>
<p>Lorem Ipsum Dolor Sit Amet...</p>
</div>
<div class="box">
<div class="img"><img></div>
<h5>Lorem Ipsum</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis cursus hendrerit. Suspendisse potenti. Aliquam posuere ex ut lacus euismod dictum. Proin et ligula posuere leo viverra tempor a in tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis cursus hendrerit. Suspendisse potenti. </p>
</div>
Both versions above breaks when the text cannot fit (try changing browser window size).
Doesn't show all, but never breaks
You can set the max-height for the paragraph if you don't want it to break and clip the text which cannot fit with overflow: hidden.
p {
max-height: 250px;
overflow: hidden;
}
.box {
background-color: #ddd;
padding: 16px;
margin-bottom: 24px;
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 300px;
align-content: space-between;
justify-content: space-between;
}
.img {
background-color: #333;
color: #ddd;
width: 300px;
height: 300px;
}
h5, p {
padding: 0;
margin: 0;
background-color: #ccc;
/* 100% - image width - margin between */
width: calc(100% - 300px - 16px);
}
h5 {
font-size: 18px;
margin-bottom: 8px;
}
p {
flex-grow: 1;
}
.box:nth-child(even) .img {
order: 3;
}
p {
max-height: 250px;
overflow: hidden;
}
<div class="box">
<div class="img"><img></div>
<h5>Version 3</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis cursus hendrerit. Suspendisse potenti. Aliquam posuere ex ut lacus euismod dictum. Proin et ligula posuere leo viverra tempor a in tellus</p>
</div>
<div class="box">
<div class="img"><img></div>
<h5>Lorem Ipsum</h5>
<p>Lorem Ipsum Dolor Sit Amet...</p>
</div>
<div class="box">
<div class="img"><img></div>
<h5>Lorem Ipsum</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis cursus hendrerit. Suspendisse potenti. Aliquam posuere ex ut lacus euismod dictum. Proin et ligula posuere leo viverra tempor a in tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis cursus hendrerit. Suspendisse potenti. Aliquam posuere ex ut lacus euismod dictum. Proin et ligula posuere leo viverra tempor a in tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis cursus hendrerit. Suspendisse potenti. Aliquam posuere ex ut lacus euismod dictum. Proin et ligula posuere leo viverra tempor a in tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis cursus hendrerit. Suspendisse potenti. Aliquam posuere ex ut lacus euismod dictum. Proin et ligula posuere leo viverra tempor a in tellus. </p>
</div>

Vertical alignment of paragraphs with paragraph titles

Please see this Fiddle...
I have two columns. Left column holds paragraph titles. Right column holds the actual paragraphs. I'm trying to align the paragraph title, with the top line of each paragraph and I'm having the hardest time doing it without using unnecessary and sloppy line breaks.
.col1 {float: left; width: 300px; border: 1px solid black; padding: 10px;}
.col2 {float: left; width: 300px; border: 1px solid black; padding: 10px;}
.line1 { height: auto; margin: 10px 0 20px 0; }
.line2 { height: auto; margin: 10px 0 20px 0; }
.line3 { height: auto; margin: 10px 0 20px 0; }
Thoughts?
This is a perfect example of when good ol' <table> elements are still useful! Also, notice that if the overall width is not big enough, your paragraphs wrap under all the titles completely.
The proper way to do this is to use a definition list
See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl
<dl>
<dt>Firefox</dt>
<dt>Mozilla Firefox</dt>
<dt>Fx</dt>
<dd>A free, open source, cross-platform, graphical web browser
developed by the Mozilla Corporation and hundreds of volunteers.</dd>
<!-- other terms and definitions -->
</dl>
If you did want to do this in div elements and not dt, have a look at this fiddle
HTML
<div class='wrapper'>
<div class="col1">
Paragraph 1
</div>
<div class="col2">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean eros est, pellentesque in leo at, molestie mattis sem. Phasellus at est in ligula malesuada ullamcorper nec et massa.
</div>
<div style='clear:both;'></div>
<div class="col1">
Paragraph 2
</div>
<div class="col2">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean eros est, pellentesque in leo at, molestie mattis sem. Phasellus at est in ligula malesuada ullamcorper nec et massa.
</div>
<div style='clear:both;'></div>
<div class="col1">
Paragraph 3
</div>
<div class="col2">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean eros est, pellentesque in leo at, molestie mattis sem. Phasellus at est in ligula malesuada ullamcorper nec et massa.
</div>
<div style='clear:both;'></div>
</div>
CSS
body{
width:100%;
}
.wrapper{
border: 1px solid black;
}
.col1, .col2 {
float: left; width: 300px; padding: 10px;
}
.col2{
border-left:1px solid black;
}

Resources