I am currently using CSS grid, and I'm using the grid-row-gap and column-row-gap many places. My problem is that when doing media queries, and some elements/divs are not to be shown at smaller resolution I usually just set them to display: none. However, if I'm not mistaken, even though the element can be seen, the row or column gap feature still applies on the "hidden" element.
Is there any way to fix this, or am I doing something wrong here?
EDIT:
Okay, it seems like display: none don't affect the grid-row-gap - which makes sense. But I found a small workaround by removing the grid-gap and adding a padding instead when the resolution changes. And that seems to do the trick.
Thanks for all the answers.
Is there any way to fix this [...]?
One simple fix when applying display: none; to child elements in a CSS grid, is to remember to alter the parent grid accordingly to take account of the fact that those child elements are no longer displayed.
Working Example:
body > div {
display: inline-grid;
grid-template-rows: auto auto auto;
grid-template-columns: auto auto auto;
grid-row-gap: 6px;
grid-column-gap: 12px;
width: 120px;
height: 120px;
margin-right: 24px;
border: 1px solid rgb(255, 0, 0);
}
div div {
border: 1px solid rgb(255, 0, 0);
}
body > div:hover div:nth-of-type(n+7) {
display: none;
}
body > div:nth-of-type(2):hover {
grid-template-rows: auto auto;
height: 80px;
}
<div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
In both the CSS Grids above the final three child elements are removed from display on :hover. But in the second grid (only) the height of the parent and the number of grid-template-rows are also reduced, thus avoiding displaying a gap which the non-displayed child elements used to occupy.
You can do this if you use grid-auto-columns (or grid-auto-rows) because the column definitions are dynamic, therefore no column exists if the item is hidden.
In this example I'm using grid-auto-flow: column which is sort of similar to flex-direction: row in this example. It's easier to visualize what's going on if they are columns vs. rows.
Note: grid-auto-flow is supported in IE, but *-auto-columns is.
Important: Don't confuse this with grid-template-columns: auto !
.outer-container
{
background: yellow;
outline: 1px solid black;
}
.container
{
display: grid;
grid-auto-columns: 1fr;
grid-auto-flow: column;
grid-gap: 10px;
}
.item
{
background: steelblue;
padding: .5em 1em;
color: white;
}
.item.hidden
{
display: none;
}
<div class="outer-container">
<div class="container">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item hidden">Item 3 (hidden)</div>
</div>
</div>
Related
This question already has answers here:
How to make the items in the last row consume remaining space in CSS Grid?
(6 answers)
Closed 2 years ago.
I'm trying to craft a layout with the CSS grid.
At first, I handled it with grid maker sites, well everything was reliable before I realize that the problem is that: my layout is not dynamic.
See the below picture it is what I wanted to do and did it:
Now when I remove one of the grid children this happens:
So it is my question: how I can make something that is dynamic?
I mean when I do not have enough items another items be fitted in the wrapper, something like this:
grid allow to combine row and columns, flex, is one or the other.
What you can do , is to set the CSS to match different cases :
demo from 5 to one child
html {
display: grid;
min-height: 100vh;
}
body {
margin: auto;
display: flex;
flex-wrap:wrap;
}
.grid {
width: 40vh;
height: 20vh;
margin: auto;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 5px;
margin: 5px;
padding: 5px;
background: tomato;
counter-reset:divs
}
.grid:nth-child(even) {
background: crimson;
}
.grid div:before {counter-increment:divs;content:counter(divs)}
.grid div {
background: gray;
}
.grid div:nth-child(1),
.grid div:nth-last-child(1):nth-child(2) {
grid-row: span 2;
grid-column: span 2;
}
.grid div:nth-last-child(1):nth-child(even) {
grid-column: span 2;
}
.grid div:nth-last-child(2):nth-child(2),
.grid div:nth-last-child(2):nth-child(2) + div {
grid-column: span 2;
}
.grid div:last-child:first-child {
grid-column: span 4;
}
<div class="grid">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="grid">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="grid">
<div></div>
<div></div>
<div></div>
</div>
<div class="grid">
<div></div>
<div></div>
</div>
<div class="grid">
<div></div>
</div>
I have a row of items of arbitrary width. They are centered within the container (note white space on the left and right sides of the red container):
Sometimes the container gets smaller than the width of all items:
When this happens, I want the items in the end to wrap to the next row like this:
It is very imporant for me that each row's content must be left-aligned, but the grid as a whole must be centered:
Initially, I tried implementing it with FlexBox. After a lot of frustration and hair pulling, I've learned that this is impossible witn FlexBox: https://stackoverflow.com/a/32811002/901944
Another answer on the same page suggests using CSS grid instead of flexbox.
CSS grid produces a slightly different result, but that also suits me:
Here's the code that makes it work:
.red-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(210px, max-content));
justify-content: center;
}
This code contains a lot of keywords that I don't understand: grid-template-columns, repeat, auto-fit, minmax and max-content. I tried reading up on them and failed. None of guides and API docs explicitly explain how this particualr combination works. MDN docs are way too short and cryptic.
What I specifically struggle with is this 210px magic number. Why is it necessary? (Erm, I know it's necessary because how the spec is designed, but this does not help me understand.)
The sizes of items in my grid are arbitrary, so I can't use a fixed value. Also, setting this fixed value makes the result slightly off: small items grow and large items overflow the container.
What I essentially want is:
grid-template-columns: repeat(auto-fit, minmax(min-content, max-content));
but that rule is recognized by browsers as faulty.
I've stumbled upon this answer that explains that using both min-content and max-content together is forbidden by the spec in this context. The answer's suggested solution is... to use Flexbox!
The loop has closed. I'm back to where I started, expect that I'm now lacking hair on my head for another round.
How to do I center my grid while left-aligning each row's content, with items having arbitrary widths?
Here's a boilerplate to fiddle with for your convenience: https://jsbin.com/vuguhoj/edit?html,css,output
The container can be resized by dragging it by the bottom-right corner.
PS No display: inline and float: left please.
.page {
border: 1px solid black;
overflow: hidden;
resize: horizontal;
max-width: 500px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(max-content, 50px));
justify-content: center;
}
.item {
border: 1px solid black;
margin: 1px;
}
<div class="page">
<div class="grid">
<div class="item">
Foofoofoo
</div>
<div class="item">
Bar
</div>
<div class="item">
BazBaz
</div>
<div class="item">
QuuxQuuxQuux
</div>
</div>
</div>
CSS grid approach-
root answer - joe82
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(179px, max-content));
grid-gap: 10px;
justify-content: center;
background: #999;
overflow: hidden;
padding: 10px;
}
.background {
width: 179px;
height: 64px;
background: #99d9ea;
}
.background .child {
border: 2px solid #000;
}
.background:nth-child(1) .child {
width: 110px;
height: 50px;
}
.background:nth-child(2) .child {
width: 120px;
height: 60px;
}
.background:nth-child(3) .child {
width: 50px;
height: 55px;
}
.background:nth-child(4) .child {
width: 175px;
height: 40px;
}
<div class="container">
<div class="background"><div class="child"></div></div>
<div class="background"><div class="child"></div></div>
<div class="background"><div class="child"></div></div>
<div class="background"><div class="child"></div></div>
</div>
Newbie here!
I admit that the above one isn't the output you desired, but it is my best attempt towards it, with the use of CSS grids. Here, you can understand that if we want to make it responsive then a minimum width is required after which, the column(content) will get carried to the next line, and that width is defined here(grid-template-columns: repeat(auto-fit, minmax(204px, max-content));) 204px. But because of it each column will take that much width at least, that's why I represented the actual dimension of a column with blue background and the actual content within the border.I just post it for your acknowledgment and approach so that you can get closer to the actual answer.
By the way, Flex Approach-
root idea - random COSMOS
.container {
min-width: 130px;
max-width: 340px;
overflow: auto;
background: #999;
padding: 10px 40px;
resize: horizontal;
border: 1px solid #000;
}
.main-content {
display: flex;
flex-wrap: wrap;
background: #fff;
max-width: 340px;
overflow: hidden;
}
.child {
margin: 5px;
padding: 5px;
background: #99d9ea;
border: 1px solid black;
}
<div class="container">
<div class="main-content">
<div class="child">Foofoofoo</div>
<div class="child">Bar</div>
<div class="child">BazBaz</div>
<div class="child">QuuxQuuxQuux</div>
</div>
</div>
Resize the window or the above div to see the results
The above tells that the div is centered and the content is at left but not resizing according to content.
My personal opinion -
You should use #media for making it responsize, just as the way you want it to be, It is just like coding a lot for a simple output but it can give you the best and satisfying results out of your hard work and time!
Kindly inform me if you want me to make it responsize for you, I mean just like a demo-
Regard,
Om Chaudhary
If we go for center we don't get the left-align thing:
.page {
border: 1px solid black;
max-width: 500px;
resize: horizontal;
overflow: hidden;
display: flex;
justify-content: center;
padding: 0 50px;
}
.grid {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.item {
border: 1px solid black;
margin: 1px;
}
<div class="page">
<div class="grid">
<div class="item">Foofoofoo</div>
<div class="item">Bar</div>
<div class="item">BazBaz</div>
<div class="item">QuuxQuuxQuux</div>
</div>
</div>
Now with left-align (as in #Michael Benjamin answer) it doesn't do the center stuff:
.page {
border: 1px solid black;
max-width: 500px;
resize: horizontal;
overflow: hidden;
display: flex;
justify-content: center;
padding: 0 50px;
}
.grid {
display: flex;
flex-wrap: wrap;
}
.item {
border: 1px solid black;
margin: 1px;
}
<div class="page">
<div class="grid">
<div class="item">Foofoofoo</div>
<div class="item">Bar</div>
<div class="item">BazBaz</div>
<div class="item">QuuxQuuxQuux</div>
</div>
</div>
Why?
Because in the second code and in #Michael Benjamin code the .grid is technically centered. Visual:
See the whole div is centered. The problem is that the .grid div doesn't change its width according to the content inside it. But, according to the width of its parent div (.page).
I know it doesnt solve your problem, but I am just trying to make sure that now you understand the main problem. So, maybe you can find the solution in another way.
For this case, I see the only solution is to use JavaScript.
In this code we get each .item width. Then we set .flex width = total .item width. If the total .item width is smaller then the .page width - we set .flex width = ((total .item width) - (last .item width)). I hope the JS is pretty readable. If you need more explanation - can give it in comments.
Note that this is not wary flexible and universal solution, But it works good in this particular case, because it was written for it.
This snippet you can test only on browser window size change. Better to check in using Snippet Full page and Chrome console with Devise toolbar mode. Or here https://jsfiddle.net/focusstyle/sh6dnLvt/1/
window.addEventListener("resize", function() {
flexWidth();
});
function flexWidth() {
let page = document.querySelector('.page');
let flex = document.querySelector('.flex');
let totalWidth = biggesWidth = itemWidth = lastWidth = 0;
let flexItem = document.querySelectorAll('.item');
for (let i = 0; i < flexItem.length; i += 1) {
itemWidth = flexItem[i].offsetWidth;
if (biggesWidth<itemWidth) {
biggesWidth = itemWidth;
}
biggesWidth = flexItem[i].offsetWidth;
totalWidth += itemWidth;
lastWidth = itemWidth;
}
if (totalWidth > page.clientWidth) {
totalWidth = totalWidth - lastWidth;
}
totalWidth += 1;
flex.style.cssText = "min-width: "+biggesWidth+"px; max-width: "+totalWidth+"px;";
}
.page {
border: 1px solid black;
overflow: hidden;
text-align: center;
}
.flex {
display: inline-flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.item {
border: 1px solid black;
}
<div class="page">
<div class="flex">
<div class="item">
Foofoofoo
</div>
<div class="item">
Bar
</div>
<div class="item">
BazBaz
</div>
<div class="item">
QuuxQuuxQuux
</div>
</div>
</div>
Let's understand the 210px first. When you write the below code:
grid-template-columns: repeat(auto-fit, minmax(210px, max-content));
The browser knows when to wrap the items around. It ensures that your grid items will always be wider than 210px or at least equal to 210px.
If the browser has 420px width available, it will put 2 items in a row.
If the browser has 630px width available, it will put 3 items in a row, and so on...
You can learn about CSS grids here
If you still don't want to have a min-content of 210px, you can always wite media queries in CSS.
Another thing that may suit your requirements is giving a min-width and max-width to your grid-items.
Hopefully, it saves some of your hair.
You have two containers available – .page and .grid.
This enables you to distribute the two tasks – centering and left-alignment.
Use the top-level container for centering.
Use the nested container for wrapping and left alignment.
Here's a code concept:
.page {
display: grid;
grid-template-columns: 50px 1fr 50px;
border: 1px solid black;
max-width: 500px;
}
.grid {
grid-column: 2;
justify-self: center;
display: flex;
flex-wrap: wrap;
}
.item {
border: 1px solid black;
margin: 1px;
}
<div class="page">
<div class="grid">
<div class="item">Foofoofoo</div>
<div class="item">Bar</div>
<div class="item">BazBaz</div>
<div class="item">QuuxQuuxQuux</div>
</div>
</div>
jsFiddle demo
I don't know if I'm missing something but I think this is possible to achieve this with flexboxes.
You have to get this structure :
<div class="container">
<div class="row">
<div class="item item-1"></div>
<div class="item item-2"></div>
<div class="item item-3"></div>
<div class="item item-4"></div>
</div>
</div>
where the row has a max size and margin: 0 auto (to be centered)
Here is my example on Codepen
i am trying to do the below steps to have my grids as i want:
How to set auto-margin boxes in flexible-width design using CSS?
The solution that they offer here is nice, and its works if i put this code outside angular, but the problem is when i insert this inside angular component, for example app-root
if i put the below code, outside the component(inside the index.html) works, but in another componet not.
i thinks that some functionality of angualr is breaking my basic css code, because if outside of app-root components works and inside of it doesnt, but i dont have any style, is strange.
I would suggest to use flex for layout instead. No tricks needed, flex support it all. Try this:
.container {
border: 2px dashed #444;
min-width: 800px;
max-width: 1400px;
/*No tricks needed, flex support it all*/
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.container > div {
margin-top: 16px;
border: 1px dashed #f0f;
width: 200px;
height: 200px;
display: inline-block;
}
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
I have a number of tiles, all the same, static width and height inside some container. The width of the container is dynamic. The number of tiles is dynamic. I need the tiles to "wrap" such that as many as possible fit on each row but a new row is created when the number of tiles fills up the width of the container. A full row of tiles should be centered. A row of tiles that is not full should be left aligned with a full row (or positioned as if it was full and centered). See the diagram below.
In this diagram, the green represents the container, the black represents the tiles.
I can get close by making the tiles inline-block and using text-align: center on the container. However, that comes out like the following instead:
Here's a code sample. I am able to make necessary changes to the HTML.
.container {
border: 5px solid #0f0;
width: 250px;
text-align: center;
}
.tile {
margin: 3px;
display: inline-block;
border: 5px solid #000;
width: 50px;
height: 40px;
}
<div class="container">
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
</div>
TL;DR
There is a way to do it with the CSS Grid Layout. One has to put the following properties to the parent element, which is the .container element in this scenario:
display: grid;
grid-template-columns: repeat(auto-fill, 60px);
grid-gap: 10px;
justify-content: center;
Some explanation and details
I've tried a lot to make this work, but in the end I was only successful with the CSS Grid Layout which I used for the first time here. I bet there are more readers that aren't very familiar with the above properties so I will add one or two lines of explanation for each.
display: grid;
There's not much to say about this, apart from the limitations in browser support.
grid-template-columns: repeat(auto-fill, 60px);
The grid-template-columns CSS property defines the line names and track sizing functions of the grid columns.
This property is a bit more complicated as there are many different options for its value. Here we use the repeat() function which requires the number of columns as first argument and the width of each column cell as second argument. Fortunately, it's possible to define a variable column number by using auto-fill. The width of 60px is the width of the .tile (50px for the width and 10px for the border).
grid-gap: 10px;
The grid-gap CSS property is a shorthand property for grid-row-gap and grid-column-gap specifying the gutters between grid rows and columns.
To define different gap widths for columns and rows you can use grid-gap: 5px 10px; where the first value is for the row-gap. I randomly selected 10px for this example.
justify-content: center;
Finally let's tell the browser to center the content of our container (the grid) if there is space around. This property is not related to the CSS Grid Layout. It is usually used in flexbox scenarios but works like a charm here as well.
Demo
I added the described four properties to the .container and removed the margin as well as the width from the .tile. Solution tested in Firefox 52 and Chrome 57.
.container {
border: 5px solid #0f0;
width: 250px;
display: grid;
grid-template-columns: repeat(auto-fill, 60px);
grid-gap: 10px;
justify-content: center;
}
.tile {
display: inline-block;
border: 5px solid #000;
height: 40px;
}
<div class="container">
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
</div>
Nice to know
In the FF developer console (F12) there is a built-in grid highlighter that allows easy debugging of the defined grid:
You could use flexbox, but if the last row doesn't have enough elements you'll have a similar issue as with inline-block.
You can use :before and :after to fill out the last row, but if more than 2 elements are missing from the last row it won't help entirely.
div {
display: flex;
align-items: stretch;
justify-content: center;
flex-wrap: wrap;
}
div > * {
margin: 1rem;
}
div::before,
div::after {
background: red; /* For demo only */
display: block;
content: "";
width: 200px;
height: 200px;
margin: 1rem;
order: 99999999;
}
<div>
<img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200">
</div>
Edit: Looking at my own example I realise this doesn't solve your problem. I'll delete this post unless it helps you somehow anyway.
Edit2: You may actually have to resolve to adding some empty divs using JS to fill out the void. That should work though I'd definitely consider it an ugly hack.
Can you please try the below,
.container {
border: 5px solid #0f0;
width: 250px;
}
.tile {
margin: 5px 10px;
display: inline-block;
border: 5px solid #000;
width: 50px;
height: 40px;
text-align: center;
}
<div class="container">
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
</div>
All I did is removed the text-align from .container class and moved it to .tile class. Added a margin to space the elements. You can adjust the 5px 10px based on your needs. Am able to match the output to yours. Please let me know if it works.
I have a div "container", say 400px width, with a left-floated divs inside — "box" 100px width. There are six of "box" divs so their summary width is larger than 400px which causes that line of divs to get wrapped and I get two lines, with 4 and 2 elements each. How can I make these 6 divs go in one row, one line instead of two?
You simply need white-space: nowrap on the parent element with display: inline-block on the children. Live demo here (click).
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
css:
.container {
width: 400px;
background: black;
white-space: nowrap;
overflow: scroll;
}
.container > div {
height: 50px;
width: 100px;
background: #555;
display: inline-block;
margin: 10px;
}
Assuming you continue using float: left;... If your container has a set width of 400px, then your total sum of children divs can't surprass 400px wide either. This includes any padding, margin, or border space as well.
To answer your question simply, there are several ways...
Make the container 600px wide instead of 400px...
Make the child elements 66px wide instead of 100px...
A better option is to use percentages...
Make the child elements 16.666667% wide.
You need to give display: inline-block to the children of container and also give white-space: nowrap to make them flow horizontally. Here is the CSS
#Container {
width: 400px;
border: 1px solid black;
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
}
.inside {
width: 100px;
height: 300px;
border: 1px solid black;
display: inline-block;
}
and HTML
<div id="Container">
<div class="inside"></div>
<div class="inside"></div>
<div class="inside"></div>
<div class="inside"></div>
<div class="inside"></div>
<div class="inside"></div>
</div>
Here is the fiddle for your problem http://jsfiddle.net/sgaurav/vZLWQ/
hope it will help you
display:inline-block;