Why does "flex-wrap: wrap" break "align-items"? - css

(I'm using Chrome v.39+)
I'm trying to use the flex-wrap property to stack child divs both horizontally and vertically, but I'm seeing some very strange behaviors. For example, if there's 3 child divs and the last is given a width of 100% (causing it to wrap) there will be unwanted gaps introduced.
Sometimes I can force the first 2 divs to honor align-items: stretch by giving them height: 100% or height: calc(100% - 1px), other times they won't stretch passed the mysterious gap, and sometimes they'll even disappear all together if I try to force them to stretch.
Here's a simplified example of the problem. They grey shouldn't be visible.
Why are these gaps appearing in flex-wrapped divs and how can I prevent them?

The gray area is still visible at the bottom because you set a height on the parent container.
If you don't want to see that gray area, remove the height from the container and add a fixed height that you require on one of the elements in the first row
DEMO
.a {
width: 300px;
display: flex;
flex-wrap: wrap;
position: relative;
top: 100px;
left: 200px;
background-color: #999;
}
.b {
height: 150px;
background-color: #00ff00;
}
.c {
background-color: #0000ff;
}
.d {
background-color: #ff0000;
}
.b {
flex-grow: 1;
flex-shrink: 0;
}
.c {
width: 5px;
flex-shrink: 0;
}
.d {
width: 100%;
height: 10px;
flex-shrink: 0;
}
<div class='a'>
<div class='b'></div>
<div class='c'></div>
<div class='d'></div>
</div>
Note: If you want to avoid fixed dimensions - just remove the height:10px from the red div.
This will ensure that there are no gaps and that each row has equal height
DEMO

Related

Nested row flexbox with wrapping on multiple levels

I am making responsive design where elements wrap to next line in certain way when there is not enough space, however I cannot make it working.
Requirements:
There are 3 elements (actually more, but this is minimal example), all with automatic size depending on their content: one aligned on the left, two - on the right.
They are placed inside container which always spans at least 100% width of parent.
If there is not enough space for elements to be all on one line, then right element should begin to wrap to next line, while still all being to the right of left element.
If there is still not enough space, only then right elements go under left one.
If there is still not enough space, then container expands in size.
Here are examples how it should work (red lines represent viewport borders):
Here is code I come up with so far, but it starts wrapping immediately on outer level, not on innermost as I need:
html, body {
width: 100%;
height: 100%;
}
* {
margin: 0;
padding: 0;
flex: 0 0 auto;
}
#container {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
overflow: hidden;
width: 100%;
min-width: min-content;
background-color: rgb(224, 191, 146); /* for visibility */
}
#child-left {
background-color: rgb(92, 189, 40); /* for visibility */
/* imitate content for demo, in actual code size is not set */
width: 50vw;
height: 50px;
}
#child-right {
display: flex;
flex-flow: row wrap;
background-color: rgb(111, 72, 20); /* for visibility */
}
#grandchild-right-1 {
background-color: rgb(40, 102, 189); /* for visibility */
/* imitate content for demo, in actual code size is not set */
width: 200px;
height: 20px;
}
#grandchild-right-2 {
background-color: rgb(189, 40, 112); /* for visibility */
/* imitate content for demo, in actual code size is not set */
width: 200px;
height: 20px;
}
<div id="container">
<div id="child-left">1</div>
<div id="child-right">
<div id="grandchild-right-1">2</div>
<div id="grandchild-right-2">3</div>
</div>
</div>
You can achieve this by using flex: 1.
Remember that flex is a shorthand property for flex: 1 1 0 or flex-grow: 1, flex-shrink: 1 and flex-basis: 0.
As a result, this makes the elements take any empty space in the container, hopping to the next line if theres no space left.
https://css-tricks.com/understanding-flex-grow-flex-shrink-and-flex-basis/

Can a position:absolute element be made sticky?

In CSS, position: sticky enables an element to display with a position: static behaviour (ie. it adopts its default position within the document flow) until it reaches a certain scroll position, after which it adopts position: fixed behaviour.
So... does that mean we cannot use position: sticky on an element which requires a normal behaviour of position: absolute?
Context:
I have an out-of-flow element which occupies a position towards the top-left corner of the viewport. After an inch or two of scrolling, the element hits the top of the viewport and, ideally, I'd like it not to carry on disappearing at that point.
You actually can leverage display: grid and have a sticky element that doesn't pushes its siblings:
header {
display: flex;
align-items: center;
justify-content: center;
height: 50vh;
border: 1px dashed #f00;
}
main {
display: grid;
}
div {
display: flex;
align-items: center;
justify-content: center;
}
.section {
grid-column: 1;
height: 100vh;
border: 1px dashed #0f0;
}
.first.section {
grid-row: 1;
}
.sticky {
grid-row: 1;
grid-column: 1;
position: sticky;
top: 0;
height: 30vh;
border: 1px dashed #0ff;
}
footer {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
border: 1px dashed #f00;
}
<header>I'm the header</header>
<main>
<div class="sticky">I'm sticky</div>
<div class="first section">Just</div>
<div class="section">some</div>
<div class="section">sections</div>
</main>
<footer>I'm the footer</footer>
The trick here is to place the sticky section and its first sibling on the first row and first column of their parent (because grids allow us to place many elements in the same cell).
The sticky element remains sticky in its parent so it will stay on scroll beyond its cell.
As GibboK says, the default positioning scheme isn't absolute positioning, it's the static position. Elements are laid out in normal flow by default — if out-of-flow were the default, then the default HTML page would be impossible to read. Besides, absolutely positioned elements do scroll with the page most of the time — the only time you can make an absolutely positioned behave like a fixed positioned element with respect to page scrolling is through some semi-complicated CSS.
If you're asking whether it's possible for
a stickily positioned element to be out-of-flow when stuck and unstuck, or
for the containing block of a stickily positioned element to be determined the same way as for an absolutely positioned element,
then unfortunately neither of these is supported by sticky positioning.
The point of position:sticky is that it is only fixed while the parent element is not in view. A position:absolute element isn't attached to it's parent.
It could be interesting if such a position would exist and the rule would be that the element would be absolute, while the element it is absolute positioned to is in view, but currently there exists nothing like this nativley, but you could try to recreate it using JS.
A way to make a sticky element look like it's absolutely positioned
I came up with this hack that achieves the goal, but I haven't figured out how to fix its one flaw: There's a blank area at the bottom of the scrollable content equal to the height of the sticky element + its initial vertical offset.
See the comments in the code for an explanation of how it works.
#body {
width: 100%;
position: relative;
background: Linen;
font-family: sans-serif;
font-size: 40px;
}
/* to position your sticky element vertically, use the
height of this empty/invisible block element */
#sticky-y-offset {
z-index: 0;
height: 100px;
}
/* to position your sticky element horizontally, use the
width of this empty/invisible inline-block element */
#sticky-x-offset {
z-index: 0;
width: 100px;
display: inline-block;
}
/* this element is sticky so must have a static position,
but we can fake an absolute position relative to the
upper left of its container by resizing the invisible
blocks above and to the left of it. */
#sticky-item {
width: 150px;
height: 100px;
border-radius: 10px;
background-color: rgba(0, 0, 255, 0.3);
display: inline-block;
position: sticky;
top: -80px;
bottom: -80px;
}
/* this div will contain the non-sticky main content of
the container. We translate it vertically upward by
sticky-y-offset's height + sticky-item's height */
#not-sticky {
width: 100%;
background-color: rgba(0, 0, 255, 0.1);
transform: translateY(-200px);
}
.in-flow {
width: 90%;
height: 150px;
border-radius: 10px;
margin: 10px auto;
padding: 10px 10px;
background: green;
opacity: 30%;
}
<div id="body">
<div id="sticky-y-offset"></div>
<div id="sticky-x-offset"></div>
<div id="sticky-item">absolute & sticky</div>
<div id="not-sticky">
<div class="in-flow">in flow</div>
<div class="in-flow">in flow</div>
<div class="in-flow">in flow</div>
<div class="in-flow">in flow</div>
</div>
</div>

flex-box stretching issue

I have a problem with flex-box. I want a one column with two rows where there are 2 columns on the second row. However I want to use just one wrapper element for it (I do not want to wrap the second row into a div). The issue is that I want only the second row elements to strech.
Here it is https://jsfiddle.net/x8g0wupg/
HTML
<div id="wrap">
<header>text</header>
<div id="a">text</div>
<div id="b">text</div>
</div>
CSS
#wrap {
display: flex;
flex-flow: row wrap;
height: 500px;
border: 3px solid yellow;
}
header {
background-color: white;
width: 50px;
flex: 1 100%;
height: 40px;
}
#a {
background-color: green;
flex-grow: 1;
}
#b {
background-color: red;
flex-grow: 1;
}
I managed to make them the way I want them to be positioned however the first column element just doesnt want to shring to the height I have set to him instead of it it leaves a empty space to the vertical 50% of the wrapper.
Is there any way to do it without extra wrapper for the second row?
Thank you!
A bit hackish, with align-self: flex-end; and a negative top margin on the parent.
See https://jsfiddle.net/C14L/1rj3kdkb/

flexbox one element fixed height, other filling

I want to make some kind of image viewer with some descriptive text below. Problem is, that the lower box with the description has a fixed height and the image should fill the remaining height of whatever container it is in.
I wanted to use flexbox for that, as I think it seems to be the most elegant and simple solution (without using JS).
This this code and codepen for my current work, which seems to work mostly:
html, body, #container {
height: 100%
}
#container {
display: flex;
flex-direction: column;
}
#container > #image {
/* flex-grow: 1; */ /* not needed here? */
max-width: 75%;
background-color: #fcc;
margin: 0 auto;
}
img {
max-height: 100%;
/* HERE IS WHERE MY PROBLEM STARTS!; */
max-width: 100%;
}
#container > #text {
flex-grow: 0;
flex-shrink: 0;
background-color: rgba(128, 128, 128, 0.7);
padding: 5px;
max-width: 75%;
margin: 15px auto 0;
/* TOP MARGIN DOESN'T WORK */
}
http://codepen.io/Kageetai/pen/AaCJy
I got most of it to work but the image is not resizing itself correclty. As you can see through the transparent background of the text box, it stretches itself over the border of the containing div and even behind the text box.
So how can I retain the image with the correct aspect ratio inside its container?
And furthermore the centering with margin: 0 auto; seems to make problems when resizing the window. The image is not centered anymore and the page needs a refresh to make it work again.
So does anyone know how to make the image behave correctly? :)
For image , you can set an height, margin and display.
For image container, give a 2 or 3 value to flex and none to other, so it fills as much space as avalaible.
DEMO
CSS used :
html,
body,
#container {
height: 100%
}
#container {
display: flex;
flex-direction: column;
}
#container > #text {
background-color: #ccf;
padding: 5px;
}
#container>#image {
flex:3;
display:flex;
}
img {
width:auto;
display:block;
margin:auto;
height:100%;
}
Here's a more basic demo of how to achieve this.
<html style="height: 100%">
<body style="height: 100%; margin: 0; display: flex; flex-direction: column">
<p>Toolbar</p>
<div style="background: #bbb; flex: 1">Image</div>
</body>
</html>
A demo can be seen over at Codepen.

css floated element position collapse when resized

Whenever I resize the browser, the 2nd div in .container positions below the first one.
<div class = "container">
<div class = "one"></div>
<div class = "two"></div>
</div>
The divs are really blank.
CSS
.container{
overflow: hidden;
width: 810px;
min-width: 810px;
}
.one,.two{
width: 300px;
height: 450px;
}
.one{float:left}
I just realized that, you are not floating the other element, this is causing it to shift down, you should use float: left; or right as it's a div so it will take up entire horizontal space, and hence it is pushed down.
Demo
.one, .two{
width: 300px;
height: 450px;
float:left; /* Float both elements */
background: #f00;
}
Alternative
You should use display: inline-block; and white-space: nowrap; to prevent the wrapping of the elements
Demo
This will gave you the same effect, the only thing is 4px white space, you can simply use
.two {
margin-left: -4px;
}
the above will fix the white space issue for you
Demo 2
Add this CSS. Demo.
.two {
margin-left: 300px;
}
PS: When works with float, you should clearfix.
Give your body a minimum width:
body {
min-width: 1110px;
}
Then, when the viewport gets smaller than 1110px the scrollbar will appear.
Note: if you add margin, padding or border to the divs, add it to the min-width of the body (or take some extra space).

Resources