Force flex element not to grow in cross-axis direction - css

I'm making a page with a vertical menu using display: flex;. I want the menu's width to fit snuggly around a few buttons, without having to use a fixed width.
However, I also want the menu box to have a status message, which can have quite a long text. I'd like this status-div to have the width of the menu, while not forcing the menu container to grow its width. Instead, the status-div should grow its height and wrap the text.
Explaining this in words is pretty difficult, so I suggest you checkout out this fiddle: http://jsfiddle.net/bXL3q/
Note the difference when setting .statusmessage to display: none;.
Any ideas, or is what I'm trying to do not feasible? ..should it be?
What I've tried:
width: 100% fails, obviously it just assumes the parent width
width: -webkit-min-content sort of works, but it makes the element too narrow
flex-basis and flex-grow affect the height of the element, and do nothing to affect the width
position: absolute will solve the width issues, but now I have no way to define the height of the status-div.. (for the purpose of forcing a scroll bar in windows with small height - instead it will just flow over the button elements)
body {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
flex-flow: row nowrap;
align-items: stretch;
}
.page {
flex-grow: 1;
background-color: yellow;
}
.menu {
background-color: red;
height: 100%;
display: flex;
flex-flow: column nowrap;
}
.somechildren {
white-space: nowrap;
background-color: green;
border: 1px solid black;
}
.menu>* {
flex-shrink: 0;
}
.separate {
flex-grow: 1;
}
.statusmessage {
background-color: magenta;
align-self: flex-end;
/*display: none;*/
}
<div class=menu>
<div class=somechildren>I'd like the menu's</div>
<div class=somechildren>width to fit nicely</div>
<div class=somechildren>around these children</div>
<div class=separate></div>
<div class=statusmessage>
While forcing this status message to wrap and grow its height, without affecting the width of the container.
</div>
</div>
<div class=page>
The page
</div>

You were almost there with width. What you need to do is set width and min-width (demo):
.statusmessage {
width:0; /* Collapses .statusmessage so it doesn't affect column width */
min-width:100%; /* Expands .statusmessage to width of column */
}
The width can be (and probably should be) set to a value other than 0. It should just be the minimum width of the column or smaller. So use a value that works for you.
I've tested this on Chrome and Firefox and seems to work in both. Now, is it supposed to work? I'm not sure, I haven't read into the spec that much (it could be undefined). Make sure to test in all browsers you need it to work in. (And check the spec to see if this behavior is undefined/incorrect.)

width: 0; min-width: 100%; didn't work for me.
Instead, I set
position: relative;
on the flex child and wrapped its contents in an inner div with
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
This prevents the contents from contributing to the flex container size while still matching the cross-axis size determined by the rest of the flex children.

For me, the issue was that the default value of align-items is stretch. So the items stretch out the cross axis by default.
You can either set align-items: flex-start or align-self: flex-start for the single flex child.
The visual here is quite illustrative.

Related

How to set height of flex row but still allow row to expand vertically?

Consider this very simple header:
.box {
height: 100%;
width: 50px;
background-color: blue;
}
.row {
display: flex;
height: 60px;
background-color: yellow;
}
<div class="row">
<div class="box"></div>
<h1>This string needs to be able to wrap</h1>
</div>
The blue box needs to be defined by the height of the row and the text needs to be able to wrap. But as you can see, when the text wraps it extends beyond the flexbox. The flexbox can't grow at all because I have defined its height. If I remove the height specification then text wrap works correctly but the blue box disappears. This is a quite frustrating problem that I have spent hours trying to figure out. Is there no way to say to the row "your height is 60px but you can go bigger if you need to".
What I've tried so far:
Use min-height: 60px. For some reason the blue box still doesn't show up when I do this.
Use max-height: 100px. The row defaults to that size which is too big for when the text doesn't wrap.
I guess I could write media queries to manually change the height of the row, but it seems like there should be a more flexboxy way of doing this. Any ideas?
Here is the JS Fiddle if you want to play with it.
You don't need height: 100%; on child element of flex element.
And if you want minimum height of 60px on parent element, use min-height: 60px; instead of height: 60px;
.box {
width: 50px;
background-color: blue;
}
.row {
display: flex;
background-color: yellow;
min-height: 60px;
}
Here is the fiddle
https://jsfiddle.net/3bzeht52/

Why do mobile viewport dimensions change if there is overflow in flexbox container with 'nowrap'?

Unsurprisingly, if a parent with display: flex:
1. Has flex-direction: row and flex-wrap: nowrap and,
2. Has any children that start wide (flex-basis) and don't allow shrinking (via flex-shrink: 0),
the content will exceed the bounds of the parent dimensions.
If this flex parent is the width of the screen, browsers generally just honor the 'nowrap' layout anyway and put up a horizontal scrollbar if the viewport gets too small. But on a mobile screen this isn't the only thing that happens. The window's opinion of its own viewport size seems to change, and it throws off the scale of the page.
I've recreated in a simplified page:
HTML:
<div id="container">
<div id="flex-container">
<div class="flex-child-text">Here's a bunch of text</div>
<div class="flex-child-button">ButtonHere</div>
<div class="flex-child-button">ButtonThere</div>
</div>
<div id="footer-bar"></div>
</div>
CSS:
#container {
display: block;
height: 100vh;
background-color: lightseagreen;
}
#flex-container {
display: flex;
height: 32px;
width: 100%;
padding: 1rem 0;
flex-flow: row nowrap;
}
.flex-child-text {
flex: 1 0 340px;
font-size: 20px;
}
.flex-child-button {
flex: 0 1 auto;
height: 1rem;
margin: 0px 0.25rem;
display: inline-block;
border: solid #aaa 1px;
background-color: #eeeeee;
border-radius: 3px;
padding: 0 8px;
}
#footer-bar {
position: fixed;
bottom: 0;
width: 100vw;
height: 32px;
background-color: black;
}
here it is in a jsfiddle
The jsfiddle is mostly because visuals are helpful, but it can't recreate the bug. Simple resizing of a window doesn't do it - I've observed in Chrome mobile emulator and verified on an Android device. If any intrepid reader were willing to cut/paste/try on their own Chrome mobile, I've included a head tag in the fiddle code to ease that along.
Toggling .flex-child-text flex-shrink property between 0 and 1 changes the viewport dimensions (both width and height, seemingly proportionally). The footer, which gets position and dimensions from the outer container, is flung offscreen. This is also evidenced by consoling window.innerWidth - the result has changed when the layout has broken.
Image - All elements contained within viewport
Image - Children break out of container, bottom-right of scroll dimensions
So to be clear, I understand why in the second image there is white space if you scroll to the right of the green container. What I don't understand is: 1. Why this also causes the y dimension to change like it does, and 2. Why an element position: fixed to bottom: 0 or right: 0 doesn't anchor to the side of the actual viewport in this special case.
Adding overflow: auto to the outer #container makes it work. The offending content still pushes out of the flex container to the right (and can be scrolled to), but the elements that rely on viewport anchoring are now back in place.
Still not clear on why the layout broke in such a way that dimension y was affected, but this fix makes that not an issue. Got the idea in this blog post

Header height to fit background image

I have a question related to header height.
HTML:
<header>
<h1>
Hello World!
</h1>
</header>
CSS:
header {
background-image: url("../images/header.png");
background-size: cover;
height: 100%;
text-align: center;
color: white;
}
In this case header height fits text height. Is there a way to set header height the same as image height?
You could do the header image as a foreground img tag rather than background image and then do this in the CSS
header {
position: relative;
}
header img {
width: 100%;
height: auto;
}
header h1 {
position: absolute;
top: 0;
}
In the header's css, you can put:
background: url("../images/header.png") 50% 50% / 100% no-repeat fixed;
It will automatically place and size the image so it's not stretched.
Why don't you just use Flexbox? It's the defacto standard these days.
Here is a jsfiddle what I have in mind:
JSFiddle example
Essentially, what you want to do is your <h> element should actually be a child of another div within your <header>:
<header>
<div class="backimage">
<h1>
Hello World!
</h1>
</div>
</header>
Your <header> div, as the flex "container", display child elements as column, without wrap, aligned in the center with space around, and justified center:
header {
display: flex;
flex-flow: column nowrap;
justify-content: space-around;
align-items: center;
align-content: space-around;
}
Child elements within the flex "container" should be ordered for number of appearance either as a row or column, in this case the "container" displays items in a column layout, so you want your header to appear as the first item. flex is shorthand for flex-grow, flex-shrink, and flex-basis; this third one being what size the element should be, the first two controlling what priority or portion of the container element this child element will be treated as compared to other children. In this case, handle it auto. We want to reposition the header to be vertically centered in the containing div, so position: relative;, add 50% of the container's height to the origin point of the ` element's origin (which always starts from the top left of an object), and because the text default is set at 1em, it would logically follow that .5em would be the center, so subtract that from the 50%. Give it text-align center for horizontal centering:
header h1 {
position: relative;
top: 50%;
margin-top: -.5em;
text-align: center;
order: 1;
flex: 1 0 auto;
}
Then you just have to worry about your background. Background, no repeat, define the height of the image (which in this case will be applied to the div itself), border here for example just so you can see the boundary of the div, and don't forget that this should appear second compared to the header:
.backimage {
background-image: url('http://upload.wikimedia.org/wikipedia/ \
commons/4/47/PNG_transparency_demonstration_1.png');
background-repeat: no-repeat;
border: 1px solid blue;
width: 500px;
height: 300px;
order:2;
}
Here's what you end up with:
Here's a great guide on flexbox usage:
Flexbox guide

Lay out children with equal space between each other to fill container

I have a div with a variable width, and I have a variable amount of children inside this div. I want the children to fill up the space inside the div. I first tried to change the div to display:table and the children to display:table-cell but I ended up all the children filling up all the space and not obeying their width and max-width properties. Then I've tried the table approach: I've changed the div to a table (yes, I know, it's not recommended, that's why I'm probably here asking) and wrapped the children into a tr and each in tds, but I ended up all the children cells filling up the whole space, but aligned to left (I've set the children divs display:inline-block):
If I change the alignment to center, I get this:
They are centered, but I still get spaces on the left and right of the parent (with the yellow background that I've set for distinguishing). What I actually want is this:
I've achieved this by setting the first td to align text to left, the second to center, the third to right. But I may have more of these thumbnails, so I need a general solution.
How can I lay out a variable number of children inside a container to fill the width, with the first element starting at the exact left border of the container (unlike the second image) and the last element ending at the exact right border of the container (like shown in the third image)?
Something like this?
HTML:
<div>
<span id="s1"></span>
<span id="s2"></span>
<span id="s3"></span>
</div>
CSS:
div{
background: #ff6;
text-align: justify; /* Important */
font-size: 0; /* Used to remove spaces */
}
div:after{ /* Used to create a new last line */
content: '.';
display: inline-block;
width: 100%;
height: 0;
overflow: hidden;
}
span{
display: inline-block;
height: 150px;
}
/* Use your widths, min-widths and max-widths here: */
#s1{
background: red;
width: 15%;
min-width: 50px;
max-width: 150px;
}
#s2{
background: green;
width: 40%;
min-width: 50px;
max-width: 250px;
}
#s3{
background: blue;
width: 40%;
min-width: 50px;
max-width: 200px;
}
Demo
You can obtain equally spaced boxes using text-align: justify on the wrapper. The problem is that it doesn't work for the last line (which in this case is the first too), so you can either use text-align-last, or an :after pseudo element with width: 100% in order to create a new last line.

Play button centred with different image/video sizes

How can I keep the play button centred even if the the image/video size changed?
.image{
position:relative;
width:500px; //changed
height:300px;
}
Here is my example...
Attention: my images/videos haven't no specific size, so they can change according to their own size... This is just an example!
I set up three examples to show how you could solve this problem.
Please see the fiddle: http://jsfiddle.net/audetwebdesign/mxSkQ/
The HTML is essentially yours:
<div class ="image ex1">
<a href="#">
<img src="http://placehold.it/200x150" alt="video">
<span class="play">
<span></span>
</span>
</a>
</div>
I am using a demo image with configurable dimensions, 200x150 for example, easily changed for testing.
Example 1 - Image Size Determines Size of the Parent Container
.ex1.image {
position: relative;
display: inline-block;
margin-left: 50px;
}
.image a {
display: inline-block;
position: relative;
}
/* Gets rid of the extra white space that follows an inline element*/
.image img {
vertical-align: bottom;
}
If you want the .image div to shrink to fit the image, use inline-block to display.
The margin-left is optional, will depend on the rest of the layout.
Important: To center the play button motif, simply set the a tag to display as inline-block and your arrow-motif-span will position itself nicely.
Because img is an inline element, browsers insert a small space after it that can show up if you have borders or backgrounds. Use vertical-align: bottom to clean that up.
Example 2 - Parent Container Has A Specified Width
You can specify a width for the .image parent container and then use text-align to position the image element.
.ex2.image {
position: relative;
display: inline-block;
width: 400px;
height: auto;
text-align: center;
}
Example 3 - Parent Container Has Full Width and Specified Height
In this example, I let the parent container fill up the width of the window and set the
height to 200px. To get vertical centering, I set margin-top to a hard-coded value that will depend on the height of the image. If you let the image take on a fixed height, this example is useful.
.ex3.image {
position: relative;
display: block;
width: auto;
height: 200px;
text-align: center;
}
.ex3.image a {
margin-top: 25px;
}
You need
top: 50%;
left: 50%;
margin: -25px 0 0 -25px; // top and left equal to half of the size * (-1)
http://jsfiddle.net/nGKcn/13/
Try playing with the image size/different images.
Give the image CSS of:
display: block;
margin: 0 auto;
This is just a general way you put stuff in the middle.
Unless I'm missing something maybe?

Resources