Is it possible with CSS3 grid to create such a layout?
All of the elements should be as wide as their content. And the last two elements should be right-aligned.
However, the way I've tried does not work. As soon as a column has min-content, the justify-self property doesn't do anything.
.grid {
display: grid;
grid-template-areas: "first second third";
grid-template-columns: min-content min-content min-content;
}
.first {
grid-area: first;
}
.second {
grid-area: second;
justify-self: right; /* doesn't work */
}
.third {
grid-area: third; /* doesn't work */
}
<div class="grid">
<div class="first">first</div>
<div class="second">second</div>
<div class="third">third</div>
</div>
This is only a downsized version of a much more complex layout, therefore:
I'm not looking for workarounds like creating wrappers
Stretching .first is not an option because there will be calculations on that element
The reason I chose grid over flex is because of the gaps
Using flexbox may be more appropriate? note the margin-right on the first element in container 1, and margin-left on the second element in container 2. auto margins are quite powerful in flex containers.
.container-1,
.container-2 {
display: flex;
background: lightgray;
}
.item {
width: max-content;
margin: 1em;
padding: 1em;
background: white;
}
.container-1 .item-1 {
margin-right: auto;
}
.container-2 .item-2 {
margin-left: auto;
}
<div class="container-1">
<div class="item item-1">
item 1
</div>
<div class="item item-2">
item 2
</div>
<div class="item item-3">
item 3
</div>
</div>
<div class="container-2">
<div class="item item-1">
item 1
</div>
<div class="item item-2">
item 2
</div>
<div class="item item-3">
item 3
</div>
</div>
Related
I've a dynamic list of elements generated by an external library which I do not have any control.
.item-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-items: flex-start;
position: relative;
}
.item {
background-color: red;
}
.item:nth-last-child(2),
.item:last-child {
position: absolute;
right: 0;
}
.item:last-child {
top: 57px;
}
<div class="item-wrapper">
<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>
And I need to generate a layout where the last two elements always need to wrap and align to right side as below.
I tried using flexbox to achieve the desired layout and here's a bit of snippet I've written.
With the above css, the second last element of top row always overlaps to the last element of top row.
I've aware that whenever absolute positioning is used, it takes the element out of the flow and absolutely position them.
However, I do not have much knowledge on how to achieve the layout as given above.
I'm really flexible with other css approach if it's not possible with flexbox.
So, For your output what I did is I wrap all the items in flexbox layout.
Main logic for this layout is we need all row 3 child. and same space to last 2 child to align them at right.
To get that space I have added .item-wrapper with padding-right: calc(100%/4 - 20px);.
Now need each time last two elements to align right, so I just set .item-wrapper to position:relative and than set both last to child with position:asbolute and set second last to top:0 and last on to bottom:0.
to fulfil the desired width I just divided height for second last by 4 and removed that height from 100% for last child.
* {
box-sizing: border-box;
}
.item-wrapper {
display: flex;
flex-wrap: wrap;
gap: 5px;
padding-right: calc(100%/4 - 20px);
position: relative;
}
.item {
flex: 1 0 calc(100%/3 - 20px);
padding: 20px;
background-color: gray;
}
.item:nth-last-child(2),
.item:last-child {
position: absolute;
right: 0;
width: calc(100%/4 - 25px);
height: calc(50% - 2px);
}
.item:nth-last-child(2) {
top: 0;
height: calc(100%/4 - 5px);
}
.item:last-child {
bottom: 0;
right: 0;
height: calc(100% - 100%/4);
}
<div class="item-wrapper">
<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 class="item">7
</div>
<div class="item">8
</div>
<div class="item">9
</div>
<div class="item">10
</div>
<div class="item">11
</div>
<div class="item">12
</div>
</div>
This may look a bit simpler as a grid.
Each item is placed in its desired column, the last two being special and the grid flow set to dense so the second to last element can start back at the top.
.item-wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-flow: dense;
}
.item:nth-child(3n+1) {
grid-column: 1;
}
.item:nth-child(3n+2) {
grid-column: 2;
}
.item:nth-child(3n) {
grid-column: 3;
}
.item:nth-last-child(2),
.item:last-child {
grid-column: 4;
}
<div class="item-wrapper">
<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 class="item">7</div>
<div class="item">8</div>
</div>
Let's say I have a flex container, I want to be display between 1 and 3 items within.
I would like the items to be:
By default 1/3 of the width of the container (even if there's only 1 item)
If one of the item's text content expands slightly beyond the 33% width, I'd like that item to try and expand if it can (i.e. by one of the other items shrinking).
Justified using flex-start
.container {
display: 'flex';
flex-direction: 'row';
background-color: 'red';
width: '100%';
justify-content: 'flex-start';
}
.item {
flex: 1;
border: 1px solid red;
flex-wrap: 'no-wrap';
}
<div class="container">
<div class="item">blahhhhhhhhhhhhhhhhhhhhhhhhhh</div>
<div class="item">blahhh</div>
<div class="item">blahhh</div>
</div>
<div class="container">
<div class="item">I should take up 1/3</div>
</div>
The above works for the 3 item case, however when a singular item is placed there it expands to fill the space.
Is this possible?
You can use CSS-Grid for that.
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
margin-bottom: 1em;
}
.item {
height: 50px;
border: 1px solid red;
}
.item.wide {
width: 250px;
}
<div class="container">
<div class="item wide"> I'm wider than 1/3</div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="container">
<div class="item"></div>
</div>
<div class="container">
<div class="item"></div>
<div class="item"></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 want to make a responsive css grid that looks like this:
box | box | box
b o x | b o x
b o x | b o x
and then when the screen size gets small enough, all of the columns collapse into a single column with each box on its own row.
Is this possible?
Edit:
For anyone who has this issue, changing the number of columns via media query from 6 to 1 was not working. However, I had the idea to make all of the items span 6 columns at the break point and that worked perfectly. This is an addition to the answer given by lawrence-witt and paulie-d.
By using a SIX column grid
.item {
height: 3em;
border: 2px solid green;
}
.container {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-gap: 4px;
width: 90%;
margin: 1em auto;
}
.item:nth-child(1),
.item:nth-child(2),
.item:nth-child(3) {
grid-column: span 2
}
.item:nth-child(4),
.item:nth-child(5) {
grid-column: span 3;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
After that it's just a matter of adding a media query to collapse the grid to a single column at the appropriate breakpoint,
Here is one way to do it using the nth-child feature. To make the grid responsive you simply add a media query that changes the styling of cells and grid at a certain screen width.
.grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
}
.cell {
border: 1px solid black;
grid-column: span 3;
}
.cell:nth-child(1),
.cell:nth-child(2),
.cell:nth-child(3){
grid-column: span 2;
}
<div class="grid">
<div class="cell">One</div>
<div class="cell">Two</div>
<div class="cell">Three</div>
<div class="cell">Four</div>
<div class="cell">Five</div>
<div class="cell">Six</div>
<div class="cell">Seven</div>
</div>
By using CSS flex layout and CSS media query you can achieve your need. See the below code, if the screen size reduced to 600px then it will change to single column layout.
.container {
display: flex;
width: 100%;
justify-content: space-between;
flex-direction: row;
margin-bottom: 10px;
}
.three-box-row > div {
width: 33%;
height: 50px;
}
.two-box-row > div {
width: 49%;
height: 50px;
}
#media only screen and (max-width: 600px) {
.container {
flex-direction: column;
}
.container > div {
width: 100%;
}
}
<body>
<div class="three-box-row container">
<div style="background-color: red;">Box 1</div>
<div style="background-color: green;">Box 2</div>
<div style="background-color: blue;">Box 3</div>
</div>
<div class="two-box-row container">
<div style="background-color: red;">Box 1</div>
<div style="background-color: green;">Box 2</div>
</div>
<div class="two-box-row container">
<div style="background-color: green;">Box 1</div>
<div style="background-color: blue;">Box 2</div>
</div>
</body>
I’ve to implement a layout based on a square grid. As shown on the following image, some elements have to overlap responsive within this grid. (The squares are offset on the x-axis and overlap by one grid cell on the y-axis.)
http://i.stack.imgur.com/9bZ5G.jpg
Does anybody know how to achieve this effect? I'm using the framework Foundation 6. I’d prefer a solution without JavaScript. I can’t use the Foundation .#-push-# and .#pull-# classes because they would shift the elements inwards and the two squares have to be in separate rows.
I’ve set up a JSFiddle containing the two squares.
.square {
background: #f00;
position: relative;
}
.square:after {
content: "";
display: block;
padding-bottom: 100%;
}
.content {
position: absolute;
width: 100%;
height: 100%;
}
.dark {
background: #cbcbcb;
}
.light {
background: #dedede;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.2.0/foundation.css" rel="stylesheet"/>
<div class="row">
<div class="small-12 columns">
<div class="row">
<div class="small-8 columns end">
<div class="square dark">
<div class="content">test</div>
</div>
</div>
</div>
<div class="row">
<div class="small-6 small-offset-6 columns end">
<div class="square light">
<div class="content">test</div>
</div>
</div>
</div>
</div>
</div>
Many thanks in advance for your help.
I know this question is a little bit old, but for the benefit of all this is now very easily and cleanly possible with CSS Grid. Taking the image posted in the original question we define a container and two squares:
<div class="container">
<div class="content square1">Square 1</div>
<div class="content square2">Square 2</div>
</div>
Then in the CSS define the container as a CSS Grid with 6 columns and 6 rows. In the example below I have used the repeat() CSS Method to have the browser create 6 of the same sized columns and rows.
.container {
display: grid;
grid-column-template: repeat(6, 1fr);
grid-row-template: repeat(6, 30px);
width: 100%;
height: 600px;
}
Then for each item you set where the upper left hand corner will be located on that grid. You can use the long handed properties of grid-column-start, grid-column-end, grid-row-start, and grid-row-end, but I find it's easier to use the short hand properties as shown below:
.square1 {
grid-column: 1 / 5;
grid-row: 1 / 5;
background: #cbcbcb;
}
.square2 {
grid-column: 4 / 7;
grid-row: 4 / 7;
background: #dedede;
}
As far as the placement goes, you specify it based on the grid lines you're invisibly drawing in the container. Position 1 / 1 in this case is the upper left-most corner (or where 0,0 would be if you're talking about coordinates). CSS Grid is now widely supported and I believe there are some JS libraries out there that do create fall backs for this if you don't want to hard code your own with feature queries.
Here's the full block of code with the SO Code Snippet runner:
.container {
display: grid;
grid-column-template: repeat(6, 1fr);
grid-row-template: repeat(6, 30px);
width: 100%;
height: 600px;
}
.square1 {
grid-column: 1 / 5;
grid-row: 1 / 5;
background: #cbcbcb;
}
.square2 {
grid-column: 4 / 7;
grid-row: 4 / 7;
background: #dedede;
}
<div class="container">
<div class="content square1">Square 1</div>
<div class="content square2">Square 2</div>
</div>
It seems to work if you calculate the offset with percent and also mind the column spacing. I therefore adjustet the snippet and added another square with 4 colums:
.square {
background: #f00;
position: relative;
}
.square:after {
content: "";
display: block;
padding-bottom: 100%;
}
.content {
position: absolute;
width: 100%;
height: 100%;
}
.dark {
background: #cbcbcb;
}
.light {
background: #dedede;
}
/* NEW */
.small-6.columns.overlap-top > .square {
margin-top: calc(-33.3% + 1.33*0.625rem); // one third is 33.3% minus 1.33 times col spacing
}
.small-4.columns.overlap-top > .square {
margin-top: calc(-50% + 1*0.625rem); // one half is 50% minus 1 times col spacing
}
#media screen and (min-width: 40em) {
.small-6.columns.overlap-top > .square {
margin-top: calc(-33.3% + 1.33*0.9375rem);
}
.small-4.columns.overlap-top > .square {
margin-top: calc(-50% + 1*0.9375rem);
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.2.0/foundation.css" rel="stylesheet"/>
<div class="row">
<div class="small-12 columns">
<div class="row">
<div class="small-8 columns end">
<div class="square dark">
<div class="content">Square 1</div>
</div>
</div>
</div>
<div class="row">
<!-- New class overlap-top -->
<div class="small-6 small-offset-6 columns overlap-top end">
<div class="square light">
<div class="content">Square 2</div>
</div>
</div>
</div>
<!-- New square -->
<div class="row">
<div class="small-4 small-offset-4 columns overlap-top end">
<div class="square dark">
<div class="content">Square 3</div>
</div>
</div>
</div>
</div>
</div>
JSFiddle: https://jsfiddle.net/jwt0k1pw/1/
Hope this helps!