Grid layout but avoid space distribute equally - css

I'm trying to use the grid layout for two columns in one row which can be easily achieved by flex. I have to create one more div for flex but the grid doesn't need one more div.
The problem with the grid is that it will divide the width space by 2 (cannot align to start/left) and that's not what I want, please refer to the first example below and you will understand.
Is there any way to use the grid in this situation but we can align the items to the left like in the second example?
#main-1 {
display: grid;
gap: 30px;
grid-teplate-column: repeat(2, minmax(0, 1fr));
}
.test-1 {
background-color: orange;
grid-area: span 1 / span 2;
}
.test-2 {
background-color: gray;
width: 150px;
}
#main-2 {
display: flex;
gap: 30px;
margin-top: 30px;
}
.test-3 {
background-color: orange;
width: 100%;
}
.test-4 {
background-color: gray;
width: 150px;
}
.test-1,
.test-2,
.test-3,
.test-4 {
height: 60px;
}
<h1>Grid</h1>
<div id="main-1">
<div class="test-1"></div>
<div class="test-2"></div>
<div class="test-2"></div>
</div>
<h1 style="margin:30px 0 0 0;padding-top:15px;border-top: 3px solid #000;">Flex</h1>
<p style="margin:0 0 30px 0;">This is the desired layout but with one more extra div</p>
<div>
<div class="test-3"></div>
<div id="main-2">
<div class="test-4"></div>
<div class="test-4"></div>
</div>
</div>
Edited
Inline-block might work but we cannot control how many items should be on each row. Imagine the width of the first div .first is dynamic and we do not know how wide it would be(but I will make it 30px for illustration). Now the desired layout should be only one .first and one .second on each row.
By inline-block it would appear that now each row is one .first, one .second, and one .first. Check out the example below. Because we cannot control the amount like grid on each row.
#main {
width: 120px;
}
.first,
.second {
display: inline-block;
height: 60px;
}
.first {
background-color: orange;
width: 30px;
}
<div id="main">
<div class="first"></div>
<p class="second">hhhhhh</p>
<div class="first"></div>
<p class="second">hhhhhh</p>
<div class="first"></div>
<p class="second">hhhhhh</p>
</div>

Define the columns as auto and keep only one at 1fr then you can align to the left.
#main-1 {
display: grid;
gap: 30px;
/* update "5" based on your needs */
grid-template-columns: repeat(5,auto) 1fr;
justify-content: left; /* align to left */
}
.test-1 {
background-color: orange;
grid-column: 1/-1; /* take all the columns */
}
.test-2 {
background-color: gray;
width: 150px;
}
#main-2 {
display: flex;
gap: 30px;
margin-top: 30px;
}
.test-3 {
background-color: orange;
width: 100%;
}
.test-4 {
background-color: gray;
width: 150px;
}
.test-1,
.test-2,
.test-3,
.test-4 {
height: 60px;
}
<h1>Grid</h1>
<div id="main-1">
<div class="test-1"></div>
<div class="test-2"></div>
<div class="test-2"></div>
</div>
<h1 style="margin:30px 0 0 0;padding-top:15px;border-top: 3px solid #000;">Flex</h1>
<p style="margin:0 0 30px 0;">This is the desired layout but with one more extra div</p>
<div>
<div class="test-3"></div>
<div id="main-2">
<div class="test-4"></div>
<div class="test-4"></div>
</div>
</div>

Related

Horizontally center middle element in 3 column row [duplicate]

I'm trying to align a top menu which consists of 3 blocks of content.
What I'm trying to achieve is this:
block 1: left aligned
block 2: centered horizontally
block 3: right aligned
If all 3 blocks were the same size, I could use flexbox (as in the snippet), but they're not, so it doesn't produce the output I require.
Instead, flexbox puts equal space between the 3 blocks - resulting in the middle block being aligned off-center.
I was wondering if this could be achieved with flexbox, or if not, another solution. This needs to work robustly in production so a 'Grid' solution is not applicable as there is insufficient support.
.container {
margin: 20px 0;
}
.row {
background-color: lime;
display: flex;
justify-content: space-between;
}
.item {
background-color: blue;
color: #fff;
padding: 16px;
}
<div class="container">
<div class="row">
<div class="item">left, slightly longer</div>
<div class="item">center, this item is much longer</div>
<div class="item">right</div>
</div>
</div>
You can consider flex-grow:1;flex-basis:0% for the left and right elements then use text-align to align content inside. I have added an extra wrapper to keep the background only around the text.
The trick is to calculate the free space by removing only the middle content and split it equally to the left and right element.
.container {
margin: 20px 0;
padding-top:10px;
background:linear-gradient(#000,#000) center/5px 100% no-repeat; /*the center*/
}
.row {
background-color: lime;
display: flex;
color: #fff;
}
.item:not(:nth-child(2)) {
flex-basis: 0%;
flex-grow: 1;
}
.item:last-child {
text-align: right;
}
.item span{
background-color: blue;
display:inline-block;
padding: 16px;
border: 2px solid red;
box-sizing:border-box;
}
<div class="container">
<div class="row">
<div class="item"><span>left, slightly longer</span></div>
<div class="item"><span>center, this item is much longer</span></div>
<div class="item"><span>right</span></div>
</div>
</div>
You can also do the same by keeping the element close. Simply adjust text-align:
.container {
margin: 20px 0;
padding-top: 10px;
background: linear-gradient(#000, #000) center/5px 100% no-repeat; /*the center*/
}
.row {
background-color: lime;
display: flex;
color: #fff;
}
.item:not(:nth-child(2)) {
flex-basis: 0%;
flex-grow: 1;
}
.item:first-child {
text-align: right;
}
.item span {
background-color: blue;
display: inline-block;
padding: 16px;
border: 2px solid red;
box-sizing: border-box;
}
<div class="container">
<div class="row">
<div class="item"><span>left, slightly longer</span></div>
<div class="item"><span>center, this item is much longer</span></div>
<div class="item"><span>right</span></div>
</div>
</div>
I asked what seems to be a very similar question and stack overflow directed me here. The response from #Paolamoralesval inspired me to realise the required effect can be achieved in CSS grid. Now that grid support is pretty much universal I hope that this meets everyone's needs. This solution is I believe fully responsive to window size as well as height and width of the header items as you should see if you resize the window where you view the snippet.
.header {
grid-row: 1;
grid-column: 1;
display: grid;
grid-template-rows: min-content;
grid-template-columns: 1fr 1fr 1fr;
}
.header-left {
justify-self: start;
align-self: center;
text-align: left;
background-color: red;
}
.header-center {
justify-self: center;
align-self: center;
text-align: center;
background-color: green;
}
.header-right {
justify-self: end;
align-self: center;
text-align: right;
background-color: blue;
}
.shrink-kitty {
width: 200px;
}
<html>
<body>
<div class="page">
<div class="header">
<div class="header-left">
<img class="shrink-kitty" src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Kittyply_edit1.jpg/1280px-Kittyply_edit1.jpg"/><br/>
By David Corby<br/>
Edited by: Arad<br/>Image:Kittyplya03042006.JPG<a><br/><a href="https://creativecommons.org/licenses/by/2.5" title="Creative Commons Attribution 2.5">CC BY 2.5, Link
</div>
<div class="header-center">In the middle</div>
<div class="header-right">
Much much much much more on the right hand side</br>
Indeed two lines
</div>
</div>
<div class="body">Body of the page</div>
<div class="footer">At the bottom</div>
</div>
</body>
</html>
can you give flex-grow:1 for the item class and check
.item {
background-color: blue;
color: #fff;
padding: 16px;
flex-grow:1;
}
Hope this is what you are looking for
Alternative using display table (an ancient supported grid).
Quote from https://www.w3schools.com/cssref/pr_tab_table-layout.asp
If no widths are present on the first row, the column widths are divided equally across the table, regardless of content inside the cells
.container {
display: table;
table-layout: fixed
} // would divide cells equally along table's 100% width.
.row {
display: table-row
}
.item {
display: table-cell
}

How to arrange items in css grid with auto heights

I want to make a fairy simple looking layout with css grid.
As, shown in above image, i want to place 3 items in a grid in the exact same order as shown in the image, I don't want to set specific height to any of the item, since each of the item have different height depending upon the content in it. I want to give 70% of grid horizontal space to item1 and 30% to item2 & item3, item3 should be placed beneath item2. Again, heights of items should be auto. I am trying to achieve this for many hours but failed to do so.
Markup of the problem:
<div class="container">
<div class="item1">some content in it...</div>
<div class="item2">some Content in it...</div>
<div class="item3">some Content in it...</div>
</div>
Solution as requested:
You could use grid-template-areas to span the first item across multiple rows. And use a spacer at the end of the right column to make the items just as big as needed.
We can use calc to account for the grip-gap.
.grid {
display: grid;
grid-gap: 20px;
grid-template-columns: calc(70% - 10px) calc(30% - 10px);
grid-template-areas: "item-1 item-2" "item-1 item-3" "item-1 spacer";
border: 1px dashed #000;
align-items: start;
}
.item {
display: flex;
justify-content: center;
align-items: center;
font-size: 32px;
font-weight: bold;
}
#media screen and (max-width: 600px) {
.grid {
grid-template-areas: "item-2" "item-1" "item-3";
grid-template-columns: 100%;
}
}
.item-1 {
grid-area: item-1;
}
.item-2 {
grid-area: item-2;
}
.item-3 {
grid-area: item-3;
}
.purple {
background-color: #5B00FF;
}
.red {
background-color: #FF0000;
}
.pink {
background-color: #FF00FD
}
.h-500 {
height: 500px;
}
.h-100 {
height: 100px;
}
.h-200 {
height: 200px;
}
<div class="grid">
<div class="item item-1 purple h-500">Item 1</div>
<div class="item item-2 red h-100">Item 2</div>
<div class="item item-3 pink h-200">Item 3</div>
</div>
Alternative solution with different columns:
You could use grid-gap along with grid-template-columns. You have to take the grid-gap into account for the width of template-column That's why there is this calc.
.grid {
display: grid;
grid-gap: 20px;
grid-template-columns: calc(70% - 10px) calc(30% - 10px);
border: 1px dashed #000;
}
.col--right {
display: flex;
flex-flow: column nowrap;
gap: 20px;
}
.item {
display: flex;
justify-content: center;
align-items: center;
font-size: 32px;
font-weight: bold;
}
.purple {
background-color: #5B00FF;
}
.red {
background-color: #FF0000;
}
.pink {
background-color: #FF00FD
}
.h-500 {
height: 500px;
}
.h-100 {
height: 100px;
}
.h-200 {
height: 200px;
}
<div class="grid">
<div class="col col--left">
<div class="item purple h-500">Item 1</div>
</div>
<div class="col col--right">
<div class="item red h-100">Item 2</div>
<div class="item pink h-200">Item 3</div>
</div>
</div>

Repeating CSS grid without defining more grid areas

I'm trying to use CSS grid to layout some content in the following constraints.
I have three divs - all should be 50% wide but div two and three should stack on top of each other next to div 1.
I've managed to achieve this using grid-template-areas, but I'm using PHP to dynamically populate this, so there's no guarantee that there will always be three divs, so if it goes over this amount, I simply want the grid to repeat.
I'm using the following code right now:
.container {
display: grid;
grid-template-columns: 50% 50% 50%;
gap: 0px 0px;
grid-auto-flow: row;
grid-template-areas:
"Grid-1 Grid-2 ."
"Grid-1 Grid-3 ."
". . .";
}
.Grid-2 { grid-area: Grid-2; }
.Grid-3 { grid-area: Grid-3; }
.Grid-1 { grid-area: Grid-1; }
html, body , .container {
height: 100%;
margin: 0;
}
.container * {
border: 1px solid red;
position: relative;
}
.container *:after {
content:attr(class);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: grid;
align-items: center;
justify-content: center;
}
<div class="container">
<div class="Grid-1"></div>
<div class="Grid-2"></div>
<div class="Grid-3"></div>
</div>
It would also ne nice to not have to give each div that I'm generating a PHP the specific area class. Is this achievable using grid?
Simply like below:
.container {
display: grid;
grid-template-columns: 50% 50%; /* 2 columns */
grid-auto-rows:50vh; /* size of one row*/
}
/* for each 3 divs make the first one span 2 rows */
.container > :nth-child(3n + 1) { grid-row:span 2 }
.container * {
border: 1px solid red;
display: grid;
place-items: center;
}
.container *:after {
content:"some content";
}
body {
margin: 0;
}
<div class="container">
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
</div>

Align 3 unequal blocks left, center and right

I'm trying to align a top menu which consists of 3 blocks of content.
What I'm trying to achieve is this:
block 1: left aligned
block 2: centered horizontally
block 3: right aligned
If all 3 blocks were the same size, I could use flexbox (as in the snippet), but they're not, so it doesn't produce the output I require.
Instead, flexbox puts equal space between the 3 blocks - resulting in the middle block being aligned off-center.
I was wondering if this could be achieved with flexbox, or if not, another solution. This needs to work robustly in production so a 'Grid' solution is not applicable as there is insufficient support.
.container {
margin: 20px 0;
}
.row {
background-color: lime;
display: flex;
justify-content: space-between;
}
.item {
background-color: blue;
color: #fff;
padding: 16px;
}
<div class="container">
<div class="row">
<div class="item">left, slightly longer</div>
<div class="item">center, this item is much longer</div>
<div class="item">right</div>
</div>
</div>
You can consider flex-grow:1;flex-basis:0% for the left and right elements then use text-align to align content inside. I have added an extra wrapper to keep the background only around the text.
The trick is to calculate the free space by removing only the middle content and split it equally to the left and right element.
.container {
margin: 20px 0;
padding-top:10px;
background:linear-gradient(#000,#000) center/5px 100% no-repeat; /*the center*/
}
.row {
background-color: lime;
display: flex;
color: #fff;
}
.item:not(:nth-child(2)) {
flex-basis: 0%;
flex-grow: 1;
}
.item:last-child {
text-align: right;
}
.item span{
background-color: blue;
display:inline-block;
padding: 16px;
border: 2px solid red;
box-sizing:border-box;
}
<div class="container">
<div class="row">
<div class="item"><span>left, slightly longer</span></div>
<div class="item"><span>center, this item is much longer</span></div>
<div class="item"><span>right</span></div>
</div>
</div>
You can also do the same by keeping the element close. Simply adjust text-align:
.container {
margin: 20px 0;
padding-top: 10px;
background: linear-gradient(#000, #000) center/5px 100% no-repeat; /*the center*/
}
.row {
background-color: lime;
display: flex;
color: #fff;
}
.item:not(:nth-child(2)) {
flex-basis: 0%;
flex-grow: 1;
}
.item:first-child {
text-align: right;
}
.item span {
background-color: blue;
display: inline-block;
padding: 16px;
border: 2px solid red;
box-sizing: border-box;
}
<div class="container">
<div class="row">
<div class="item"><span>left, slightly longer</span></div>
<div class="item"><span>center, this item is much longer</span></div>
<div class="item"><span>right</span></div>
</div>
</div>
I asked what seems to be a very similar question and stack overflow directed me here. The response from #Paolamoralesval inspired me to realise the required effect can be achieved in CSS grid. Now that grid support is pretty much universal I hope that this meets everyone's needs. This solution is I believe fully responsive to window size as well as height and width of the header items as you should see if you resize the window where you view the snippet.
.header {
grid-row: 1;
grid-column: 1;
display: grid;
grid-template-rows: min-content;
grid-template-columns: 1fr 1fr 1fr;
}
.header-left {
justify-self: start;
align-self: center;
text-align: left;
background-color: red;
}
.header-center {
justify-self: center;
align-self: center;
text-align: center;
background-color: green;
}
.header-right {
justify-self: end;
align-self: center;
text-align: right;
background-color: blue;
}
.shrink-kitty {
width: 200px;
}
<html>
<body>
<div class="page">
<div class="header">
<div class="header-left">
<img class="shrink-kitty" src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Kittyply_edit1.jpg/1280px-Kittyply_edit1.jpg"/><br/>
By David Corby<br/>
Edited by: Arad<br/>Image:Kittyplya03042006.JPG<a><br/><a href="https://creativecommons.org/licenses/by/2.5" title="Creative Commons Attribution 2.5">CC BY 2.5, Link
</div>
<div class="header-center">In the middle</div>
<div class="header-right">
Much much much much more on the right hand side</br>
Indeed two lines
</div>
</div>
<div class="body">Body of the page</div>
<div class="footer">At the bottom</div>
</div>
</body>
</html>
can you give flex-grow:1 for the item class and check
.item {
background-color: blue;
color: #fff;
padding: 16px;
flex-grow:1;
}
Hope this is what you are looking for
Alternative using display table (an ancient supported grid).
Quote from https://www.w3schools.com/cssref/pr_tab_table-layout.asp
If no widths are present on the first row, the column widths are divided equally across the table, regardless of content inside the cells
.container {
display: table;
table-layout: fixed
} // would divide cells equally along table's 100% width.
.row {
display: table-row
}
.item {
display: table-cell
}

Extend column background into grid gutters

Consider this…
You are creating three column layout using grid system. Your container is fixed sized and centered using margin:0 auto.
Your design specs calls for first column to have background color that extends to the left edge of browser.
Any idea how you can achieve this? I could make it work this way, which is kind of a hack and it may not work for certain kind of image backgrounds.
HTML:
<main>
<section>
<div class="container">
<header>
<h1>Hey There!</h1>
</header>
<div>
<h2>Column 2</h2>
</div>
<div>
<h2>Column 3<h2>
</div>
</div>
</section>
</main>
CSS
main {
min-width: 800px;
}
section {
background-image: linear-gradient(to right, #dfdfdf 50%, #fff 50%);
border-bottom: 1px solid #dfdfdf;
}
.container {
width: 500px;
margin:0 auto;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
section > * {
min-height: 200px;
}
.container > div {
background-color: #fff;
padding-left: 30px;
}
You could also generate the background using a pseudoelement.
The left value is large enough to keep the background extending out of the screen. It may be problematic if you need precise position of a background image.
Also, you could consider grid-gap instead of adding padding-left to the grid items.
body {
margin: 0;
}
main {
min-width: 800px;
}
section {
border-bottom: 1px solid #dfdfdf;
}
.container {
width: 500px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
section>* {
min-height: 200px;
}
.container>div {
background-color: #fff;
padding-left: 30px;
}
header {
position: relative;
}
header:before {
content: '';
background: lightgrey;
display: block;
position: absolute;
left: -50vw;
right: 0;
bottom: 0;
height: 100%;
z-index: -1;
}
<main>
<section>
<div class="container">
<header>
<h1>Hey There!</h1>
</header>
<div>
<h2>Column 2</h2>
</div>
<div>
<h2>Column 3</h2>
</div>
</div>
</section>
</main>
You could try making the columns stretch the full width of the container, and then aligning the content inside to appear as three centered columns.
.container {
display: grid;
grid-template-columns: 1fr auto 1fr;
grid-column-gap: 20px;
min-height: 50px;
}
header {
background-color: lightgray;
display: flex;
justify-content: flex-end;
}
section {
border-bottom: 1px solid #dfdfdf;
}
body {
margin: 0;
}
p { text-align: center;}
p > span { padding: 5px; background-color: aqua; }
<main>
<section>
<div class="container">
<header>
<h1>Hey There!</h1>
</header>
<div>
<h2>Column 2</h2>
</div>
<div>
<h2>Column 3</h2>
</div>
</div>
</section>
</main>
<p><span>True Center</span></p>
codepen demo

Resources