CSS Grid - unnecessary word break - css

I have a problem with CSS grid.
In the following snippet extracted from the codebase, I have a very simple HTML structure with grid layout. Content is set to break-word to prevent text from overflowing. Event though there is enough space for the text to NOT get broken, it does create a line break just before the last letter.
My understanding was that in grid layout, by default, items are calculated to fit the content as much as possible, which is somehow not the case in this example.
Removing padding from content or margins from grid item does solve the issue, but margin is there for centering and padding is also needed.
Is there any property I have to or can use to solve this problem?
P.S. To my knowledge, the bug is not present in Firefox, I have found it in Chrome and Safari so far.
.grid {
display: grid;
grid-template-columns: auto;
}
.item {
margin: 0 auto;
}
p {
word-break: break-word;
padding: 0 4%;
}
<div class="grid">
<div class="item">
<p>HOTEL</p>
<p>GRAND</p>
</div>
</div>

It's not a bug but a complex calculation.
There is a kind of cycle to calculate the final width of the element which create the issue. The width is first calculated considering the content (a shrink-to-fit behavior based on the properties you used) then using percentage value with padding will consider the calculated width1. At the end, we will reduce the calculated padding from the width creating the word break.
This will happen with the smallest value since in all the cases the width will always be less than the needed width to contain the longest word:
.grid {
display: grid;
grid-template-columns: auto;
}
.item {
margin:auto;
border:1px solid;
}
.pad p {
word-break: break-word;
padding: 0 1%;
}
<div class="grid">
<div class="item">
<p>HOTEL</p>
<p>I_WILL_FOR_SURE_BREAK</p>
</div>
</div>
<div class="grid">
<div class="item pad">
<p>HOTEL</p>
<p>I_WILL_FOR_SURE_BREAK</p>
</div>
</div>
As you can see, the first grid with padding is shrinked to its content and the second grid is having exactly the same width and the padding is creating the line break.
An easy fix is to use pixel value instead of percentage in case you know the value you want:
.grid {
display: grid;
grid-template-columns: auto;
justify-content:center;
}
.item {
margin:auto;
border:1px solid;
}
.pad p {
word-break: break-word;
padding: 0 20px;
}
<div class="grid">
<div class="item">
<p>HOTEL</p>
<p>I_WILL_NOT_BREAK</p>
</div>
</div>
<div class="grid">
<div class="item pad">
<p>HOTEL</p>
<p>I_WILL_NOT_BREAK</p>
</div>
</div>
Why you don't see this on firefox?
Simply because break-word is not supported there (https://developer.mozilla.org/en-US/docs/Web/CSS/word-break)
So you will have an overflow instead of a word break. You may notice this behavior on firefox if you use break-all:
.grid {
display: grid;
grid-template-columns: auto;
}
.item {
margin:auto;
border:1px solid;
}
p {
word-break: break-all;
padding: 0 1%;
}
<div class="grid">
<div class="item">
<p>HOTEL</p>
<p>I_WILL_FOR_SURE_BREAK</p>
</div>
</div>
1: The size of the padding as a percentage, relative to the width of the containing block.ref

Related

how to make columns the same width with css flex

hello I need to basicaly display a table but with flex (only because I need to adjust the look for mobile)
.container {
display: flex
}
<div class="container">
<div class="row">
<div>1000</div>
<div>mary</div>
</div>
<div class="row">
<div>1</div>
<div>john</div>
</div>
<div class="row">
<div>11</div>
<div>mike</div>
</div>
</div>
how can I make each column have the same width? or at least the width of the largest item.
If you apply flex-grow: 1 to each div in the .row then it will expand accordingly. Note that the .row divs need the display flex (flex only apply to the DIRECT children of the flexed element.
The width can be set by using flex-basis and calc() and dividing the full-width by the number of columns you want (2 in this case). I added a border to demonstrate.
I would be remiss if I didn't suggest that the best tool to display a table structure - is a ... table... then you can work out how to modifiy for responsive layout - but the semantic structure of a table is correct for tabulr content.
.container {
width: 100%;
}
.row {
display: flex;
width: 100%;
}
.row div {
flex-grow: 1;
flex-basis: calc(100% / 2);
padding: 4px 8px;
border: solid 1px red;
}
<div class="container">
<div class="row">
<div>1000</div>
<div>mary</div>
</div>
<div class="row">
<div>1</div>
<div>john</div>
</div>
<div class="row">
<div>11</div>
<div>mike</div>
</div>
</div>
Use flex-basis to set the 'default' width first.
Use flex-grow and/or flex-shrink to allow it to grow /shrink
Use max-width' and min-width` to define by how much it can grow/shrink
.container{ #Stack the div inside the conainer
display:flex;
flex-flow: columns nowrap;
}
.row{ # Set div as cell in the row.
display:flex;
flex-flow: row nowrap;
flex-basis: 33%; # Calculate width from there.
flex-grow:1; #Allow resizing bigger
flex-shrink:1; #Allow resize smaller.
max-width:50%; #Adjust to your liking
min-width:25%; #Adjust o your liking
}
Please consider this ressource https://css-tricks.com/snippets/css/a-guide-to-flexbox/
You can add a flex-basis to the .row items by doing this:
.row {
flex-basis:calc(100% / 3);
}
.container {
width: 100%;
}
.row {
display: flex;
width: 100%;
}
.row div {
flex-grow: 1;
flex-basis: calc(100% / 2);
padding:10px;
margin: 5px;
background: #eee;
}
<div class="container">
<div class="row">
<div>1000</div>
<div>mary</div>
</div>
<div class="row">
<div>1</div>
<div>john</div>
</div>
<div class="row">
<div>11</div>
<div>mike</div>
</div>
</div>

How do I best use CSS to make a page with tall nested elements to be limited to the height of the viewport?

How do I best enforce a page to be limited to the height of the viewport? I would like to manage the overflow-y on children elements, but it seems I cannot get the height to constrain--I can only manage a max-height in pixels or a percentage. I think I need to constrain the height of certain elements to the remaining portion (not a percentage) of the viewport height, but I can't find a way to do that.
(I am using bootstrap, but I suspect the answer isn't bootstrap-centric.)
I thought maybe I could use flex with flex-grow, but that doesn't constrain the height either. What am I missing?
<div class="container d-flex flex-column ">
<div>
My full-width page title goes here
</div>
<div class="row flex-grow-1">
<div class="col col-4">
potentially long content here
</div>
<div class="col-8">
main content
</div>
</div>
</div>
With CSS
body {
height: 100vh;
}
.row {
margin-top: 20px;
}
.col {
border: solid 1px black;
padding: 10px;
overflow-y: scroll;
}
(Fiddle)
Actual: Page is unlimited height; inner scroll bar is moot; viewport scroll bar is on
Desired: Page uses the whole height of the viewport and no more; inner scroll bar effective; no viewport scroll bar
If you aren't married to using bootstrap/flexbox, css grid makes this pretty easy.
HTML
<div class="wrapper">
<div class="header">
My full-width page title goes here
</div>
<div class="sidebar">
sidebar content
</div>
<div class="main">
main content
</div>
</div>
CSS
html, body, .wrapper {
height: 100%;
}
.wrapper {
display: grid;
grid-template-columns: 1fr 2fr;
grid-auto-rows: min-content auto;
grid-template-areas:
"header header"
"sidebar main";
}
.header {
grid-area: header;
background: red;
}
.sidebar {
grid-area: sidebar;
overflow: auto;
}
.main {
grid-area: main;
}
fiddle

The relative unit (percentage) cannot be applied by the browser to the space set by fr

Problem details
I wrote code like this. Here, the image should be spread by the space reserved by fr because thewidth, height, andobject-fit properties work. So I thought the text of the second .item would overflow.
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
In Firefox this causes text to run out,
Not so with Chrome.
Also, wrapping the image in a div element instead of just below the grid item will fix the problem.
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item">
<div class="content">
<img src="https://unsplash.it/450/450">
</div>
</div>
<div class="item">
<div class="content">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
</div>
Unrelated but similar articles
I found a question that might be related, but I don't think this problem is related to my question because the source code of this question text works correctly in Firefox and Chrome.
Chrome / Safari not filling 100% height of flex parent
Heights rendering differently in Chrome and Firefox
Using 'height: 100%' and 'align-items: stretch' in flexbox
Why is my Grid element's height not being calculated correctly?
Unrelated but similar bugs
I also looked for related bug tickets, but these were already stated to have been fixed and may not be relevant to my problem.
https://bugs.chromium.org/p/chromium/issues/detail?id=426898
https://github.com/philipwalton/flexbugs/issues/197
Quesiton
Is this difference a bug without a bug ticket or undefined behavior?
If this is not an undefined behavior, which is the correct behavior?
If this is not an undefined behavior, which is the correct behavior?
I would say, Firefox is doing correctly here and Chrome is half correct.
First, you can reduce your code to the following as grid-template-rows: 1fr is not needed to have the behavior
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
Then you can use any percentage value (either smaller or bigger than 100%) and you will see that chrome will do nothing in the second case where we have text:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 50%;
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
As I explained here you are facing a particular behavior of percentage height. By default percentage height is relative to the explicit height of the containing block and in our case we didn't set any explicit height to our row (1fr isn't explicit). Considering the CSS2 specification we should fail to auto
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 (i.e., it depends on content height), and this element is not absolutely positioned, the value computes to 'auto'. ref
Chrome is doing this with the second image. It consider the height to be auto which is not completely wrong.
In the CSS3 specification we have another section that allow the browser to do more effort:
Sometimes the size of a percentage-sized box’s containing block depends on the intrinsic size contribution of the box itself, creating a cyclic dependency. When calculating the intrinsic size contribution of such a box (including any calculations for a content-based automatic minimum size), a cyclic percentage—that is, a percentage value that would resolve against a containing block size which itself depends on that percentage—is resolved specially: ref
Then you have a set of complex rules and the two importants parts are:
Otherwise, the percentage is resolved against the containing block’s size. (The containing block’s size is not re-resolved based on the resulting size of the box; the contents might thus overflow or underflow the containing block).
And one rule with the note:
Note: Grid items and flex items do allow percentages to resolve in this case.
To make it easy, the browser will first ignore the percentage height to calculate the height of each grid track based on the content to have the following:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
/*height: 100%;*/
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
Then considering the previous calculated height of grid track we resolve the percentage height to get the following and we don't get back to recalculate the height of the track again:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
The text will logically overflow since the height was defined by the image+text and we told to the image to take all the height making the text outside and making the image filling all the space.
Using any percentage value will give the same result which is make the image X% of the height defined by the image+text.
Setting an explicit height will make both behave the same since we have no complex calculation and we can rely on the CSS2 part of the specification:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows:200px;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
Adding an extra wrapper will make the height fail to auto in all the cases as it become more complex1 for browsers to handle such case.
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 958%;
object-fit: cover;
}
<div class="container">
<div class="item">
<div>
<img src="https://unsplash.it/450/450">
</div>
</div>
<div class="item">
<div>
<img src="https://unsplash.it/450/450">
</div>
text
</div>
</div>
Adding height:100% to the extra wrapper will put as back to the previous cases and height can be resolved again:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 50%;
object-fit: cover;
}
.item>div {
height: 100%;
}
<div class="container">
<div class="item">
<div>
<img src="https://unsplash.it/450/450">
</div>
</div>
<div class="item">
<div>
<img src="https://unsplash.it/450/450">
</div>
text
</div>
</div>
In this case, the div is taking the height defined by the image+text (the text will overflow) then the image inside the div will take half that height.
Related questions with similar situations:
https://stackoverflow.com/a/52137966/8620333
Chrome / Firefox percentage height differences in CSS Grid
Grid gap percentage without height
1: I won't be able to give the exact part explaining why we cannot resolve in this case.

Using calc() with a dynamic value?

I am wondering if this is possible: I have a header that can contain a variable amount of text. Below that I have another element which I want to take up the remaining height of the page.
<div class="header row">
<div class="title column large-5">Potentially very long text</div>
<div class="menu column large-7">Menu items</div>
</div>
<div class="content">
</div>
<div class="footer">
</div>
Normally I would do this using calc, eg:
.content {
height: calc(100vh - 75px);
}
Where 75px is the set height of .header.
But in this example, the .header element is dynamic and does not have a set height. Only a padding and font-size are set.
To complicate things, this also uses the Foundation Grid layout, which makes me nervous about using display: table (.title and .menu sit side by side on desktop, but stacked on mobile) .
Is there anyway to get the height of the dynamic header element (without resorting to JQuery)?
You can use flexbox and set .content to flex-grow: 1 so that it will fill to grow the available space.
body {
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
}
.content {
flex-grow: 1;
background: #eee;
}
<div class="header row">
<div class="title column large-5">Potentially very long text</div>
<div class="menu column large-7">Menu items</div>
</div>
<div class="content">
</div>
<div class="footer">
</div>
I made a small pen to show the way to do this using flex box, it involved changing your markup a bit:
css:
.container {
display: flex;
flex-direction: column;
height: 250px; // whatever you want here
}
.header {
width: 100%;
background: red;
padding: 10px;
}
.content {
background: yellow;
width: 100%;
flex-grow: 1;
}
So the content will always take the available space inside the content div.
check the whole pen: http://codepen.io/anshul119/pen/yMYeLa
hope this helps.

holy grial layout with flex

I just use flex for layout, the markup like this:
<div class="grid">
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
</div>
and the normal view like this:
layout structure - default
when in a breakpoint, view like this:
layout structure - responsive
the problem is, do not change the markup and just use flex, how can I make this?
With flex, you may use min-width to set a breaking-point.
example with 2 childs at:
min-width:200px;
margin:10px;
padding:10px;
flex:1
makes a first break point at 200px + 40px margin + 40px padding = ~ 480px (mind borders too or reset box-sizing properties)inside the flex container .
Example with your structure breaking at average 750px
.grid {
display: flex;
flex-wrap: wrap;
border: solid;
}
.grid div {
padding: 10px;
margin: 10px;
flex: 1;
border: solid;
min-width: 200px;
/* from 3 childs: avearge breaking points */
/* first breaking-point at 200 x 3 + padding/margin = 730px within flex-container */
/*second breaking-point at 200 x 2 + padding/margin = 480px within flex-container */
}
<div class="grid">
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
</div>

Resources