Reputation: 4796
Is there a more automatic way to have data flow around an element? I feel like I've gotten really close with CSS Grid, but there are so many nooks and crannies in Grid that I haven't found yet, so maybe I just need to add a couple extra rules.
Here's what I'm trying to do:
This would be really similar to in MS Word or Google Docs where you have an image on the top right, and all the text flows around it, but with elements instead of text.
What I have in the snippet below is mostly what I'm going for, with the following exceptions:
When the first section
is short, there's plenty of room for a 4th section to be left of the aside
. With the current method in order for this to work I'd need to change grid-row: 1 / 4
to grid-row: 1 / 5
, and add aside ~ section:nth-of-type(4)
to the list of narrow sections
.
When the first section
is tall, one of the section
s below it is well past the aside
, so it should take up the full 5 grid columns.
In this snippet, the heights are static, but in the real world they would be dynamic. Is there any more automatic way to solve this with CSS Grid? The only other way I can think of is to detect the sizes with javascript and add classes or inline css depending on the heights of the various elements.
document.querySelector('button').addEventListener('click', () => {
const aside = document.querySelector('aside');
if (aside) {
aside.remove();
} else {
document.querySelector('main').prepend(document.createElement('aside'));
}
});
document.querySelector('a').addEventListener('click', (event) => {
event.target.parentElement.classList.toggle('tall');
});
main {
display: grid;
padding: 5px;
margin: 5px;
grid-gap: 5px;
background-color: gray;
grid-template-columns: repeat(5, 1fr);
}
aside {
height: 120px;
background-color: green;
grid-column: 4 / 6;
grid-row: 1 / 4;
}
section {
height: 20px;
background-color: white;
grid-column-end: span 5;
}
section.tall {
height: 100px;
}
aside ~ section:nth-of-type(1),
aside ~ section:nth-of-type(2),
aside ~ section:nth-of-type(3) {
grid-column-end: span 3;
}
<button>Toggle Aside</button>
<main>
<aside></aside>
<section><a href="javascript://">Toggle Height</a></section>
<section></section>
<section></section>
<section></section>
<section></section>
<section></section>
</main>
Upvotes: 2
Views: 798
Reputation: 125
Note: This answer doesn't actually work. I was mistaken about the way auto-placement works with named grid areas. I'm leaving it for reference though.
I'd use a simpler grid. It seems like you don't actually want a uniform 5 column grid. something like:
main {
display: grid;
padding: 5px;
margin: 5px;
grid-gap: 5px;
background-color: gray;
/*grid-template-columns: repeat(5, 1fr);*/
grid-template: "a aside"
"a aside"
"a aside"
"wide wide"
"wide wide"
/ 4fr 1fr;
}
aside {
/* height: 120px; */
background-color: green;
/*grid-column: 4 / 6;*/
/*grid-row: 1 / 4;*/
grid-area: aside;
}
section {
height: 20px;
background-color: white;
/* grid-column-end: span 5; */
}
<button>Toggle Aside</button>
<main>
<aside></aside>
<section><a href="javascript://">Toggle Height</a></section>
<section></section>
<section></section>
<section></section>
<section></section>
<section></section>
</main>
MDN grid-template
documentation: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template
Upvotes: 0
Reputation: 13536
It looks that the simplest solution to this problem would be using the good old float
property, which causes other elements to flow around the given element "out of the box". The only addition needed is to make the sections establish the new Block Formatting Context, to prevent them from overlapping with floating element (as display: block
elements do by default). There are several ways to do this, including the standard (but, unfortunately, not cross-browser yet) solution — display: flow-root
.
document.querySelector('button').addEventListener('click', () => {
const aside = document.querySelector('aside');
if (aside) {
aside.remove();
} else {
document.querySelector('main').prepend(document.createElement('aside'));
}
});
document.querySelector('a').addEventListener('click', (event) => {
event.target.parentElement.classList.toggle('tall');
});
main {
/* the display:flow-root alternative with the best balance
between browser support and unwanted side effects (IMO).
Other alternatives live comparison: https://codepen.io/SelenIT/pen/qrORXm */
column-count: 1;
padding: 5px 5px 0;
margin: 5px;
background-color: gray;
}
aside {
height: 120px;
width: calc((100% - 20px) * 0.4); /* 2/5 of (100% - 4 gaps of 5px each) */
background-color: green;
margin: 0 0 5px 5px;
float: right;
}
section {
column-count: 1;
height: 20px;
background-color: white;
margin-bottom: 5px;
}
section.tall {
height: 100px;
}
<button>Toggle Aside</button>
<main>
<aside></aside>
<section><a href="javascript://">Toggle Height</a></section>
<section></section>
<section></section>
<section></section>
<section></section>
<section></section>
</main>
Upvotes: 2