Masonry layout with css grid - css

I'm trying to create masonry layout using css grid layout. All items in grid have variable heights. And I don't know what items will be. So I can't define grid-row for each item. Is it possible to start each new item right after end of previous in column?
Code I'm trying:
.wrapper {
display: grid;
grid-template-columns: repeat(auto-fill, 330px);
align-items: flex-start;
grid-column-gap: 10px;
grid-row-gap: 50px;
}
.item {
background: black;
border-radius: 5px;
}
<div class="wrapper">
<div class="item" style="height:50px"></div>
<div class="item" style="height:100px"></div>
<div class="item" style="height:30px"></div>
<div class="item" style="height:90px"></div>
<div class="item" style="height:80px"></div>
<div class="item" style="height:50px"></div>
<div class="item" style="height:70px"></div>
<div class="item" style="height:40px"></div>
</div>
full codepen here

In your question you are setting the height of each item individually. If you are happy to do this then a Masonry layout can easily be achieved with grid.
Instead of setting a height for each item set grid-row-end so that each item spans a certain number of rows.
<div class="item" style="grid-row-end: span 5"></div>
The height of the item will then depend on the values of grid-auto-rows and grid-row-gap you have set for the grid.
I have made a Codepen here: https://codepen.io/andybarefoot/pen/NaprOB
If you don't want to individually set the grid-row-end value for each item you can use a bit of JavaScript to do it dynamically. I put another "container" div inside each item and measure the height of this container to calculate how many rows the item needs to span. I do this on page load, and again for each item when any images are loaded (as the height of the content will have changed). If you combine this approach with a responsive layout then you should also recalculate on page resize as the width of the columns may have changed and this will affect the height of the content.
Here's my full example with responsive column resizing: https://codepen.io/andybarefoot/pen/QMeZda
If you have items with variable widths you can still achieve a similar effect but the packing of the grid won't be perfect and the item order may be changed to optimise the packing.
I wrote a blog on Medium about this approach in case it is of interest: A Masonry style layout using CSS Grid

You can set span values for grid-row-end dynamically (with a bit of JS, like the one based on my Codepen experiment in the example below) and use the dense keyword for grid-auto-placement:
const gridStyles = getComputedStyle(document.querySelector('.wrapper',null));
const rowHeight = parseInt(gridStyles.getPropertyValue('--grid-row-height'));
const gap = parseInt(gridStyles.getPropertyValue('--grid-gutter'));;
let makeGrid = function() {
let items = document.querySelectorAll('.item');
for (let i=0, item; item = items[i]; i++) {
// take an item away from grid to measure it
item.classList.add('is-being-measured');
let height = item.offsetHeight;
// calcylate the row span
let rowSpan = Math.ceil((height + gap)/(rowHeight + gap));
// set the span value for grid-row-end
item.style.gridRowEnd = 'span '+rowSpan;
// return the item into the grid
item.classList.remove('is-being-measured');
}
}
window.addEventListener('load', makeGrid);
window.addEventListener('resize', () => {
clearTimeout(makeGrid.resizeTimer);
makeGrid.resizeTimer = setTimeout(makeGrid, 50);
});
.wrapper {
display: grid;
grid-template-columns: repeat(auto-fill, 330px);
--grid-gutter: 10px;
grid-gap: var(--grid-gutter);
--grid-row-height: 10px;
grid-auto-rows: var(--grid-row-height);
grid-auto-flow: row dense;
position: relative;
}
.item {
background: black;
color: white;
border-radius: 5px;
}
.item.is-being-measured {
/* temporary styles for measuring grid items */
position: absolute;
width: 330px;
top: 0;
left: 0;
}
.item > * { margin-left: 20px; }
<div class="wrapper">
<div class="item"><h3>1.1</h3><p>1.2</p></div>
<div class="item"><p>2.1</p><p>2.2</p><p>2.3</p><p>2.4</p><p>2.5</p></div>
<div class="item"><h2>3.1</h2></div>
<div class="item"><h2>4.1</h2><p>4.2</p><p>4.3</p><p>4.4</p></div>
<div class="item"><p>5.1</p><p>5.2</p><p>5.3</p><p>5.4</p></div>
<div class="item"><h2>6.1</h2><p>6.2</p></div>
<div class="item"><h2>7.1</h2><p>7.2</p><p>7.3</p></div>
<div class="item"><p>8.1</p><p>8.2</p></div>
</div>

This is one way to create the Masonry layout using only CSS.
*,
*:before,
*:after {
box-sizing: border-box !important;
}
article {
-moz-column-width: 13em;
-webkit-column-width: 13em;
-moz-column-gap: 1em;
-webkit-column-gap: 1em;
}
section {
display: inline-block;
margin: 0.25rem;
padding: 1rem;
width: 100%;
background: #efefef;
}
p {
margin: 1rem 0;
}
body {
line-height: 1.25;
}
<article>
<section>
<p>Lorem ipsum dolor sit amet, consectetur.</p>
</section>
<section>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aliquid reprehenderit expedita odio beatae est.</p>
</section>
<section>
<p>Lorem ipsum dolor sit amet, consectetur.</p>
</section>
<section>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nobis quaerat suscipit ad.</p>
</section>
<section>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Rem nihil alias amet dolores fuga totam sequi a cupiditate ipsa voluptas id facilis nobis.</p>
</section>
<section>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Rem ut debitis dolorum earum expedita eveniet voluptatem quibusdam facere eos numquam commodi ad iusto laboriosam rerum aliquam.</p>
</section>
<section>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
</section>
<section>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat architecto quis tenetur fugiat veniam iste molestiae fuga labore!</p>
</section>
<section>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odit accusamus tempore at porro officia rerum est impedit ea ipsa tenetur. Labore libero hic error sunt laborum expedita.</p>
</section>
<section>
<p>Lorem ipsum dolor sit.</p>
</section>
<section>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima asperiores eveniet vero velit eligendi aliquid in.</p>
</section>
<section>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloribus dolorem maxime minima animi cum.</p>
</section>
</article>
Note: I didn't made the code, I found it an made some small adaptation,
the original code can be found here.
Please note that, as pointed out by Zen:
[...] the items are laid out top-to-bottom, left-to-right,
whereas what one usually expects (cultural assumptions excused) is
left-to-right, top-to-bottom layout. This is the showstopper for the usual CSS3-columns-based recommendations.

You can accomplish this with column.
.wrapper {
column-gap: 10px;
column-count: 4;
}
.item {
display: inline-block;
background: #000;
width: 100%;
border-radius: 3px;
}
It looks like you were trying to use a combination of flex and grid, which may have been confusing things. As far as I know, flex is relative to the rest of the items on the page, where setting a column affects items falling into those columns.
updated codepen

Related

Items not centering inside of grid container

I am having trouble centering a heading and text element in a 2x2 grid. I have already tried using justify-content/items/self on the parent container ".skills", and the child-elements ".skills-item". Also, note that the grid is inside a flex container, I do not know if that has anything to do with it but I am clueless.
.about {
display: flex;
flex-direction: column;
align-items: center;
padding: 12.8rem 8rem 6.4rem 8rem;
background-color: #ebebff;
}
.backstory {
display: flex;
flex-direction: column;
align-items: center;
width: 60rem;
margin-bottom: 4.8rem;
text-align: center;
}
.skills {
display: grid;
grid-template-columns: repeat(2, 1fr);
row-gap: 4.8rem;
column-gap: 6.4rem;
justify-items: center;
}
.skills-item {
display: inline-block;
}
<section class="about">
<div class="backstory">
<h2>Hi, I'm Mayitu Wangala</h2>
<p>
I'm a software developer. I became interested in computer science in college and became immersed in the world of IT. Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos cum magni similique adipisci.
</p>
</div>
<div class="skills">
<!--Rename/regroup skills later-->
<div class="skills-item 3-lang">
<h3 class="skills-heading">HTML, CSS, Javascript</h3>
<p>
My knowledge of HTML, CSS, and javascript allows me to create beautiful, responsive projects and websites.
</p>
</div>
<div class="skills-item python">
<h3 class="skills-heading">Python</h3>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos cum magni similique adipisci
</p>
</div>
<div class="skills-item data-structures">
<h3 class="skills-heading">Data Structures</h3>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos cum magni similique adipisci
</p>
</div>
<div class="skills-item design">
<h3>Design</h3>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos cum magni similique adipisci
</p>
</div>
</div>
</section>
You should set the parent class as flexbox in order to align the child elements as you wish.
.skills-item{
display:flex;
}
Since the text inside p tags won't seem like centered, even if it is, you can try to use text-align:center;.

How to make image go below text for mobile

How do I make the image go under the text in the mobile? And for the text to go on the left side, instead of being more on the right - if this makes sense :)
CSS (Image): https://pastebin.com/pGBdbBhs
CSS (Content): https://pastebin.com/1SY2JXUa
HTML:
<div class="content">
<div class="left-200">
<div>
<h3>Lorem ipsum</h3>
<div class="padding-4"></div>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Eius, natus pariatur aut soluta sed consectetur deleniti tempore ducimus at quas officia, deserunt eaque magni!</p>
</div>
<div class="padding-10"></div>
<img src="./images/default-image.png" alt="Image" class="image"/>
</div>
</div>
Desktop:
Mobile:
Thank you!
To make a responsive design you can either go the bootstrap root or you can do the #media in the CSS. Take a look at this site which will point you in the right direction.
https://www.w3schools.com/cssref/css3_pr_mediaquery.asp
I solved it! Basically I wrapped it up in a div and set display:none; if the device was on mobile. And I did the same thing for desktop!
Your CSS and HTML looks a little random here and there. For example those padding divs. Also your .left-200 class has two display properties. I would recommend to clean it up and then add the paddings, margins etc.
You really only need display: flex and flex-wrap for this task and set the other values like width accordingly.
.content {
padding: 200px 50px;
display: flex;
flex-wrap: wrap;
}
#flex-text {
width: 400px;
}
<div class="content">
<div id="flex-text">
<h3>Lorem ipsum</h3>
p>Lorem ipsum dolor sit amet consectetur adipisicing elit. <!--
-->Eius, natus pariatur aut soluta sed consectetur deleniti tempore ducimus at quas officia, deserunt eaque magni!</p>
</div>
<div id="flex-image"> <img src="./images/default-image.png" alt="Image" class="image"/> </div>
</div>

CSS force text to fit into a particular height by overflowing its parent horizontally

I've got a relatively long phrase that consumes way too much space on mobile devices. It looks something like this:
.artificial-phone-viewport {
width: 320px;
height: 500px;
border: 1px solid darkgrey;
}
.container {
width: 100%;
height: 100%;
}
.text {
/*
* Don't want to change font-size, because text
* sometimes maybe shorter and 2.2rem is perfect
* for phrases that are not as long
*/
font-size: 2.2rem;
}
<body class="artificial-phone-viewport">
<div class="container">
<div class="text">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Temporibus saepe illum a magni sequi error fugit dolore neque voluptates in laborum.
</div>
</div>
</body>
What I want is to make this text span at most, let's say, 10rem height. If it can't fit into 10rem of height, it should instead expand horizontally, maybe, overflowing its parent, maybe like this:
.artificial-phone-viewport {
width: 320px;
height: 500px;
border: 1px solid darkgrey;
}
.text {
font-size: 2.2rem;
white-space: nowrap;
}
<body class="artificial-phone-viewport">
<!-- Deleted container to reduce code, it actually
doesn't matter, because it anyway spans
100% width and height of its parent -->
<div class="text">
Lorem ipsum dolor, sit amet consectetur<br/>
adipisicing elit. Temporibus saepe illum<br/>
a magni sequi error fugit dolore neque<br/>
voluptates in laborum.
</div>
</body>
P.S. This snippet is just an example of what I want to see, I don't want any of these <br/>s or white-space: nowrap. Also I want the text to overflow its parent, because I then can use Javascript to scale it propertly, but it is not very relevant for the question, I suppose.
So I figured out a way to do it with Javascript, although I don't like it too much. I just increased the width of the element, until the height was small enough, like this
const text = document.querySelector('.text')
const rem = parseFloat(
getComputedStyle(document.documentElement).fontSize
)
let width = text.clientWidth / rem
while(text.clientWidth > 10*rem) {
width++
text.style.width = `${width}rem`
}
.artificial-phone-viewport {
width: 320px;
height: 500px;
border: 1px solid darkgrey;
}
.text {
font-size: 2.2rem;
}
<body class="artificial-phone-viewport">
<div class="text">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Temporibus saepe illum a magni sequi error fugit dolore neque voluptates in laborum.
</div>
</body>
It's not very nice, but it works for me. If someone finds a way to do it without javascript, I'm open to other solutions

Flex item exceeds its container

I have a flex container with a defined width. Container has flex-direction: row and 2 columns. One is fixed width, it is inflexible. The other is flexible and should fit all container's remaining space.
When flexible column content is long enough it overflows the container, exceeds its width.
Why is that happening? And how should I fix it right way?
Note: I already solved it by using flex: 1 0 0 instead of 1 0 auto and it would be just fine. But I just don't understand why it stops exceeding the parent and why it starts wrapping the content? Is it even the right way to do it?
HTML:
<div class="flex-container">
<div class="flex-item inflexible">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</div>
<div class="flex-item flexible">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</div>
</div>
CSS:
.flex-container {
display: flex;
flex-direction: row;
width: 500px;
background-color: green;
}
.flex-item {
display: block;
margin: 20px 0;
}
.inflexible {
flex: 0 0 auto;
width: 100px;
background-color: red;
}
.flexible {
flex: 1 0 auto;
background-color: blue;
}
The JSFiddle
To achieve expected result, specify the width for the .flexible class as well
.flexible {
flex: 1 0 auto;
width: 200px;
background-color: blue;
word-wrap: break-word;
}
http://codepen.io/nagasai/pen/LkJzLz
With flex: 1 0 auto in .flexible you set the following
1 is flex-grow and its set to grow
0 is flex-shrink so you set that to don't shrink
auto is flex-basis which in this case refers to width which is determined by the flex item's contents.
Also you can't use display: block in flex-container it doesn't work. Default value of flex-direction is row so you can remove that. You can set fixed width of .inflexible like this flex: 0 0 100px;. And for .flexible you can just use flex: 1 and it will take rest of free width.
.flex-container {
display: flex;
width: 500px;
background-color: green;
}
.flex-item {
margin: 20px 0;
}
.inflexible {
flex: 0 0 100px;
background-color: red;
}
.flexible {
flex: 1;
background-color: blue;
}
<div class="flex-container">
<div class="flex-item inflexible">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus, cupiditate earum quos! Laborum quibusdam dolor temporibus corporis
</div>
<div class="flex-item flexible">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tenetur modi sed ab voluptatum obcaecati repudiandae, quia architecto ipsa deserunt recusandae dolorum harum, aperiam sint, molestias iure voluptatem doloremque. In, rem.
</div>
</div>
In http://www.w3schools.com/cssref/css3_pr_flex-basis.asp it says the following about flex-basis: auto:
The length is equal to the length of the flexible item. If the item has no length specified, the length will be according to its content
flex-basis is the third property set by the flex property shorthand.
So if you set it to auto, the .flexible div's maximum width (depending on its content) will be the width of the display: flex container.
I think you're misunderstanding one or two of the flex values here, if I understand you correctly. Consider the following codepen:
http://codepen.io/anon/pen/NALvPw
Check this out:
.your-flex-item { /*(child, not container)*/
flex: <flex-grow> <flex-shrink> <flex-basis>;
}
Yours was:
.inflexible {
flex: 0 0 auto;
width: 100px;
background-color: red;
}
.flexible {
flex: 1 0 auto;
background-color: blue;
}
If you prefer doing it this way, I suggest changing the auto value to your desired width, so more like this:
.inflexible {
flex: 0 0 100px; /*auto changed to 100px*/
width: 100px;
background-color: red;
}
.flexible {
flex: 1 0 auto; /* 1 0 auto will grow to whatever 'auto' will allow it to grow to. Put in size here for it to grow 'up-to' that size. */
background-color: blue;
}
Update flex: 1 0 auto; to just flex: 1 in .flexible. And remove flex: flex: 0 0 auto; from .inflexible.
To understand more about flexbox check https://css-tricks.com/snippets/css/a-guide-to-flexbox/
Updated Fiddle.
.flex-container {
display: flex;
flex-direction: row;
width: 500px;
background-color: green;
}
.flex-item {
display: block;
margin: 20px 0;
}
.inflexible {
flex: 0 0 auto;
width: 100px;
background-color: red;
}
.flexible {
flex: 1 0 auto;
background-color: blue;
}
.solution .flexible {
flex: 1 0 0;
}
.question {
margin-bottom: 20px;
}
<div class="question">
<div class="flex-container">
<div class="flex-item inflexible">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus, cupiditate earum quos! Laborum quibusdam dolor temporibus corporis
</div>
<div class="flex-item flexible">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tenetur modi sed ab voluptatum obcaecati repudiandae, quia architecto ipsa deserunt recusandae dolorum harum, aperiam sint, molestias iure voluptatem doloremque. In, rem.
</div>
</div>
</div>
<div class="solution">
<div class="flex-container">
<div class="flex-item inflexible">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus, cupiditate earum quos! Laborum quibusdam dolor temporibus corporis
</div>
<div class="flex-item flexible">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tenetur modi sed ab voluptatum obcaecati repudiandae, quia architecto ipsa deserunt recusandae dolorum harum, aperiam sint, molestias iure voluptatem doloremque. In, rem.
</div>
</div>
</div>
The flex-wrap property is a sub-property of the Flexible Box Layout module.
It defines whether the flex items are forced in a single line or can be flowed into multiple lines.
The flex-wrap property accepts 3 different values:
nowrap (default): single-line which may cause the container to overflow
wrap: multi-lines, direction is defined by flex-direction
wrap-reverse: multi-lines, opposite to direction defined by flex-direction
SO
try to use flex-wrap to make the flex items in multiply lines.

HTML - How to keep semantically correct <article> placement with scattered containers?

I'm building a HTML document that is supposed to be semantically correct. Therefore, <aside> that is related to the website (i.e. sidebar) should be placed outside <article> and <aside> related directly to the article (if any) should be placed inside <article>.
That's not a problem with the left layout on my image because this will work:
<article>article</article>
<aside>sideabr</aside>
.article, .aside { float: left; }
.article { width: 70%; }
.aside { width: 30%; }
But what if main <h1> of the article is supposed to be placed above the article and the sidebar? I can't just nest the article and the sidebar in <article> because the sidebar is not a side note in the article. And I can't nest header + paragraphs in <article> because the sidebar will not align to paragraphs but to the header like in the first case.
Any idea how the HTML structure of such document should look like?
Most websites good as reference use the left version: http://twentytwelvedemo.wordpress.com/about-2/ so I can't find anything reliable to learn from.
This is how it should look like technically but can this be done in HTML at all?:
By separating article in two: heading and content (just a div) and then floating this content, you can achieve the desired effect.
aside may be floating too (you must then set a width) or be set to overflow: hidden.
You can't add a clearfix on article (or aside will stay below) so you've to put it on parent (here section). Same for background if it must have one.
Demo: http://codepen.io/anon/pen/BAjLr
CSS
section {
width: 500px;
background-color: tomato;
}
/* clearfix */
section:after {
content: '';
display: table;
clear: left;
}
/* article */
h1 {
margin-bottom: 0;
}
article .content {
float: left;
width: 300px;
background-color: lightgreen;
}
/* aside of section */
aside {
overflow: hidden;
color: white;
background-color: darkslateblue;
}
HTML
<section>
<article>
<h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Id laudantium beatae quae recusandae!</h1>
<div class="content">
<p>article Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nesciunt repudiandae nam commodi iusto ullam neque aliquam ut numquam. Cumque ut tempora consectetur quam velit ad incidunt necessitatibus saepe eos exercitationem.</p>
</div>
</article>
<aside>
<h2>sidebar</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laboriosam quae</p>
</aside>
</section>

Resources