alignment breaks when placed inside other container - html

I am exploring Flexbox and am trying to align some items. The following code works and shows what I want to achieve (successful codepen here):
.labelinput {
display: flex;
flex-flow: row;
margin: 1px;
}
.labelinput > *:first-child {
flex-basis: 10em;
flex-grow: 0;
flex-shrink: 0;
}
.labelinput > *:nth-child(2) {
flex-grow: 1;
flex-shrink: 1;
border: 3px solid purple;
}
<div class='labelinput'>
<div>1st input</div>
<div>this is just a div</div>
</div>
<div class='labelinput'>
<div>2nd:</div>
<input type="text" name="foo" value="this is an input box" />
</div>
The above code produces the nicely align output shown below:
My reading of the above code is that it works because the first child in every div has a determined size (10em) and is totally inflexible (flex-grow and flex-shrink set to 0) whereas the second child has no determined size and will grow and shrink as appropriately.
What breaks though is when I try to embed the two top-level div elements (of class labelinput) into yet another container (failing codepen here):
#container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
margin: 5px;
border: 1px solid grey;
}
.labelinput {
display: flex;
flex-flow: row;
margin: 1px;
}
.labelinput > *:first-child {
flex-basis: 7em;
flex-grow: 0;
flex-shrink: 0;
}
.labelinput > *:nth-child(2) {
flex-grow: 1;
flex-shrink: 1;
border: 3px solid purple;
}
<div id='container'>
<div class='labelinput'>
<div>1st input</div>
<div>this is just a div</div>
</div>
<div class='labelinput'>
<div>2nd:</div>
<input type="text" name="foo" value="this is the input box" />
</div>
</div>
The above produces the following unsatisfactory output:
I can't explain why this fails as I am just inserting the content of the successful case into a container that simply performs the default vertical stacking (flex-direction: column;).
By experimentation I have discovered that removing the align-items property from the outer level container (#container) fixes the problem but I can't explain that either.
I understand that the outermost container (#container) is asked to layout two other containers (of class labelinput) so whatever property I set for align-items in the outermost container should apply to the inner containers as a whole, not change the layout of their internal items.
Moreover I can't explain why the layout is changed based on the element type when there's nothing in my CSS that differentiates between items of element div versus items of element input.

I can't explain why this fails as I am just inserting the content of the successful case into a container that simply performs the default vertical stacking (flex-direction: column).
The difference is that this new primary container has align-items: flex-start.
By experimentation I have discovered that removing the align-items property from the outer level container (#container) fixes the problem but I can't explain that either.
When you nest the .labelinput flex containers in the larger container (#container), then the .labelinput elements become flex items, in addition to flex containers.
Since the #container flex container is set to flex-direction: column, the main axis is vertical and the cross axis is horizontal1.
The align-items property works only along the cross axis. It's default setting is align-items: stretch2, which causes flex items to expand the full width of the container.
But when you override the default with align-items: flex-start, like in your code, you pack the two labelinput items to the start of the container, as illustrated in your problem image:
Because stretch is the default value for align-items, when you omit this property altogether, you get the behavior you want:
I understand that the outermost container (#container) is asked to layout two other containers (of class labelinput) so whatever property I set for align-items in the outermost container should apply to the inner containers as a whole, not change the layout of their internal items.
The outermost container is not changing the layout of the inner container's children. At least not directly.
The align-items: flex-start rule on the outermost container is applying directly to the inner containers. The internal items of the inner containers are just responding to the sizing adjustment of their parent.
Here's an illustration of align-items: flex-start impacting .labelinput (red borders added).
#container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
margin: 5px;
border: 1px solid grey;
}
.labelinput {
display: flex;
flex-flow: row;
margin: 1px;
border: 2px dashed red;
}
.labelinput > *:first-child {
flex-basis: 7em;
flex-grow: 0;
flex-shrink: 0;
}
.labelinput > *:nth-child(2) {
flex-grow: 1;
flex-shrink: 1;
border: 3px solid purple;
}
<div id='container'>
<div class='labelinput'>
<div>1st input</div>
<div>this is just a div</div>
</div>
<div class='labelinput'>
<div>2nd:</div>
<input type="text" name="foo" value="this is the input box" />
</div>
</div>
Moreover I can't explain why the layout is changed based on the element type when there's nothing in my CSS that differentiates between items of element div versus items of element input.
There may be no difference between div and input in your code, but there are intrinsic differences.
Unlike a div, an input element has a minimum width set by the browser (maybe to always allow for character entry).
You may be able to reduce the input width by applying min-width: 0 or overflow: hidden3.
Footnotes
1. Learn more about flex layout's main axis and cross axis here: In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?
2. Learn more about the align-items property in the spec: 8.3. Cross-axis Alignment: the align-items and align-self properties
3. Why doesn't flex item shrink past content size?

Related

Why does my header center its children vertically? [duplicate]

This question already has answers here:
How to disable equal height columns in Flexbox?
(4 answers)
Closed 1 year ago.
I am having trouble understanding the default behavior of display: flex
Why does my header vertically centers its children when I only add display: flex to it.
I have not added align-items: center; to the header.
Is it because I assigned it height: 100px;?
header {
display: flex;
border: 1px solid black;
height: 100px;
}
header div {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
max-width: 1224px;
margin: 0 auto;
}
nav a:not(:last-child) {
margin-right: 55px;
}
<header>
<div>
Link
<nav>
one
two
three
</nav>
Link
</div>
</header>
I am aware that the div has display: flex and align-items: center; But why does the parent html element header also need display: flex for its children to vertically center?
When you apply display: flex to the parent div, its children "stretch" to assume the full height and width of the container, since the default align-items property is stretch.
Hence, the child of the header now has a height of 100px (previously the height of its content), and the content is centered.
Your solution (to apply height: 100%) works since the content's height is now 100px (the height of the container), essentially simulating flexbox's vertical stretch.
It is not aligning vertically the header, it is aligning vertically the div inside the header.
Your header > div has display:flex and align-items: center which is explicit telling the div to align items to the center.
You don't need display: flex; on parents with only one child.
Your <header> tag has 100px of height and by default your <div> has implicit align-self: stretch; which is like you do height: 100% on it. For solving your problem you have two options:
Remove display: flex; from your <header> tag.
Asign align-self: flex-start; to your <div>.
Apply these two options to the <div> children for achieve the same results if you consider. The main key of this answer is: All children of a flex container has an implicit align-self: stretch by default.

Flex direction elements width [duplicate]

I have a container <div> with display: flex. It has a child <a>.
How can I make the child appear "inline"?
Specifically, how can I make the child's width determined by its content, and not expand to the width of the parent?
What I tried:
I set the child to display: inline-flex, but it still took up the full width. I also tried all other display properties, but nothing had an effect.
Example:
.container {
background: red;
height: 200px;
flex-direction: column;
padding: 10px;
display: flex;
}
a {
display: inline-flex;
padding: 10px 40px;
background: pink;
}
<div class="container">
Test
</div>
http://codepen.io/donpinkus/pen/YGRxRY
Use align-items: flex-start on the container, or align-self: flex-start on the flex items.
No need for display: inline-flex.
An initial setting of a flex container is align-items: stretch. This means that flex items will expand to cover the full length of the container along the cross axis.
The align-self property does the same thing as align-items, except that align-self applies to flex items while align-items applies to the flex container.
By default, align-self inherits the value of align-items.
Since your container is flex-direction: column, the cross axis is horizontal, and align-items: stretch is expanding the child element's width as much as it can. (The column setting is also the reason why display: inline-flex isn't working.)
You can override the default with align-items: flex-start on the container (which is inherited by all flex items) or align-self: flex-start on the item (which is confined to the single item).
Learn more about flex alignment along the cross axis here:
How does flex-wrap work with align-self, align-items and align-content?
Learn more about flex alignment along the main axis here:
In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?
In addition to align-self you can also consider auto margin which will do almost the same thing
.container {
background: red;
height: 200px;
flex-direction: column;
padding: 10px;
display: flex;
}
a {
margin-right:auto;
padding: 10px 40px;
background: pink;
}
<div class="container">
Test
</div>
width: min-content also yields the same result.
Not supported in IE11 if that matters to you: https://caniuse.com/mdn-css_properties_width_min-content

Make flex items take content width, not width of parent container

I have a container <div> with display: flex. It has a child <a>.
How can I make the child appear "inline"?
Specifically, how can I make the child's width determined by its content, and not expand to the width of the parent?
What I tried:
I set the child to display: inline-flex, but it still took up the full width. I also tried all other display properties, but nothing had an effect.
Example:
.container {
background: red;
height: 200px;
flex-direction: column;
padding: 10px;
display: flex;
}
a {
display: inline-flex;
padding: 10px 40px;
background: pink;
}
<div class="container">
Test
</div>
http://codepen.io/donpinkus/pen/YGRxRY
Use align-items: flex-start on the container, or align-self: flex-start on the flex items.
No need for display: inline-flex.
An initial setting of a flex container is align-items: stretch. This means that flex items will expand to cover the full length of the container along the cross axis.
The align-self property does the same thing as align-items, except that align-self applies to flex items while align-items applies to the flex container.
By default, align-self inherits the value of align-items.
Since your container is flex-direction: column, the cross axis is horizontal, and align-items: stretch is expanding the child element's width as much as it can. (The column setting is also the reason why display: inline-flex isn't working.)
You can override the default with align-items: flex-start on the container (which is inherited by all flex items) or align-self: flex-start on the item (which is confined to the single item).
Learn more about flex alignment along the cross axis here:
How does flex-wrap work with align-self, align-items and align-content?
Learn more about flex alignment along the main axis here:
In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?
In addition to align-self you can also consider auto margin which will do almost the same thing
.container {
background: red;
height: 200px;
flex-direction: column;
padding: 10px;
display: flex;
}
a {
margin-right:auto;
padding: 10px 40px;
background: pink;
}
<div class="container">
Test
</div>
width: min-content also yields the same result.
Not supported in IE11 if that matters to you: https://caniuse.com/mdn-css_properties_width_min-content

Align flex item to end of container

Using flexbox, how can you align a <p> to its parent's "end"?
According to the documentation, adding align-content: flex-end to the parent element should be enough. However, this isn't working in the example below.
Example:
Here is a simple div containing a p element.
div {
display: flex;
flex-direction: column;
align-content: flex-end; /* This should do the alignment, but doesn't. */
background: yellow;
padding: 50px;
height: 300px;
width: 400px;
}
<div>
<p>Align me to the end.</p>
</div>
jsFiddle
A hacky solution would be to add a ::before to the child with flex: 1, but I'm curious if there is a better solution.
The align-content property only works when there are multiple lines of flex items in a container. This property is designed to distribute the space between lines.
Since there is only one line in your container, align-content is having no effect.
Use align-items in a single line container.
div {
display: flex;
flex-direction: column;
align-items: flex-end; /* changed from align-content */
justify-content: flex-end; /* optional */
background: yellow;
padding: 50px;
width: 400px;
height: 300px;
}
<div>
<p>
Align me in the bottom left corner.
</p>
</div>
revised fiddle
8.4. Packing Flex Lines: the align-content
property
The align-content property aligns a flex container’s lines within
the flex container when there is extra space in the cross-axis,
similar to how justify-content aligns individual items within the
main-axis.
Note, this property has no effect on a single-line flex container.
Only multi-line flex containers ever have free space in the cross-axis
for lines to be aligned in, because in a single-line flex container
the sole line automatically stretches to fill the space.

Images in flex items causing equal width to break

The red borders are around flex items. They have
flex-grow: 1
These items are contained in a flex container:
width: 100%
display: flex
flex-direction: row
align-items: flex-end
As you can see the vertical alignment is working fine.
When I put the images in, the width of the images pushes the flex items to be bigger or smaller than they should be. The width of the images overrides the width of the flex items. What I want to say is something like:
Firstly make the flex items all the same size and then size the contained images (whatever their natural size) to be the size of their container (the flex item). I've tried width: 100% and width: auto on the images. Didn't help.
This image shows the equally spaced flex items (red borders). I want the images to fit into these boxes without causing the width of the box to change. This is the behaviour I get if I replace the flex items with table cells.
This Fiddle shows the 3 equal boxes: https://jsfiddle.net/justinwyllie/zdrd89gu/
.flex-container {
display: flex;
flex-direction: row;
align-items: flex-end;
}
.flex-boxes {
flex-grow: 1;
border: 2px solid red;
}
<div class="flex-container">
<div class="flex-boxes">A</div>
<div class="flex-boxes">B</div>
<div class="flex-boxes">C</div>
</div>
This one shows an image in the middle flex item. It has completely messed up the equal boxes. The question is what do I need to do to make the cat fit into the middle box? (Fit width-wise that is; i don't mind about height).
https://jsfiddle.net/justinwyllie/zdrd89gu/1/
.flex-container {
display: flex;
flex-direction: row;
align-items: flex-end;
}
.flex-boxes {
flex-grow: 1;
border: 2px solid red;
}
.cat {
width: auto;
}
<div class="flex-container">
<div class="flex-boxes">A</div>
<div class="flex-boxes">
<img class="cat" src="http://www.publicdomainpictures.net/pictures/30000/velka/annoyed-cat.jpg" />
</div>
<div class="flex-boxes">C</div>
</div>
The flex-grow property tells flex items how much available space in the container to consume.
In your second image, the empty items are all equal width because flex-grow: 1 tells them to distribute available space equally among themselves.
Any content you put in these items will impact flex-grow since, as mentioned, flex-grow is based on the free space in the container – and content consumes space.
If you want the items to maintain a fixed width, use flex-basis.
So instead of:
flex-grow: 1;
Try:
flex: 0 0 20%; /* don't grow, don't shrink, fixed width at 20% */
The 20% is just an example, if you wanted five items per row.
Here's your revised code:
jsFiddle
.flex-container {
display: flex;
flex-direction: row;
align-items: flex-end;
}
.flex-boxes {
/* flex-grow: 1; <-- REMOVE */
flex: 0 0 33.33%; /* NEW */
border: 2px solid red;
/* box-sizing: border-box; you may need to add this property if
you enable wrapping */
}
.cat {
width: auto;
/* vertical-align: bottom; add this property if you want to remove
the small gap of whitespace underneath images
https://stackoverflow.com/a/31445364/3597276 */
}
<div class="flex-container">
<div class="flex-boxes">A</div>
<div class="flex-boxes">
<img class="cat" src="http://www.publicdomainpictures.net/pictures/30000/velka/annoyed-cat.jpg" />
</div>
<div class="flex-boxes">C</div>
</div>
Learn more here:
flex-grow not sizing flex items as expected