How to wrap element in CSS grid - css

With Flex, I can wrap using flex-wrap. I'm experimenting with CSS grid and want to wrap an element but can't find a way to do that. Is it possible?
Here is my code
.item1 {
grid-area: topic;
}
.item2 {
grid-area: user;
}
.item3 {
grid-area: bin;
}
.bin-grid {
display: grid;
grid-template-columns: 200px auto;
grid-template-areas: 'topic topic topic' 'user bin bin';
gap: 0;
padding-bottom: 15px;
margin-bottom: 20px;
}
<div class="bin-grid">
<div class="item1">Header</div>
<div class="item2">Left</div>
<div class="item3">Right</div>
</div>
I tried using grid-template-columns:200px auto; but that isn't wrapping the elements. I need item2 and item3 to wrap.
After a link that was posted by a SO user for the same issue, I tried using grid-template-columns:repeat(auto-fill, 186px) but the elements will not wrap in my case. Yet, I can see the example works just fine. I'm not sure why it won't work in my case.

Related

Grid layout with search bar in header

I am trying to come up with a solution for advanced layout. I decided to use a css grid as it seemed to be the best match for my needs.
The requirements:
Header - consists of three elements - logo, search bar and menu
search bar should be aligned with the main content if space allows, i.e. on small screen search bar doesn't need to start where the main content starts but it should end where main content ends or take all available space
Main - consists of content and sidebar- should be centred and take max 100rem width space
content should be 2 times bigger than the sidebar
main content should have at least 1rem space from left and right
This is how it looks now. It matches my requirements on big screens (4k) but when the screen gets smaller it gets messy. I would really like to avoid any javascript and solve this with pure CSS if possible.
How would you approach this problem? I am now more inclining that this is not solvable with pure CSS and JS is needed. (Probably some resize observer on main-content element)
Examples:
Big screen -> alignment is correct ✔️
Small screen -> alignment is not correct. ❌ The search box should be within the black "brackets"
Even smaller screen -> alignment is not correct too. ❌ The search box should expand up to the black line
html,
body {
padding: 0;
margin: 0;
}
body {
font-family: sans-serif;
display: grid;
grid-template-columns: minmax(1rem, 1fr) minmax(min-content, 100rem) minmax(
1rem,
1fr
);
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
". main .";
min-height: 100vh;
}
.header {
padding: 1rem 1rem;
background: green;
grid-area: header;
display: grid;
grid-template-columns: minmax(max-content, 1fr) minmax(min-content, 100rem) minmax(
max-content,
1fr
);
}
.search-box {
background: yellow;
max-width: calc(2/3 * 100%);
}
.logo {
background: yellowgreen;
min-width: 5rem;
}
.menu {
background: brown;
}
.main {
grid-area: main;
display: flex;
}
.main-content {
background: red;
flex: 2;
}
.sidebar {
background: blue;
flex: 1;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<header class="header">
<div class="logo">Logo</div>
<div class="search-box">
Search box should should match main content position
</div>
<div class="menu">Menu with two submenus at least</div>
</header>
<main class="main">
<section class="main-content">Main content</section>
<aside class="sidebar">Sidebar</aside>
</main>
</body>
</html>
Sandbox available here

How to make images in a CSS Grid lay next to each other and jump to another row when lacking space

I'm trying to position images the way shown in the picture using CSS Grid and I can't find a right solution.
Right now I'm simply changing the grid flow to column, but the grid elements don't jump to another row when they meet the end of the container - they resize it and stay in the same, first row.
I tried to use grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)) - it solves this jump to another line issue, but it gives all the elements a fixed width whereas some images are not that wide. It creates empty holes between images which I'd like to avoid.
Any ideas on how to accomplish it?
wrong solution 1
Code from the image above:
container {
display: grid;
grid-gap: 1rem;
grid-auto-flow: column;
}
photo { // all container's elements have this class
height: 10rem;
width: auto;
}
wrong solution 2
Code from the image above:
container {
display: grid;
grid-gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}
photo {
height: 10rem;
max-width: 100%;
}
You see, this is tusk for flex, not for grid. Using grid means columns with same width on each row. No need here at all.
html {
font-size: 10px;
}
.conteiner {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.photo {
height: 10rem;
margin: 0 1rem 1rem 0;
}
.photo img {
width: auto;
height: 100%;
}
<div class="conteiner">
<div class="photo"><img src="https://via.placeholder.com/500x200"></div>
<div class="photo"><img src="https://via.placeholder.com/300x200"></div>
<div class="photo"><img src="https://via.placeholder.com/200x200"></div>
<div class="photo"><img src="https://via.placeholder.com/400x200"></div>
<div class="photo"><img src="https://via.placeholder.com/500x200"></div>
<div class="photo"><img src="https://via.placeholder.com/300x200"></div>
<div class="photo"><img src="https://via.placeholder.com/200x200"></div>
</div>
Although I'm using Justified gallery jQuery plugin, if I wand all images in each row to fill all the width.

Center The Leftover Item From The Last Row In GRID (1fr 1fr)

This is something that I've been struggling with for a while, but I can't seem to find a way to do it.
If you have an odd number of items in grid and you want 2 items per row (1fr 1fr), you end up with a single item in the last row that is left-centered.
I just want to make it centered so it looks nicer.
Here's a picture too.
You can try something like this jsfiddle:
/* visibility properties */
body {
width: 60%;
margin: 5% auto;
}
div {
margin: 3%;
width: 100px;
height: 100px;
background-color: black;
justify-self: center;
}
div:nth-of-type(2n) {
background-color: red;
}
/* actual code: */
section {
display: grid;
grid-template-columns: 1fr 1fr;
}
#last-div {
grid-column: 1 / span 2;
}
<section>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div id="last-div">
</div>
</section>
Get more info on CSS Grid: complete-guide-grid
You could try something like this since I faced a similar issue in one of my earlier projects.
grid-template-columns : repeat(auto-fit, minmax(<minSize>, 1fr));
Set minSize to whatever minimum width you want an element to occupy.

CSS Grid min-content cell expanding based on neighbour

Ive got a CSS grid that's two columns, five rows (at a display above 768px).
All the rows are set to "min-content" bar the last, being set to auto.
I've defined template grid areas, one for each "cell", with the exception of one that covers the 3rd to 5th row on the second column - named a6 (in the sample code)
When there is little or no content in a6, the grid behaves exactly as I desire. However, if when a bit more content is added to a6, the a5 and a7 areas expand in height, despite their content not changing.
CSS:
html,
body {
height: 100vh;
padding: 0;
margin: 0;
}
.maingrid {
height: 100%;
padding-left: 15px;
padding-right: 15px;
background-color: red;
display: grid;
grid-template-rows: min-content min-content min-content min-content min-content min-content min-content auto;
grid-template-areas: 'a1' 'a2' 'a3' 'a4' 'a5' 'a6' 'a7' 'a8';
grid-row-gap: .2em;
}
#media only screen and (min-width:768px) {
.maingrid {
grid-template-columns: 9fr 3fr;
grid-template-rows: min-content min-content min-content min-content auto;
grid-template-areas: 'a1 a2' 'a3 a4' 'a5 a6' 'a7 a6' 'a8 a6';
background-color: darkcyan;
}
}
.maingrid div {
background-color: black;
}
.a1 {
grid-area: a1;
background-color: pink !important;
}
.a2 {
grid-area: a2;
background-color: aliceblue !important;
}
.a3 {
grid-area: a3;
background-color: aqua !important;
}
.a5 {
grid-area: a4;
background-color: blue !important;
}
.a4 {
grid-area: a5;
background-color: brown !important;
}
.a6 {
grid-area: a6;
background-color: burlywood !important;
}
.a7 {
grid-area: a7;
background-color: chartreuse !important;
}
.a8 {
grid-area: a8;
background-color: darkorange !important;
}
HTML:
<main class="maingrid">
<div class="a1">BLAH</div>
<div class="a2">BLAH</div>
<div class="a3">BLAH</div>
<div class="a4">BLAH</div>
<div class="a5">BLAH</div>
<div class="a6">
at<br />at<br />
</div>
<div class="a7">BLAH</div>
<div class="a8">
<button type="button" onclick="BreakTheGrid();">click me :(</button>
</div>
</main>
JS (just to get the toggle button to work):
var isBroken = false;
function BreakTheGrid() {
if (!isBroken) {
$('.a6').html("the<br/>left<br />columns<br />have<br />expanded<br />boo!<br />");
} else {
$('.a6').html("no<br/>issue");
}
isBroken = isBroken == false;
}
Here's a JSFiddle replicating the issue: https://jsfiddle.net/up6afdj4/
If you click the button in a8, you can toggle the content of a6, thus toggling the issue.
I've only just started messing around with CSS grid, so I expect its something I've got completely wrong, but I can't figure it :)
By applying auto to the fifth row, which includes the a6 grid area, you trigger the Grid auto stretch algorithm, which distributes free space among rows covered by the grid area (spec §11.5, §11.5.1 and §11.8).
If you switch from auto to 1fr, the last row then consumes all free space, pinning the rows above to the top.
revised demo
For a more detailed explanation of auto space distribution, see my answers here:
Remove wide gaps in CSS Grid
How do you collapse unused row in a CSS grid?
(Illustrations generated by Firefox DevTools Grid Inspector.)
Changing auto to 1fr in the grid-template-rows definition solves the problem.
I don't know why however, and if someone could explain it better, I'll give you the accepted answer :)

Swap DIV position with CSS only

I'm trying to swap two divs' locations for responsive design (the site looks different depending on width of the browser/good for mobile).
Right now I have something like this:
<div id="first_div"></div>
<div id="second_div"></div>
But would it be possible to swap their placements to make it look like second_div is first, using CSS only? The HTML stays the same. I've tried using floats and stuff but it doesn't seem to work the way I want it to. I don't want to use absolute positioning because the heights of the divs are always changing. Are there any solutions, or is there just no way to do this?
Someone linked me this: What is the best way to move an element that's on the top to the bottom in Responsive design.
The solution in that worked perfectly. Though it doesn’t support old IE, that doesn’t matter for me, since I’m using responsive design for mobile. And it works for most mobile browsers.
Basically, I had this:
#media (max-width: 30em) {
.container {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-orient: vertical;
-moz-box-orient: vertical;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
/* optional */
-webkit-box-align: start;
-moz-box-align: start;
-ms-flex-align: start;
-webkit-align-items: flex-start;
align-items: flex-start;
}
.container .first_div {
-webkit-box-ordinal-group: 2;
-moz-box-ordinal-group: 2;
-ms-flex-order: 2;
-webkit-order: 2;
order: 2;
}
.container .second_div {
-webkit-box-ordinal-group: 1;
-moz-box-ordinal-group: 1;
-ms-flex-order: 1;
-webkit-order: 1;
order: 1;
}
}
This worked better than floats for me, because I needed them stacked on top of each other and I had about five different divs that I had to swap around the position of.
The accepted answer worked for most browsers but for some reason on iOS Chrome and Safari browsers the content that should have shown second was being hidden. I tried some other steps that forced content to stack on top of each other, and eventually I tried the following solution that gave me the intended effect (switch content display order on mobile screens), without bugs of stacked or hidden content:
.container {
display:flex;
flex-direction: column-reverse;
}
.section1,
.section2 {
height: auto;
}
This question already has a great answer but in the spirit of exploring all possibilities here is another technique to reorder dom elements whilst still allowing them to take up their space, unlike the absolute positioning method.
This method works in all modern browsers and IE9+ (basically any browser that supports display:table) it has a drawback that it can only be used on a max of 3 siblings though.
//the html
<div class='container'>
<div class='div1'>1</div>
<div class='div2'>2</div>
<div class='div3'>3</div>
</div>
//the css
.container {
display:table;
}
.div1 {
display:table-footer-group;
}
.div2 {
display:table-header-group;
}
.div3 {
display:table-row-group;
}
This will reorder the elements from 1,2,3 to 2,3,1. Basically anything with the display set to table-header-group will be positioned at the top and table-footer-group at the bottom. Naturally table-row-group puts an element in the middle.
This method is quick with good support and requires much less css than the flexbox approach so if you are only looking to swap a few items around for a mobile layout for example then dont rule out this technique.
You can check out a live demo on codepen: http://codepen.io/thepixelninja/pen/eZVgLx
This solution worked for me:
Using a parent element like:
.parent-div {
display:flex;
flex-direction: column-reverse;
}
In my case I didn't have to change the css of the elements that I needed to switch.
In some cases you can just use the flex-box property order.
Very simple:
.flex-item {
order: 2;
}
See: https://css-tricks.com/almanac/properties/o/order/
Using CSS only:
#blockContainer {
display: -webkit-box;
display: -moz-box;
display: box;
-webkit-box-orient: vertical;
-moz-box-orient: vertical;
box-orient: vertical;
}
#blockA {
-webkit-box-ordinal-group: 2;
-moz-box-ordinal-group: 2;
box-ordinal-group: 2;
}
#blockB {
-webkit-box-ordinal-group: 3;
-moz-box-ordinal-group: 3;
box-ordinal-group: 3;
}
<div id="blockContainer">
<div id="blockA">Block A</div>
<div id="blockB">Block B</div>
<div id="blockC">Block C</div>
</div>
Assuming Nothing Follows Them
If these two div elements are basically your main layout elements, and nothing follows them in the html, then there is a pure HMTL/CSS solution that takes the normal order shown in this fiddle and is able to flip it vertically as shown in this fiddle using one additional wrapper div like so:
HTML
<div class="wrapper flipit">
<div id="first_div">first div</div>
<div id="second_div">second div</div>
</div>
CSS
.flipit {
position: relative;
}
.flipit #first_div {
position: absolute;
top: 100%;
width: 100%;
}
This would not work if elements follow these div's, as this fiddle illustrates the issue if the following elements are not wrapped (they get overlapped by #first_div), and this fiddle illustrates the issue if the following elements are also wrapped (the #first_div changes position with both the #second_div and the following elements). So that is why, depending on your use case, this method may or may not work.
For an overall layout scheme, where all other elements exist inside the two div's, it can work. For other scenarios, it will not.
Simple flexbox solution utilizing the order-property:
.container {
display: flex;
flex-direction: column;
}
.first {
order: 3;
}
.second {
order: 2;
}
<div class="container">
<div class="first">First</div>
<div class="second">Second</div>
<div class="third">Third</div>
</div>
assuming both elements have 50% width, here is what i used:
css:
.parent {
width: 100%;
display: flex;
}
.child-1 {
width: 50%;
margin-right: -50%;
margin-left: 50%;
background: #ff0;
}
.child-2 {
width: 50%;
margin-right: 50%;
margin-left: -50%;
background: #0f0;
}
html:
<div class="parent">
<div class="child-1">child1</div>
<div class="child-2">child2</div>
</div>
example: https://jsfiddle.net/gzveri/o6umhj53/
btw, this approach works for any 2 nearby elements in a long list of elements. For example I have a long list of elements with 2 items per row and I want each 3-rd and 4-th element in the list to be swapped, so that it renders elements in a chess style, then I use these rules:
.parent > div:nth-child(4n+3) {
margin-right: -50%;
margin-left: 50%;
}
.parent > div:nth-child(4n+4) {
margin-right: 50%;
margin-left: -50%;
}
Yesterday ran into the same problem. Grid areas worked out great in my case:
.content-body {
display: grid;
grid-template-areas: " left right ";
grid-template-columns: 1fr 1fr;
}
.first_div {
grid-area: right;
}
.second {
grid-area: left;
}
You don't need anything fancy. Make a copy of your second div, and place it on top. Like this
<div id="second_div_copy"></div>
<div id="first_div"></div>
<div id="second_div"></div>
Give the second_div_copy display: none when you want first div to appear on top. Give the second_div_copy display: block, and the second_div display: none when you want the second div to appear on top.
It's really that simple. Or am I missing something ?

Resources