This question concerns a browser with full css3 support including flexbox.
I have a flex container with some items in it. They are all justified to flex-start but I want the last .end item to be justified to flex-end. Is there a good way to do this without modifying the HTML and without resorting to absolute positioning?
.container {
display: flex;
flex-direction: column;
outline: 1px solid green;
min-height: 400px;
width: 100px;
justify-content: flex-start;
}
p {
height: 50px;
background-color: blue;
margin: 5px;
}
<div class="container">
<p></p>
<p></p>
<p></p>
<p class="end"></p>
</div>
Flexible Box Layout Module - 8.1. Aligning with auto margins
Auto margins on flex items have an effect very similar to auto margins in block flow:
During calculations of flex bases and flexible lengths, auto margins are treated as 0.
Prior to alignment via justify-content and align-self, any positive free space is distributed to auto margins in that dimension.
Therefore you could use margin-top: auto to distribute the space between the other elements and the last element.
This will position the last element at the bottom.
p:last-of-type {
margin-top: auto;
}
.container {
display: flex;
flex-direction: column;
border: 1px solid #000;
min-height: 200px;
width: 100px;
}
p {
height: 30px;
background-color: blue;
margin: 5px;
}
p:last-of-type {
margin-top: auto;
}
<div class="container">
<p></p>
<p></p>
<p></p>
</div>
Likewise, you can also use margin-left: auto or margin-right: auto for the same alignment horizontally.
p:last-of-type {
margin-left: auto;
}
.container {
display: flex;
width: 100%;
border: 1px solid #000;
}
p {
height: 50px;
width: 50px;
background-color: blue;
margin: 5px;
}
p:last-of-type {
margin-left: auto;
}
<div class="container">
<p></p>
<p></p>
<p></p>
<p></p>
</div>
This flexbox principle also works horizontally
During calculations of flex bases and flexible lengths, auto margins
are treated as 0. Prior to alignment via justify-content and
align-self, any positive free space is distributed to auto margins in
that dimension.
Setting an automatic left margin for the Last Item will do the work.
.last-item {
margin-left: auto;
}
Code Example:
.container {
display: flex;
width: 400px;
outline: 1px solid black;
}
p {
height: 50px;
width: 50px;
margin: 5px;
background-color: blue;
}
.last-item {
margin-left: auto;
}
<div class="container">
<p></p>
<p></p>
<p></p>
<p class="last-item"></p>
</div>
Codepen Snippet
This can be very useful for Desktop Footers.
As Envato did here with the company logo.
Codepen Snippet
Related
I have a simple grid layout, that has a limited height and scrolls.
.outer {
border: 1px solid red;
display: grid;
grid-auto-flow: row;
padding: 30px;
grid-gap: 30px;
max-height: 150px;
overflow-y: scroll;
}
.inner {
background-color: gray;
height: 100px;
width: 100px;
}
<div class="outer">
<div class="inner">
one
</div>
<div class="inner">
two
</div>
</div>
The padding is being applied at the top, left and right of the grid:
But when I scroll down, the padding on the bottom isn't applied:
If I remove the max-height the padding at the bottom is now applied:
Why isn't the bottom padding being used? How can I ensure padding works on a grid item with limited height?
Clarity around overflow and padding is a current issue in the CSS spec and the behavior may differ based on each case.
Until the spec is clarified or browsers change their behavior, a workaround for your use case is to add an empty element at the end (since your padding is equal to the gap).
.outer {
border: 1px solid red;
display: grid;
grid-auto-flow: row;
padding: 30px;
grid-gap: 30px;
max-height: 150px;
overflow-y: scroll;
}
.inner {
background-color: gray;
height: 100px;
width: 100px;
}
.outer::after {
content:"";
height:0.1px;
}
<div class="outer">
<div class="inner">
one
</div>
<div class="inner">
two
</div>
</div>
You need to wrap the inner content with a div/container and give the container the grid display, in that case the padding will be applied on that div.
.outer {
border: 1px solid red;
max-height: 150px;
overflow-y: scroll;
}
.outer-content {
display: grid;
grid-auto-flow: row;
padding: 30px;
grid-gap: 30px;
}
.inner {
background-color: gray;
height: 100px;
width: 100px;
}
HTML
<div class="outer">
<div class="outer-content">
<div class="inner">one</div>
<div class="inner">Two</div>
</div>
</div>
If I set #main.overflow is none, I get what I want.
And if I set #main.overflow is scroll, which is not what I want, the last element is broken by error.
Why do scroll bars cause Flex layout errors?
right result
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
#main {
overflow: none;
width: 200px;
background-color: #321;
display: flex;
flex-direction: row;
padding: 10px;
flex-wrap: wrap;
justify-content: flex-start;
}
.el {
width: 50px;
height: 50px;
background-color: red;
margin: 5px;
}
<div id="main">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
incorrect result
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
#main {
overflow: scroll;
width: 200px;
background-color: #321;
display: flex;
flex-direction: row;
padding: 10px;
flex-wrap: wrap;
justify-content: flex-start;
}
.el {
width: 50px;
height: 50px;
background-color: red;
margin: 5px;
}
<div id="main">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
The simple answer is: The scrollbars take up space inside the div. If you force them to render by setting overflow: scroll you take up some of the space inside the flexbox and flex: wrap property causes the content to wrap.
Your main flexbox can handle content of 180px width*.
The content width is also exactly 180px**
This allows no space for the scrollbars and the content ends up wrapping onto the next line.
There is a deprecated property: overflow: overlay which causes the scrollbars to not take up space but it has very limited support and is deprecated so I would not recommend using it.
*Because the width is set to 200px which includes 10px padding on each side. The box-sizing: border-box property makes it so the padding is included in the height and width calculation
**Each el has 60px width including the margin on both sides.
This question already has answers here:
When flexbox items wrap in column mode, container does not grow its width
(9 answers)
Closed 6 years ago.
When using flex box in default row direction, the container height grows to contain all the flex items, even if it is absolutely positioned.
#container {
position: absolute;
display: flex;
flex-wrap: wrap;
}
#container > div {
flex: 0 0 200px;
height: 200px;
}
See http://codepen.io/tamlyn/pen/dPjLoN/?editors=110
However if the flex direction is changed to column, the container collapses to the width of a single flex item, even if the items wrap onto the next column.
#container {
position: absolute;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
#container > div {
flex: 0 0 200px;
width: 200px;
}
See http://codepen.io/tamlyn/pen/rarbeN?editors=110
How can I make the container contain all flex items in column mode?
I've actually found a CSS-only solution to this but it isn't the most perfect thing in the world. Here it is: http://codepen.io/anon/pen/vEPBKK
The trick here is to create a visibility: collapsed container. In flex, visibility: collapsed objects take themselves out of the normal flex flow but retain their dimensions for the purpose of layout. This widens the flex container to the desired width but leaves the flex items unaffected. There are a few caveats, however:
This requires a bit of fiddling. As you can see, the magic <div> is a set width but it uses :nth-child to determine how many boxes are before it. If your actual design breaks at more or less than 3 rows, you'll have to adjust this and you'll most certainly have to adjust the width of the object.
Because of a rendering bug, this does not work in IE. Luckily, IE's incorrect implementation does exactly what you wanted in the first place without any changes so all you have to do is give IE it's own stylesheet with some conditional statements and shoot the div.magic some good old display: none.
HTML
<div id="container">
<div class="fb"></div>
<div class="fb"></div>
<div class="fb"></div>
<div class="fb"></div>
<div class="fb"></div>
<div class="fb"></div>
<div class="fb"></div>
<div class="magic"></div>
</div>
CSS
#container {
position: absolute;
display: flex;
flex-direction: column;
flex-wrap: wrap;
border: 1px solid #f00;
height: 650px;
padding: 1px;
}
#container div.fb {
border: 1px solid #555;
flex: 0 0 200px;
background-color: #ccc;
width: 200px;
margin: 1px;
height: 200px;
}
#container > div.magic {
height: 0;
margin: 0;
padding: 0;
visibility: collapsed;
}
#container > div.magic:nth-child(5),
#container > div.magic:nth-child(6),
#container > div.magic:nth-child(7) {
width: 408px;
}
#container > div.magic:nth-child(8),
#container > div.magic:nth-child(9),
#container > div.magic:nth-child(10) {
width: 612px;
}
#container > div.magic:nth-child(11),
#container > div.magic:nth-child(12),
#container > div.magic:nth-child(13) {
width: 816px;
}
I think this is the CSS you're looking for:
#container {
display: flex;
flex-flow: row wrap;
border: 1px solid #f00;
padding: 1px;
}
#container > * {
border: 1px solid #555;
background-color: #ccc;
height: 200px;
width: 200px;
margin: 1px;
}
The "Container" will always the the width of it's container, in this case the page, but now the boxes will adjust within it properly.
Let me know if I misunderstood your question.
Update
I've been playing with what you're asking for for several days now, and it really seems like it's not possible to do what you're asking... at least not in the direction that you're asking.
The container wants to be the maximum width possible. Unless you force the container to be the exact width, at which point it wont be the full width, but it wont flex with the flexing content either.
.flex-container {
padding: 0;
margin: 0;
list-style: none;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-flow: row wrap;
justify-content: space-around;
border: 1px solid #f00;
}
.flex-item {
background-color: #ccc;
padding: 5px;
width: 200px;
height: 150px;
line-height: 150px;
color: white;
font-weight: bold;
font-size: 3em;
text-align: center;
border: 1px solid #555;
}
<div id="container" class="flex-container">
<div class="flex-item">1</div>
<div class="flex-item">2</div>
<div class="flex-item">3</div>
<div class="flex-item">4</div>
<div class="flex-item">5</div>
<div class="flex-item">6</div>
<div class="flex-item">7</div>
</div>
The first try I do not understand what you mean
as reference material you can see this tutorial
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
I have:
.container {
display:flex;
align-items:center
}
.content {
flex-grow:1
}
in order to align the .content div vertically with css only. The content changes dynamically and that's why I can't use position:absolute; margin-top:50%... styling. Because I never know the exact height of div on each content update.
But in a scenario where .container width changes but height remains, .content overflows .container top because it wraps the text within.
What I'm trying to do is never let .content exceed the top position less than 0. Even the most ideal situation will be preserving the padding-top value of .container and margin-top value of .content. Overflowing bottom will be OK, in fact it'll be my preference.
Any workarounds?
You can make .container height flexible: use min-height instead of height.
.container {
display: flex;
align-items:center;
min-height: 75px;
border: 3px solid blue;
}
.content {
height: 150px;
flex-grow: 1;
border: 3px solid red;
resize: vertical;
overflow: auto;
}
<div class="container">
<div class="content"></div>
</div>
Another possibility is using overflow: auto. However, instead of centering using align-items:center, use margin: auto 0.
.container {
display: flex;
height: 75px;
border: 3px solid blue;
overflow: auto;
resize: vertical;
}
.content {
height: 150px;
flex-grow: 1;
margin: auto 0;
border: 3px solid red;
}
<div class="container">
<div class="content"></div>
</div>
I want to make P to be able to take more text than the height can contain, just so the text can be scrolled down to be read. DIV CLASS="others" has the right height I want. (500px)
The problem is, when I use the overflow: scroll function it goes all the way to the bottom of the page.
EDIT: Forgot to mention I want the titles "News" and "Products" to be without the scroll bar.
Thanks.
.others {
position: relative;
vertical-align: middle;
width: 70%;
background-color: #d0d0d0;
height: 500px;
margin: 0px;
padding: 40px 15% 20px 15%;
display: flex;
justify-content: center;
}
.others div {
width: 400px;
display: inline-block;
float: left;
margin: 0px 15px;
}
.others #news {
background-color: black;
color: white;
text-align: center;
}
.others #products {
background-color: black;
color: white;
text-align: center;
}
.others a {
color: white;
text-decoration: none !important;
}
.others #newsfeed, #productsfeed {
margin: 0px;
padding: 10px 0px;
background-color: lightgreen;
}
.others p {
margin: 0px;
padding: 10px 10px;
vertical-align: middle;
height: 800px;
overflow: scroll;
}
<DIV CLASS="others">
<DIV ID="news">
<H3 ID="newsfeed">News</H3>
<P>News will come here.</P>
</DIV>
<DIV ID="products">
<H3 ID="productsfeed">Products</H3>
<P>Cool photos here.</P>
</DIV>
</DIV>
As I mentioned in my comment, the issue is caused by specifying an explicit height to the inner paragraphs.
Besides, in order to make the inner paragraphs respect the height of their parents (#news and #products flex items which have the same height of their flex container, the .other) you could change the display type of the parents to flex as well and set their flex-direction to column.
And then give flex: 1; to the paragraphs as follows:
Example Here
#news, #products {
display: flex;
flex-direction: column;
}
#news p, #products p {
flex: 1;
overflow: auto; /* up to you */
}
As a side-note: make sure you have included the old (prefixed) syntax of flexbox as well for the sake of browser support. You could use tools like Auto Prefixer to achieve that.
You need a containing div on the paragraphs, then set overflow: scroll; and height: 460px; on that container (or whatever height you need to have it contained within the 500px tall .others block).
You'd also need to make sure your .others div styling doesn't apply to that container - in my example below, I changed that selector to .others > div to only select immediate children of .others. And you should remove the height: 800px; from the inner paragraphs, as mentioned by Hashem Qolami.
jsfiddle example