Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                .masonry
  - var n = 0
  while n < 18
    - var size = Math.floor(Math.random() * 300 + 1)
    .masonry-panel
      .masonry-panel__content(style=`height: ${size}px`)
        h1= n
    - n++

              
            
!

CSS

              
                /**
  * Masonry CSS
*/
$config = {
  items: 18,
  responses: {
    breakpoints: {
      sm: 430px
      md: 768px
      lg: 992px
      xl: 1500px
    },
    cols: {
      sm: 1,
      md: 2,
      lg: 3,
      xl: 4,
    }
  }
}

.masonry
  display   flex
  flex-flow column wrap

.masonry-panel
  overflow   hidden

  for $breakpoint, $value in $config.responses.breakpoints

    define('$cols', $config.responses.cols[$breakpoint])

    @media(min-width $value)
      width (100% / $cols * 1%)

      for $panel in (1..$config.items)
        &:nth-child({$panel})
          $newOrder = $panel % $cols
          if $newOrder == 0
            $newOrder = $cols
          order $newOrder



/**
  * Theming
*/
$generateRandomColor()
  // Define hexidecimal range
  $hex = 0 1 2 3 4 5 6 7 8 9 'A' 'B' 'C' 'D' 'E' 'F'
  // get random number between 0 and $hex length
  $random()
    return floor(math(0, 'random')*((length($hex) - 1) - 0 + 1) + 0)
  $result = '#'
  for $key in (0..5)
    push($result, $hex[$random()])
  return unquote(join('', $result))

*
  box-sizing border-box

body
  margin           0
  padding          2px

h1
  font-size  42px
  margin     0
  text-align center

.masonry-panel
  &__content
    align-items     center
    border-radius   10px
    color           white
    display         flex
    justify-content center
    margin          4px
    min-height      150px
    padding         10px

  transform scale(0)
  animation scaleIn .25s cubic-bezier(.64, .57, .67, 1)
  animation-fill-mode forwards

  for $panel in (1..$config.items)
    &:nth-child({$panel})
      .masonry-panel__content
        background $generateRandomColor()
      animation-delay ($panel * .15s)

@keyframes scaleIn
  from
    transform scale(0)
  to
    transform scale(1)

              
            
!

JS

              
                (function() {
  // Classname reference
  const CLASSES = {
    MASONRY: 'masonry',
    PANEL  : 'masonry-panel',
    PAD    : 'masonry-pad',
  }

  class Masonry {
    constructor(el) {
      this.container = el
      this.panels = el.querySelectorAll(`.${CLASSES.PANEL}`) 
      this.state = {
        heights: [],
      }
      this.layout()
    }
    /**
      * Reset the layout by removing padding elements, resetting heights
      * reference and removing the container inline style
    */
    __reset() {
      const {
        container,
      } = this
      this.state.heights = []
      const fillers = container.querySelectorAll(`.${CLASSES.PAD}`)
      if (fillers.length) {
        for(let f = 0; f < fillers.length; f++) {
          fillers[f].parentNode.removeChild(fillers[f])
        }
      }
      container.removeAttribute('style')
    }
    /**
      * Iterate through panels and work out the height of the layout
    */
    __populateHeights() {
      const {
        panels,
        state,
      } = this
      const {
        heights,
      } = state
      for (let p = 0; p < panels.length; p++) {
        const panel = panels[p]
        const {
          order: cssOrder,
          msFlexOrder,
          height,
        } = getComputedStyle(panel)
        const order = cssOrder || msFlexOrder
        if (!heights[order - 1]) heights[order - 1] = 0
        heights[order - 1] += parseInt(height, 10)
      }
    }
    /**
      * Set the layout height based on referencing the content cumulative height
      * This probably doesn't need its own function but felt right to be nice
      * and neat
    */
    __setLayout() {
      const {
        container,
        state,
      } = this
      const {
        heights,
      } = state
      this.state.maxHeight = Math.max(...heights)
      container.style.height = `${this.state.maxHeight}px`
    }
    // /**
    //   * JavaScript method for setting order of each panel based on panels.length and desired number of columns
    // */
    // __setOrders() {
    //   const {
    //     panels,
    //   } = this
    //   const cols = 3 // There needs to be an internal reference here that checks how many cols for viewport size
    //   panels.forEach((panel, idx) => {
    //     panel.style.order = ((idx + 1) % cols === 0) ? cols : (idx + 1) % cols
    //   })
    // }
    /**
      * Pad out layout "columns" with padding elements that make heights equal
    */
    __pad() {
      const {
        container,
      } = this
      const {
        heights,
        maxHeight,
      } = this.state
      heights.map((height, idx) => {
        if (height < maxHeight && height > 0) {
          const pad             = document.createElement('div')
          pad.className         = CLASSES.PAD
          pad.style.height      = `${maxHeight - height}px`
          pad.style.order       = idx + 1
          pad.style.msFlexOrder = idx + 1
          container.appendChild(pad)
        }
      })
    }
    /**
      * Resets and lays out elements
    */
    layout() {
      this.__reset()
      // this.__setOrders()
      this.__populateHeights()
      this.__setLayout()
      this.__pad()
    }
  }

  window.myMasonry = new Masonry(document.querySelector(`.${CLASSES.MASONRY}`))
  /**
    * To make responsive, onResize layout again
    * NOTE:: For better performance, please debounce this!
  */
  window.addEventListener('resize', () => myMasonry.layout())
})()

              
            
!
999px

Console