Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      10 Top Generative AI Development Companies for Enterprise Node.js Projects

      August 30, 2025

      Prompting Is A Design Act: How To Brief, Guide And Iterate With AI

      August 29, 2025

      Best React.js Development Services in 2025: Features, Benefits & What to Look For

      August 29, 2025

      August 2025: AI updates from the past month

      August 29, 2025

      This 3-in-1 charger has a retractable superpower that’s a must for travel

      August 31, 2025

      How a legacy hardware company reinvented itself in the AI age

      August 31, 2025

      The 13+ best Walmart Labor Day deals 2025: Sales on Apple, Samsung, LG, and more

      August 31, 2025

      You can save up to $700 on my favorite Bluetti power stations for Labor Day

      August 31, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Call for Speakers – JS Conf Armenia 2025

      August 30, 2025
      Recent

      Call for Speakers – JS Conf Armenia 2025

      August 30, 2025

      Streamlining Application Automation with Laravel’s Task Scheduler

      August 30, 2025

      A Fluent Path Builder for PHP and Laravel

      August 30, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Windows 11 KB5064081 24H2 adds taskbar clock, direct download links for .msu offline installer

      August 30, 2025
      Recent

      Windows 11 KB5064081 24H2 adds taskbar clock, direct download links for .msu offline installer

      August 30, 2025

      My Family Cinema not Working? 12 Quick Fixes

      August 30, 2025

      Super-linter – collection of linters and code analyzers

      August 30, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»News & Updates»Making a Masonry Layout That Works Today

    Making a Masonry Layout That Works Today

    July 28, 2025

    Many CSS experts have weighed heavily on possible syntaxes for a new masonry layout feature last year. There were two main camps and a third camp that strikes a balance between the two:

    1. Use display: masonry
    2. Use grid-template-rows: masonry
    3. Use item-pack: collapse

    I don’t think they’ve came up with a resolution yet. But you might want to know that Firefox already supports the masonry layout with the second syntax. And Chrome is testing it with the first syntax. While it’s cool to see native support for CSS Masonry evolving, we can’t really use it in production if other browsers don’t support the same implementation…

    So, instead of adding my voice to one of those camps, I went on to figure out how make masonry work today with other browsers. I’m happy to report I’ve found a way — and, bonus! — that support can be provided with only 66 lines of JavaScript.

    In this article, I’m gonna show you how it works. But first, here’s a demo for you to play with, just to prove that I’m not spewing nonsense. Note that there’s gonna be a slight delay since we’re waiting for an image to load first. If you’re placing a masonry at the top fold, consider skipping including images because of this!

    Anyway, here’s the demo:

    CodePen Embed Fallback

    What in the magic is this?!

    Now, there are a ton of things I’ve included in this demo, even though there are only 66 lines of JavaScript:

    • You can define the masonry with any number of columns.
    • Each item can span multiple columns.
    • We wait for media to load before calculating the size of each item.
    • We made it responsive by listening to changes with the ResizeObserver.

    These make my implementation incredibly robust and ready for production use, while also way more flexible than many Flexbox masonry knockoffs out there on the interwebs.

    Now, a hot tip. If you combine this with Tailwind’s responsive variants and arbitrary values, you can include even more flexibility into this masonry grid without writing more CSS.

    Okay, before you get hyped up any further, let’s come back to the main question: How the heck does this work?

    Let’s start with a polyfill

    Firefox already supports masonry layouts via the second camp’s syntax. Here’s the CSS you need to create a CSS masonry grid layout in Firefox.

    .masonry {
      display: grid;
      grid-template-columns: repeat(
        auto-fit,
        minmax(min(var(--item-width, 200px), 100%), 1fr)
      );
      grid-template-rows: masonry;
      grid-auto-flow: dense; /* Optional, but recommended */
    }

    Since Firefox already has native masonry support, naturally we shouldn’t mess around with it. The best way to check if masonry is supported by default is to check if grid-template-rows can hold the masonry value.

    function isMasonrySupported(container) {
      return getComputedStyle(container).gridTemplateRows === 'masonry'
    }

    If masonry is supported, we’ll skip our implementation. Otherwise, we’ll do something about it.

    const containers = document.querySelectorAll('.masonry')
    
    containers.forEach(async container => {
      if (isMasonrySupported(container)) return
    })

    Masonry layout made simple

    Now, I want to preface this segment that I’m not the one who invented this technique.

    I figured out this technique when I was digging through the web, searching for possible ways to implement a masonry grid today. So kudos goes to the unknown developer who developed the idea first — and perhaps me for understanding, converting, and using it.

    The technique goes like this:

    1. We set grid-auto-rows to 0px.
    2. Then we set row-gap to 1px.
    3. Then we get the item’s height through getBoundingClientRect.
    4. We then size the item’s “row allocation” by adding the height the column-gap value together.

    This is really unintuitive if you’ve been using CSS Grid the standard way. But once you get this, you can also grasp how this works!

    Now, because this is so unintuitive, we’re gonna take things step-by-step so you see how this whole thing evolves into the final output.

    Step by step

    First, we set grid-auto-rows to 0px. This is whacky because every grid item will effectively have “zero height”. Yet, at the same time, CSS Grid maintains the order of the columns and rows!

    containers.forEach(async container => {
      // ...
      container.style.gridAutoRows = '0px'
    })
    Three columns over overlapping stacked cards with white backgrounds, black rounded borders and randomly generated placeholder text.

    Second, we set row-gap to 1px. Once we do this, you begin to notice an initial stacking of the rows, each one one pixel below the previous one.

    containers.forEach(async container => {
      // ...
      container.style.gridAutoRows = '0px'
      container.style.setProperty('row-gap', '1px', 'important')
    })
    Cards are still stacked and in three columns, but now they are more directly stacked on top of one another.

    Third, assuming there are no images or other media elements in the grid items, we can easily get the height of each grid item with getBoundingClientRect.

    We can then restore the “height” of the grid item in CSS Grid by substituting grow-row-end with the height value. This works because each row-gap is now 1px tall.

    When we do this, you can see the grid beginning to take shape. Each item is now (kinda) back at their respective positions:

    containers.forEach(async container => {
      // ...
      let items = container.children
      layout({ items })
    })
    
    function layout({ items }) {
      items.forEach(item => {
        const ib = item.getBoundingClientRect()
        item.style.gridRowEnd = `span ${Math.round(ib.height)}`
      })
    }
    A masonry layout of cards that alternates between one and two columns.

    We now need to restore the row gap between items. Thankfully, since masonry grids usually have the same column-gap and row-gap values, we can grab the desired row gap by reading column-gap values.

    Once we do that, we add it to grid-row-end to expand the number of rows (the “height”) taken up by the item in the grid:

    containers.forEach(async container => {
      // ...
      const items = container.children
      const colGap = parseFloat(getComputedStyle(container).columnGap)
      layout({ items, colGap })
    })
    
    function layout({ items, colGap }) {
      items.forEach(item => {
        const ib = item.getBoundingClientRect()
        item.style.gridRowEnd = `span ${Math.round(ib.height + colGap)}`
      })
    }
    A three-column masonry layout. Most items occupy one column but two elements stretch to take up two columns. the order flows from left to right.

    And, just like that, we’ve made the masonry grid! Everything from here on is simply to make this ready for production.

    Waiting for media to load

    Try adding an image to any grid item and you’ll notice that the grid breaks. That’s because the item’s height will be “wrong”.

    The first item in the masonry layout contains an image with text and sits behind other items in the first two columns.

    It’s wrong because we took the height value before the image was properly loaded. The DOM doesn’t know the dimensions of the image yet. To fix this, we need to wait for the media to load before running the layout function.

    We can do this with the following code (which I shall not explain since this is not much of a CSS trick 😅):

    containers.forEach(async container => {
      // ...
      try {
        await Promise.all([areImagesLoaded(container), areVideosLoaded(container)])
      } catch(e) {}
    
      // Run the layout function after images are loaded
      layout({ items, colGap })
    })
    
    // Checks if images are loaded
    async function areImagesLoaded(container) {
      const images = Array.from(container.querySelectorAll('img'))
      const promises = images.map(img => {
        return new Promise((resolve, reject) => {
          if (img.complete) return resolve()
          img.onload = resolve
          img.onerror = reject
        })
      })
      return Promise.all(promises)
    }
    
    // Checks if videos are loaded
    function areVideosLoaded(container) {
      const videos = Array.from(container.querySelectorAll('video'))
      const promises = videos.map(video => {
        return new Promise((resolve, reject) => {
          if (video.readyState === 4) return resolve()
          video.onloadedmetadata = resolve
          video.onerror = reject
        })
      })
      return Promise.all(promises)
    }

    Voilà, we have a CSS masnory grid that works with images and videos!

    A complete masonry layout with six items. The first and third items occupy the first two columns and items 2, 4, 5, and 6 flow into the third column,.

    Making it responsive

    This is a simple step. We only need to use the ResizeObserver API to listen for any change in dimensions of the masonry grid container.

    When there’s a change, we run the layout function again:

    containers.forEach(async container => {
    // ...
    const observer = new ResizeObserver(observerFn)
    observer.observe(container)
    
    function observerFn(entries) {
      for (const entry of entries) {
        layout({colGap, items})
      }
    }
    })

    This demo uses the standard Resize Observer API. But you can make it simpler by using the refined resizeObserver function we built the other day.

    containers.forEach(async container => {
      // ...
      const observer = resizeObserver(container, {
        callback () {
          layout({colGap, items})
        }
      })
    })

    That’s pretty much it! You now have a robust masonry grid that you can use in every working browser that supports CSS Grid!

    Exciting, isn’t it? This implementation is so simple to use!

    Masonry grid with Splendid Labz

    If you’re not adverse to using code built by others, maybe you might want to consider grabbing the one I’ve built for you in Splendid Labz.

    To do that, install the helper library and add the necessary code:

    # Installing the library
    npm install @splendidlabz/styles
    /* Import all layouts code */
    @import '@splendidlabz/layouts';
    // Use the masonry script
    import { masonry } from '@splendidlabz/styles/scripts'
    masonry()

    One last thing: I’ve been building a ton of tools to help make web development much easier for you and me. I’ve parked them all under the Splendid Labz brand — and one of these examples is this masonry grid I showed you today.

    If you love this, you might be interested in other layout utilities that makes layout super simple to build.

    Now, I hope you have enjoyed this article today. Go unleash your new CSS masonry grid if you wish to, and all the best!


    Making a Masonry Layout That Works Today originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleRilasciata Slackel 8.0: Una Distribuzione GNU/Linux Basata su Slackware
    Next Article Variables and Outputs: Enhancing Terraform Flexibility

    Related Posts

    News & Updates

    This 3-in-1 charger has a retractable superpower that’s a must for travel

    August 31, 2025
    News & Updates

    How a legacy hardware company reinvented itself in the AI age

    August 31, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    Why I just added Gemini 2.5 Pro to the very short list of AI tools I pay for

    News & Updates

    Building a Blended Material Shader in WebGL with Solid.js

    Web Development

    Windows Snipping Tool Gets Smarter with New Text Extractor feature in Upcoming Update

    Operating Systems

    4 ways to scale generative AI experiments into production services

    News & Updates

    Highlights

    CVE-2025-6102 – Wifi-soft UniBox Controller Os Command Injection Vulnerability

    June 16, 2025

    CVE ID : CVE-2025-6102

    Published : June 16, 2025, 3:15 a.m. | 1 hour, 14 minutes ago

    Description : A vulnerability classified as critical was found in Wifi-soft UniBox Controller up to 20250506. Affected by this vulnerability is an unknown functionality of the file /authentication/logout.php. The manipulation of the argument mac_address leads to os command injection. The attack can be launched remotely. The exploit has been disclosed to the public and may be used. The vendor was contacted early about this disclosure but did not respond in any way.

    Severity: 8.8 | HIGH

    Visit the link for more details, such as CVSS details, affected products, timeline, and more…

    How to Know When You Need Mental Help: Signs You Shouldn’t Ignore

    April 28, 2025

    ClickFix Malware Campaign Exploits CAPTCHAs to Spread Cross-Platform Infections

    August 5, 2025

    top 10 website design

    June 26, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.