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.
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>
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.
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>
i created .container with some color & in that container header with some other color and text. now i want to center text. i am able to do that with grid but problem is my header color (background color of header) shrink to vertically and horizontally center too. i want only text to get in center not the color. hope i explained clearly. plz explain me how i can achieve that.
(
i am using visual studio. align-item align-content align-self(on header)justify-item,content,self nothing working for me plz clear my confusion
i need only text in center if i put image or logo that in center. i dont want background image to be compromise and i wanna use grid only.
i tried 6 grid commands align-item align-content align-self(on header)justify-item,content,self nothing working for me plz clear my confusion.
i am using visual studio. align-item align-content align-self(on header)justify-item,content,self nothing working for me plz clear my confusion
<div class="container">
<div class="header">This is header</div>
<div class="small-box-1">Small-Box1</div>
<div class="small-box-2">Small-Box2</div>
<div class="small-box-3">Small-Box3</div>
<div class="main-content">Main Content</div>
<div class="side-bar">Side-Bar</div>
<div class="footer">footer</div>
</div>
</div>
To center element on the grid and avoid them to shrink, you will need to set again a grid system on your children You can use flex or grid .
examples (might not be your grid, but needed a base that you did not provide, if that does not answer your question then, please, clarify your question)
Flex can be used on the grid children to allow centering alignement.
.container> div {
display:flex;
align-items:center;
justify-content:center;
background:tomato;
border:solid;
}
/* reconstruction of a grid */
.container {
display:grid;
grid-template-columns:repeat(4,1fr);
grid-auto-rows: minmax(150px,1fr);
grid-gap:1em;
}
.container .header, .container .footer {
background:lightblue;
grid-column:span 4;
}
.small-box-1 {
grid-column:2;
}
.main-content{
grid-column:2 / span 3
}
.side-bar {
grid-column:1;
grid-row:2 / span 2;
}
<div class="container">
<div class="header">This is header</div>
<div class="small-box-1">Small-Box1</div>
<div class="small-box-2">Small-Box2</div>
<div class="small-box-3">Small-Box3</div>
<div class="main-content">Main Content</div>
<div class="side-bar">Side-Bar</div>
<div class="footer">footer</div>
</div>
Grid can also be used on the grid children to allow centering alignement.
.container>div {
display: grid;
align-items: center;
justify-content: center;
background: tomato;
border: solid;
}
/* reconstruction of a grid */
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: minmax(150px, 1fr);
grid-gap: 1em;
}
.container .header,
.container .footer {
background: lightblue;
grid-column: span 4;
}
.small-box-1 {
grid-column: 2;
}
.main-content {
grid-column: 2 / span 3
}
.side-bar {
grid-column: 1;
grid-row: 2 / span 2;
}
<div class="container">
<div class="header">This is header</div>
<div class="small-box-1">Small-Box1</div>
<div class="small-box-2">Small-Box2</div>
<div class="small-box-3">Small-Box3</div>
<div class="main-content">Main Content</div>
<div class="side-bar">Side-Bar</div>
<div class="footer">footer</div>
</div>
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.