My grid layout has 2 columns. It contains a mixture of full-width items, which occupy the full width of the grid; and half-width items, which each occupy half the width of a row. Half-width items always come in pairs. The leftmost item should be pulled to the right, and the rightmost should be pulled to the left, in effect being squeezed together.
I have prepared an example with Tailwind-style CSS that demonstrates what I want:
.m-2 { margin: 0.5rem; }
.p-2 { padding: 0.5rem; }
.bg-blue { background-color: rgba(0, 0, 255, 0.5); }
.bg-red { background-color: rgba(255, 0, 0, 0.5); }
.grid { display: grid; }
.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.col-span-2 { grid-column: span 2 / span 2; }
.justify-self-start { justify-self: start; }
.justify-self-end { justify-self: end; }
<div class="grid grid-cols-2">
<div class="col-span-2 m-2 p-2 bg-blue">
<p>Full width item</p>
</div>
<div class="justify-self-end m-2 p-2 bg-red">
<p>Half width item</p>
</div>
<div class="justify-self-start m-2 p-2 bg-red">
<p>Half width item</p>
</div>
<div class="col-span-2 m-2 p-2 bg-blue">
<p>Full width item</p>
</div>
</div>
However, in my scenario, any individual grid item only knows whether or not it is full-width or half-width. If it is full-width, it can set itself to span 2 columns. If it is half-width, it can set itself to span 1 column, but it doesn't know whether to pull itself to the left or the right.
When an individual item doesn't know if it's the first or second item in a half-width row, how can I tell it to pull itself either to the left or to the right?
Bonus points for a solution that works with 3 items in a row, with the middle one staying in the centre.
Bonus points for a solution that uses grid-auto-flow to position half-width elements next to each other that don't appear consecutively in the HTML.
The only way that I can think of to solve it using only CSS is setting different elements for your layout, and then using nth-of-type:
.container {
display: grid;
grid-template-columns: 200px 200px;
grid-auto-flow: row dense;
grid-gap: 4px;
}
span {
grid-column: span 2;
background-color: lightblue;
}
p {
width: 70px;
background-color: lightgreen;
}
p:nth-of-type(odd) {
justify-self: end;
}
<div class="container">
<span>full</span>
<span>full</span>
<p>half</p>
<span>full</span>
<p>half</p>
<p>half</p>
<span>full</span>
<p>half</p>
<p>half</p>
<p>half</p>
</div>
Related
I have a menu grid layout with an inner item (in this case it's the .metadata div) that I want to expand and push down another item. See example here :
.wrapper {
display: grid;
grid-column-gap: 8px;
grid-row-gap: 4px;
grid-template-columns: 48px minmax(0px, 3fr) 1fr;
grid-template-rows: 24px 20px 44px;
grid-template-areas:
"icon title action-bar"
"icon metadata action-bar"
"tabs .... bottom-right";
padding: 16px 16px 0 16px;
}
.metadata {
grid-area: metadata;
display: flex;
align-items: baseline;
direction: ltr;
}
.innterTest {
height: 100px;
background-color: red;
}
.metadataItem {
display: flex;
}
.tabs {
grid-area: tabs;
grid-column-end: 3;
padding-top: 4px;
}
<div class="wrapper">
<div class="icon">icon
</div>
<div class="title">TITLE
</div>
<div class="action-bar">action bar
</div>
<div class="metadata">
<div class="metadataItem">
data node 1
<div class="innterTest">
testing
</div>
</div>
<div class="metadataItem">
data node 2
</div>
<div class="metadataItem">
data node 3
</div>
<div class="metadataItem">
data node 4
</div>
</div>
<div class="tabs">tabs
</div>
</div>
https://jsfiddle.net/c8wx2bgn/
If you inspect the outer .metadata wrapping div it seems to stay a small size. What I would like to happen is for it to expand and push down the .tabs grid item. The general grid layout has been working as I had hoped, but I've added more items inside metadata and want it to push tabs down when it expands.
I've tried enforcing a height on the metadata and metadata divs but this does not seem to effect the layout. New to grid so unsure what I am missing here.
You have grid-template-rows: 24px 20px 44px.
This means that the second row, which contains your metadata div, is limited in height to 20px.
Try this: grid-template-rows: 24px auto 44px.
I'm trying to create a CSS grid with the following properties:
The grid has a width of 100%
The column's width should be 330px, but should shrink if this allows fitting one more column, but it should never shrink below 288px.
All columns should have the same width
If there is space left, left-align the columns (all remaining space should be on the right side of the grid)
Here a few examples of what I'm trying to achieve
Grid width => columns (width)
----------------------------
658px => |329px|329px|
|329px|
660px => |330px|330px|
|330px|
665px => |330px|330px|5px(whitespace)|
|330px|
863px => |330px|330px|203px(whitespace)|
|330px|
864px => |288px|288px|288px|
867px => |289px|289px|289px|
960px => |320px|320px|320px|
I've tried the following three options:
Approach 1
.grid {
grid-template-columns: repeat(auto-fill, minmax(288px, 1fr));
}
The problem: The columns shrink down to 288px, but they can grow larger than 330px Image with columns larger than the max width
Approach 2
.grid {
grid-template-columns: repeat(auto-fill, minmax(288px, 330px));
}
The problem: the columns never grow, and the columns are left-aligned, but they are always 330px and never shrink down to 288px. Image with columns that don't shrink below the max width
Approach 3
.grid {
grid-template-columns: repeat(auto-fill, minmax(288px, 1fr));
}
.grid > div {
max-width: 330px;
}
The problem: The columns shrink down to 288px, and they never grow larger than 330px, but they are not left-aligned. Image with columns that grow and shrink within boundaries, but are not left-aligned
As you can see, none of the approaches I've tried worked so far. All of them have different problem :( Is there any way to make this layout work with CSS Grid?
This should work, placing it only on the .wrapper - note I did force and exception on a column (.three) to show the effect of that. I used smaller values to accommodate this results execution window
.my-container {
width: 100%;
border: solid green 1px;
padding: 3px;
}
.wrapper {
border: solid cyan 1px;
padding: 3px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100px, 150px), 1fr));
gap: 0.25rem;
}
.thing {
border: solid 1px magenta;
}
.three {
width: 40px;/* to show test of this */
border: solid lime 1px;
}
<div class="my-container">
<div class="wrapper">
<div class="thing one">One comes before two but is a lonely number</div>
<div class="thing two">Two burgers are more than one burger</div>
<div class="thing three">Three</div>
<div class="thing four">Four is the next block of super interesting content with more very happy text.</div>
<div class="thing five">Five test of the test</div>
<div class="thing six">Six</div>
<div class="thing seven">Seven</div>
<div class="thing ">Eight</div>
<div class="thing ">More</div>
<div class="thing ">And More</div>
<div class="thing ">More so more</div>
<div class="thing ">More again</div>
</div>
</div>
This question already has answers here:
Targeting flex items on the last or specific row
(10 answers)
Closed 1 year ago.
I have a container with a dynamic number of items.
Each line can contain up to 4 items. If there are more than 4 items, the next item will start a new line (image 1). If there are less than 4 items it's OK, they just won't fill the whole line (image 2).
But I'm having troubles with the spaces between them:
I tried to use margin-right but it affects the last items in the lines (e.g.: item #4).
I tried to use justify-content: space-between but it looks good only for 4 items and up. For 3 and bellow, it creates a big space between them and I want them to look as in image 2.
Any other elegant / easy solutions?
.container {
display: flex;
flex-wrap: wrap;
/* justify-content: space-between; */
}
.item {
display: inline-block;
width: calc(25% - 12px);
/* margin-right: 12px; */
}
<div class="container">
<div class="item">
#1
</div>
<div class="item">
#2
</div>
<div class="item">
#3
</div>
<div class="item">
#4
</div>
</div>
You can use css grid, you have to use display: grid;, use grid-template-columns to set the amount of columns that you want (1fr = 1 parent container fraction) and finally use grid-gap to set the space between your items.
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 12px;
}
.item {
display: inline-block;
border: 1px solid red;
color: red;
}
<div class="container">
<div class="item">
#1
</div>
<div class="item">
#2
</div>
<div class="item">
#3
</div>
<div class="item">
#4
</div>
</div>
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 12px;
}
.item {
display: inline-block;
border: 1px solid red;
color: red;
}
<div class="container">
<div class="item">
#1
</div>
<div class="item">
#2
</div>
<div class="item">
#3
</div>
<div class="item">
#4
</div>
<div class="item">
#5
</div>
<div class="item">
#6
</div>
</div>
More info about Css grid Here!
In class .item, is defined width with calc(25% - 12px). Remember, 25% is just 4 items in each line. 20% is 5 items in each line.
So, change the width to calc(20% - 12px)
While CSS Grid is possibly the better solution for the problem, it's entirely possible to solve the problem with CSS flex-box layout, using the gap property and taking advantage – as did your original code – of the calc() function:
// this is to allow you to dynamically add more .item elements
// so you see that it should meet your needs containing more
// elements.
// we use document.querySelector() to retrieve the first element
// that matches the selector (if any exist, otherwise null):
const button = document.querySelector('button'),
// defining a named function to handle addition of new .item
// elements:
addMore = () => {
// finding the first .item element on the page:
let base = document.querySelector('.item');
// finding the .container element, and using
// .append() to attach a cloned copy of the first
// .item:
document.querySelector('.container').append(base.cloneNode(true));
}
// binding the named - addMore() - function as the event-handler
// for the 'click' event:
button.addEventListener('click', addMore);
*,
::before,
::after {
/* selecting all elements, and the pseudo-elements ::before
and ::after, setting their box-sizing model to border-box
in order that their widths include their border and padding
*/
box-sizing: border-box;
/* removing margin and padding: */
margin: 0;
padding: 0;
}
.container {
display: flex;
flex-wrap: wrap;
/* using the gap property to place a 0.5em 'gutter'
between adjacent elements, both horizontally and
vertically: */
gap: 0.5em;
}
.item {
/* setting the flex-grow: to 1, flex-shrink to 0,
and flex-basis to the result of 20% of the parent-
width minus 0.5em (the gap-space): */
flex: 1 0 calc(20% - 0.5em);
}
/* irrelevant, purely for aesthetics */
*,
::before,
::after {
line-height: 2em;
}
.container {
border: 1px solid #000;
counter-reset: itemCount;
width: 90vw;
margin-inline: auto;
}
.item {
background-color: lightblue;
flex: 1 0 calc(20% - 0.5em);
}
.item:nth-child(odd) {
background-color: palegreen;
}
.item::before {
content: counter(itemCount, decimal-leading-zero);
counter-increment: itemCount;
}
<button type="button">Add another item</button>
<div class="container">
<div class="item">
</div>
<div class="item">
</div>
<div class="item">
</div>
<div class="item">
</div>
</div>
JS Fiddle demo.
References:
box-sizing.
calc().
gap.
margin-inline.
I am trying to Auto-Sizing Columns in CSS Grid. Like if I have one child-div it will cover full space in mother-div. But if there are multiple child-div it will allow specific number of child-div in a row . Here I am using CSS grid. But I cant do it. Here is my code
<div class="mother-div">
<div class="child-div>
</div>
<div class="child-div>
</div>
</div>
css
.child-div {
background-color: #257790;
height: 100px;
width: 100%;
}
.mother-div {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 1em;
}
What you are looking is almost automatic, grid will behave like this almost out of the box.
You only need to set grid-auto-flow to the direction that you want
To limit the number of items per row, you need to set them to a specific column, using nth-child selector
this style:
.child-div:nth-child(5n+1) {
grid-column: 1;
}
selects childs for n values 0, 1, 2, and so on, that in the formula 5n+1 gives values 1, 6, 11, 16, ...
All those children will go to column 1, that matches the requirement for 5 elements per row.
.child-div {
background-color: #257790;
height: 50px;
width: 100%;
}
.child-div:nth-child(5n+1) {
grid-column: 1;
}
.child-div:nth-child(5n+2) {
grid-column: 2;
}
.child-div:nth-child(5n+3) {
grid-column: 3;
}
.child-div:nth-child(5n+4) {
grid-column: 4;
}
.child-div:nth-child(5n) {
grid-column: 5;
}
.mother-div {
margin: 10px;
width: 500px;
border: solid 3px black;
display: grid;
grid-gap: 1em;
grid-auto-flow: column;
}
<div class="mother-div">
<div class="child-div">
</div>
</div>
<div class="mother-div">
<div class="child-div">
</div>
<div class="child-div">
</div>
</div>
<div class="mother-div">
<div class="child-div">1
</div>
<div class="child-div">2
</div>
<div class="child-div">3
</div>
<div class="child-div">4
</div>
<div class="child-div">5
</div>
<div class="child-div">6
</div>
</div>
I have a CSS grid, say of 3 columns, with parent container located in the middle column, can I somehow expand child to take all three columns? In my code below I want the image area took all three columns, while text took just one:
.grid {
display: grid;
width: 100%;
grid-template-columns: 10% 1fr 10%;
}
.content {
grid-column: 2/3;
}
.image-area {
grid-column: 1/4;
color: #6dae72;
}
<div class="grid">
<div class="content">
<div class="text-area">Some text</div>
<div class="image-area">The picture</div>
</div>
</div>
Would appreciate any help.