Flex child shrinks when applying "align-self: center" - css

I'd like to horizontally center align a flex child inside a flex container.
However, when the child gets align-self: center it shrinks to width = 0.
Note that both the container and the child have max-width.
How would you solve this?
.container {
display: flex;
flex-direction: column;
max-width: 400px;
outline: 2px solid red;
}
.child {
display: flex;
flex-direction: column;
max-width: 200px;
height: 50px;
background-color: #ccc;
/* This causing the child to shrink to width = 0 */
/* align-self: center; */
}
<div class="container">
<div class="child">
</div>
</div>

The issue is the stretch effect that you disable by changing the alignment of the element. By default align-items is set to stretch thus the element will try to fill its parent width (or height for a row direction).
You can put back this feature using width:100%
.container {
display: flex;
flex-direction: column;
max-width: 400px;
outline: 2px solid red;
}
.child {
display: flex;
flex-direction: column;
max-width: 200px;
height: 50px;
width:100%;
background-color: #ccc;
align-self: center;
}
<div class="container">
<div class="child">
</div>
</div>
align-items sets the default alignment for all of the flex container’s items, including anonymous flex items. align-self allows this default alignment to be overridden for individual flex items.ref

Related

Flex items not filling the height of its parent

I have a container with children items:
HTML
<div id="container">
<div className="item">1</div>
<div className="item">2</div>
<div className="item">3</div>
<div className="item">4</div>
</div>
CSS
#container {
display: flex;
flex-direction: row;
gap: 10px;
}
.item {
flex: 1;
text-align: center;
padding: 0px;
font-size: 30px;
background: #34ace0;
}
This flexbox container sits inside a grid layout, and the cell to the left of the one here has contents which cause the height of the flexbox shown here to be higher than the contents, as shown here:
I need the squares with the numbers inside to stretch/fill the height of its container, like this
...but with the text centered vertically as well.
I tried setting the height of the .item to 100% but it doesn't fill. Is there something like the free-remaining-space used in grid for flexbox?
Make sure the grid layout container has height of 100vh and the container you've shown also has height of 100%.
To center the text inside of each item, you can make each of them display: flex.
.grid-container {
height: 100vh;
}
#container {
height: 100%;
display: flex;
flex-direction: row;
gap: 10px;
}
.item {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex: 1;
padding: 0px;
font-size: 30px;
background: #34ace0;
}

Flex auto margins behave differently in Chrome and Safari

Open this page in Chrome and in Safari and we can see the difference.
In Safari the "Maths" keyword is not on the right side as in Chrome.
Also the widths of the item-3 div is different in Chrome and in Safari.
My issue is that why it is different in Safari and what is the fix (I want CSS fix on the item-3).
flex-wrap: wrap is not a acceptable solution.
.main-container {
display: flex;
flex-direction: column;
width: 400px;
height: 300px;
border: 1px solid red;
}
.item-1 {
display: flex;
border: 1px solid blue;
}
.item-2 {
display: flex;
border: 1px solid green;
}
.item-3 {
display: flex;
margin-left: auto!important;
border: 2px solid yellow;
}
<div class="main-container">
<div class="item-1">Physics</div>
<div class="item-2">Chemistry</div>
<div class="item-3">Maths</div>
</div>
The width of .item-3 is different than its siblings because margin-left: auto packs the flex item to the right side. That's normal behavior for flex auto margins.
The reason margin-left doesn't work in Safari is a mystery / bug.
However, for cross-browser support, there is a simple flex alternative to auto margins in this case: align-self: flex-end
.main-container {
display: flex;
flex-direction: column;
width: 400px;
height: 300px;
border: 1px solid red;
}
.item-1 {
display: flex;
border: 1px solid blue;
}
.item-2 {
display: flex;
border: 1px solid green;
}
.item-3 {
display: flex;
align-self: flex-end; /* NEW */
border: 2px solid yellow;
}
<div class="main-container">
<div class="item-1">Physics</div>
<div class="item-2">Chemistry</div>
<div class="item-3"><span>Maths</span></div>
</div>
If you want .item-3 to keep the full width of the container, then use justify-content: flex-end.
.main-container {
display: flex;
flex-direction: column;
width: 400px;
height: 300px;
border: 1px solid red;
}
.item-1 {
display: flex;
border: 1px solid blue;
}
.item-2 {
display: flex;
border: 1px solid green;
}
.item-3 {
display: flex;
justify-content: flex-end; /* NEW */
border: 2px solid yellow;
}
<div class="main-container">
<div class="item-1">Physics</div>
<div class="item-2">Chemistry</div>
<div class="item-3">Maths</div>
</div>
How align-self: flex-end works
With align-self: flex-end you're shifting .item-3 along the cross axis all the way to the right.
This works because the flex container (.main-container) has flex-direction: column, which makes the main axis vertical and cross axis horizontal.
How justify-content: flex-end works
With justify-content: flex-end you're shifting the children of .item-3 along the main axis all the way to the right.
This works because .item-3 is a flex container with flex-direction: row (by default), which makes the main axis horizontal and cross axis vertical.
Then, as per the specification, text in a flex container that is not explicitly wrapped by an element, is considered an anonymous flex item. This allows justify-content to work.
4. Flex Items
Each in-flow child of a flex container becomes a flex item, and each
contiguous run of text that is directly contained inside a flex
container is wrapped in an anonymous flex item.
Why text-align: right doesn't work
text-align: right won't work because flex items (including anonymous ones) are considered block-level elements. The text-align property applies only to inline-level content.
More information
Learn more about flex alignment along the main axis here:
In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?
Learn more about flex alignment along the cross axis here:
How does flex-wrap work with align-self, align-items and align-content?

align-self: stretch not working in Safari? [duplicate]

I want to have a vertical menu with a specific height.
Each child must fill the height of the parent and have middle-aligned text.
The number of children is random, so I have to work with dynamic values.
Div .container contains a random number of children (.item) that always have to fill the height of the parent. To achieve that I used flexbox.
For making links with text aligned to the middle I am using display: table-cell technique. But using table displays requires using a height 100%.
My problem is that .item-inner { height: 100% } is not working in webkit (Chrome).
Is there a fix for this problem?
Or is there a different technique to make all .item fill the height of the parent with text vertical aligned to middle?
Example here jsFiddle, should be viewed in Firefox and Chrome
.container {
height: 20em;
display: flex;
flex-direction: column;
border: 5px solid black
}
.item {
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
height: 100%;
width: 100%;
display: table;
}
a {
background: orange;
display: table-cell;
vertical-align: middle;
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
Solution
Use nested flex containers.
Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.
Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.
Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).
Try this (no changes to HTML):
.container {
display: flex;
flex-direction: column;
height: 20em;
border: 5px solid black
}
.item {
display: flex; /* new; nested flex container */
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
display: flex; /* new; nested flex container */
flex: 1; /* new */
/* height: 100%; <-- remove; unnecessary */
/* width: 100%; <-- remove; unnecessary */
/* display: table; <-- remove; unnecessary */
}
a {
display: flex; /* new; nested flex container */
flex: 1; /* new */
align-items: center; /* new; vertically center text */
background: orange;
/* display: table-cell; <-- remove; unnecessary */
/* vertical-align: middle; <-- remove; unnecessary */
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
jsFiddle demo
Explanation
My problem is that .item-inner { height: 100% } is not working in
webkit (Chrome).
It's not working because you're using percentage height in a way that doesn't conform with the traditional implementation of the spec.
10.5 Content height: the height property
percentage Specifies a percentage height. The percentage is calculated with respect to the height of the generated box's
containing block. If the height of the containing block is not
specified explicitly and this element is not absolutely positioned, the value computes to auto.
auto The height depends on the values of other properties.
In other words, for percentage height to work on an in-flow child, the parent must have a set height.
In your code, the top-level container has a defined height: .container { height: 20em; }
The third-level container has a defined height: .item-inner { height: 100%; }
But between them, the second-level container – .item – does not have a defined height. Webkit sees that as a missing link.
.item-inner is telling Chrome: give me height: 100%. Chrome looks to the parent (.item) for reference and responds: 100% of what? I don't see anything (ignoring the flex: 1 rule that is there). As a result, it applies height: auto (content height), in accordance with the spec.
Firefox, on the other hand, now accepts a parent's flex height as a reference for the child's percentage height. IE11 and Edge accept flex heights, as well.
Also, Chrome will accept flex-grow as an adequate parent reference if used in conjunction with flex-basis (any numerical value works (auto won't), including flex-basis: 0). As of this writing, however, this solution fails in Safari.
#outer {
display: flex;
flex-direction: column;
height: 300px;
background-color: white;
border: 1px solid red;
}
#middle {
flex-grow: 1;
flex-basis: 1px;
background-color: yellow;
}
#inner {
height: 100%;
background-color: lightgreen;
}
<div id="outer">
<div id="middle">
<div id="inner">
INNER
</div>
</div>
</div>
Four Solutions
1. Specify a height on all parent elements
A reliable cross-browser solution is to specify a height on all parent elements. This prevents missing links, which Webkit-based browsers consider a violation of the spec.
Note that min-height and max-height are not acceptable. It must be the height property.
More details here: Working with the CSS height property and percentage values
2. CSS Relative & Absolute Positioning
Apply position: relative to the parent and position: absolute to the child.
Size the child with height: 100% and width: 100%, or use the offset properties: top: 0, right: 0, bottom: 0, left: 0.
With absolute positioning, percentage height works without a specified height on the parent.
3. Remove unnecessary HTML containers (recommended)
Is there a need for two containers around button? Why not remove .item or .item-inner, or both? Although button elements sometimes fail as flex containers, they can be flex items. Consider making button a child of .container or .item, and removing gratuitous mark-up.
Here's an example:
.container {
height: 20em;
display: flex;
flex-direction: column;
border: 5px solid black
}
a {
flex: 1;
background: orange;
border-bottom: 1px solid white;
display: flex; /* nested flex container (for aligning text) */
align-items: center; /* center text vertically */
justify-content: center; /* center text horizontally */
}
<div class="container">
<a>Button</a>
<a>Button</a>
<a>Button</a>
</div>
4. Nested Flex Containers (recommended)
Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.
Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.
Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).
Try this (no changes to HTML):
.container {
display: flex;
flex-direction: column;
height: 20em;
border: 5px solid black
}
.item {
display: flex; /* new; nested flex container */
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
display: flex; /* new; nested flex container */
flex: 1; /* new */
/* height: 100%; <-- remove; unnecessary */
/* width: 100%; <-- remove; unnecessary */
/* display: table; <-- remove; unnecessary */
}
a {
display: flex; /* new; nested flex container */
flex: 1; /* new */
align-items: center; /* new; vertically center text */
background: orange;
/* display: table-cell; <-- remove; unnecessary */
/* vertical-align: middle; <-- remove; unnecessary */
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
jsFiddle
Specifying a flex attribute to the container worked for me:
.container {
flex: 0 0 auto;
}
This ensures the height is set and doesn't grow either.
Solution: Remove height: 100% in .item-inner and add display: flex in .item
Demo: https://codepen.io/tronghiep92/pen/NvzVoo
For Mobile Safari There is a Browser fix. you need to add -webkit-box for iOS devices.
Ex.
display: flex;
display: -webkit-box;
flex-direction: column;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
align-items: stretch;
if you're using align-items: stretch; property for parent element, remove the height : 100% from the child element.
I have had a similar issue in iOS 8, 9 and 10 and the info above couldn't fix it, however I did discover a solution after a day of working on this. Granted it won't work for everyone but in my case my items were stacked in a column and had 0 height when it should have been content height. Switching the css to be row and wrap fixed the issue. This only works if you have a single item and they are stacked but since it took me a day to find this out I thought I should share my fix!
.wrapper {
flex-direction: column; // <-- Remove this line
flex-direction: row; // <-- replace it with
flex-wrap: wrap; // <-- Add wrapping
}
.item {
width: 100%;
}

Why are these flex items spaced in this way? [duplicate]

This question already has an answer here:
Remove space (gaps) between multiple lines of flex items when they wrap
(1 answer)
Closed 5 years ago.
I have made a simple flexbox jsfiddle To play around with all flexbox values, but stumbled upon something that I can't explain my .item divs are spaced out for some reason and .grid is automatically stretching to full height, I'm not entirely sure why this happens?
HTML
<div class="container">
<div class="grid">
<div class="item red">a</div>
<div class="item yellow">b</div>
<div class="item blue">c</div>
</div>
</div>
CSS
.container {
width: 320px;
height: 480px;
background: black;
padding:15px;
margin: 20px auto;
display: flex;
}
.grid {
background: white;
display: flex;
width: 100%;
justify-content: flex-start;
align-items: flex-start;
flex-direction: row;
flex-wrap: wrap;
}
.item {
width: 100%;
flex-grow: 0;
flex-basis: 100%;
align-self: auto;
align-items: flex-start;
padding: 10px 0;
}
.red { background: red; }
.yellow { background: yellow; }
.blue { background: blue; }
The align-items: flex-start (set on .grid) causes this type of behavior. As specified in the MDN docs
The CSS align-items property defines how the browser distributes space between and around flex items along the cross-axis of their container.
If you disable it, the value will be set to stretch by default (each flex item will be stretched to fill the container).

Multiply bested DIV, inner div full height with flexbox only - possible?

i have a complex layout in which i have a div generated by JavaScript with dynamic height, he is called ".outer" . This div has some nested divs, finally leading to a div called ".target". I need the target div to be at ".outers" height. And i don't want to address the inner divs since they are varying markup generated by my JS Framework.
I do not want to set the height via JS, i can not set position to absolute nor relative in ".outer"
HTML:
<div class="outer">
<div class="inner">
<div class="anotherInner">
<div class="target">
This div should be outer's height no matter how many divs are between target and outer like ".inner"
</div>
</div>
</div>
</div>
CSS:
body {
background: #000;
}
.outer {
background: #333;
height: 500px; /* this is not a pixel value, only for example*/
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: flex-start;
align-content: stretch;
align-items: stretch;
}
.inner {
background: #555;
}
.target {
background: #777;
order: 0;
flex: 1 1 auto;
align-self: auto;
}
Example:
http://codepen.io/anon/pen/akwWZK?editors=1100
The only possible way to do this in FlexBox is to address the .inner div's in your CSS by adding display: flex; to them.
I've created a working example for you here: http://codepen.io/mattpark22/pen/rLwwZp
To '.inner' add:
display: flex;
To '.target' add:
height: 100%;
To '.outer' change, flex-direction to:
flex-direction: row;
My codepen example also cleans up some CSS which isn't required.

Resources