<div class="grid">
  <div class="element"></div>
  <div class="element"></div>
  <div class="element"></div>
  <div class="element"></div>
  <div class="element"></div>
  <div class="element"></div>
  <div class="element"></div>
</div>

<script> CSS.layoutWorklet.addModule('https://codepen.io/tomquinonero/pen/vYZPbxJ.js');
</script>
body {
  font-family: sans-serif;
  height: 100vh;
  display: grid;
  place-items: center;
  background: #f5b4a5;
}
.grid {
  display: layout(masonry);
  --padding: 20;
  --columns: 2;
  width: 50rem;
}

.element {
  --brandingColor: #5f64e2;
  width: 20rem;
  height: 20rem;
  background: var(--brandingColor);
  border-radius: 4rem;
}
.element:nth-child(2),
.element:nth-child(6),
.element:nth-child(3) {
  height: 30rem;
}

.element:nth-child(4),
.element:nth-child(5) {
  height: 10rem;
}
registerLayout(
  "masonry",
  class {
    static get inputProperties() {
      return ["--padding", "--columns"];
    }

    async intrinsicSizes() {
      /* TODO implement :) */
    }
    async layout(children, edges, constraints, styleMap) {
      const inlineSize = constraints.fixedInlineSize;

      const padding = parseInt(styleMap.get("--padding").toString());
      const columnValue = styleMap.get("--columns").toString();

      // We also accept 'auto', which will select the BEST number of columns.
      let columns = parseInt(columnValue);
      if (columnValue == "auto" || !columns) {
        columns = Math.ceil(inlineSize / 350); // MAGIC NUMBER \o/.
      }

      // Layout all children with simply their column size.
      const childInlineSize = (inlineSize - (columns + 1) * padding) / columns;
      const childFragments = await Promise.all(
        children.map((child) => {
          return child.layoutNextFragment({ fixedInlineSize: childInlineSize });
        })
      );

      let autoBlockSize = 0;
      const columnOffsets = Array(columns).fill(0);
      for (let childFragment of childFragments) {
        // Select the column with the least amount of stuff in it.
        const min = columnOffsets.reduce(
          (acc, val, idx) => {
            if (!acc || val < acc.val) {
              return { idx, val };
            }

            return acc;
          },
          { val: +Infinity, idx: -1 }
        );

        childFragment.inlineOffset =
          padding + (childInlineSize + padding) * min.idx;
        childFragment.blockOffset = padding + min.val;

        columnOffsets[min.idx] =
          childFragment.blockOffset + childFragment.blockSize;
        autoBlockSize = Math.max(
          autoBlockSize,
          columnOffsets[min.idx] + padding
        );
      }

      return { autoBlockSize, childFragments };
    }
  }
);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.