<div class="popups-cont">
  <div class="popups-cont__overlay"></div>
  <div class="popup">
    <div class="popup__pieces"></div>
    <div class="popup__content">
      <div class="popup__close"></div>
      <h3 class="popup__heading">Shattered Popup</h3>
      <p class="popup__text">Works with any popup width and height (auto). Whole animation and triangulation made purely with css (SCSS). Javascript used only to trigger classes (I could do it css only with checkbox, but I'm tired of this crap). Requires browser with clip-path: polygon support.</p>
    </div>
  </div>
</div>

<button type="button" class="popup-btn">Show Popup</button>
*, *:before, *:after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  overflow: hidden;
  font-family: Helvetica, Arial, sans-serif;
}

$pieceAT: 0.7s;
$contentFadeAT: 0.2s;

@function map-set($map, $key, $value) {
  $new: ($key: $value);
  @return map-merge($map, $new);
}

@mixin shatteredPopup($inRow: 6, $inCol: 6) {
  $pieces: $inRow * $inCol;
  $heights: ();
  $hTotal: 100;

  @for $i from 1 through $inCol {
    $h: 100 / $inCol;
    $hVal: $h * 0.7 + floor(random($h*0.6));

    @if $i == $inCol {
      $hVal: $hTotal;
    }

    $hTotal: $hTotal - $hVal;
    $heights: map-set($heights, $i, $hVal);
    $widths: ();

    $wTotal: 100;

    @for $j from 1 through $inRow {
      $w: 100 / $inRow;
      $wVal: $w * 0.7 + floor(random($w*0.6));

      @if $j == $inRow {
        $wVal: $wTotal;
      }

      $wTotal: $wTotal - $wVal;
      $widths: map-set($widths, $j, $wVal);
    }

    $heights: map-set($heights, 'widths-#{$i}', $widths);
  }

  $k: 4;

  @for $i from 1 through $pieces {
    .popup__piece:nth-child(#{$i}) {
      $indexH: ceil($i / $inCol);
      $pieceH: map-get($heights, $indexH);
      height: percentage($pieceH / 100);

      $indexW: $i % $inRow + 1;
      $pieceW: map-get(map-get($heights, 'widths-#{$indexH}'), $indexW);

      width: percentage($pieceW / 100);

      $pieceX: percentage((30 + random(40)) / 100);

      @for $j from 1 through 3 {
        .popup__piece-inner:nth-child(#{$j}) {
          $x: (-60 + random(120)) * 1vw;
          $y: (-60 + random(120)) * 1vh;
          $z: (900 - random(1800)) * 1px;
          $rtX: 120deg + random(360deg);
          $rtY: 120deg + random(360deg);
          
          transform: translate3d($x, $y, $z) rotateX($rtX) rotateY($rtY);
          
          @if $j == 1 {
            clip-path: polygon(0 0, 0 100%, $pieceX 100%);
          }
          
          @if $j == 2 {
            clip-path: polygon(0 0, $pieceX 100%, 100% 0);
          }
          
          @if $j == 3 {
            clip-path: polygon(100% 0, 100% 100%, $pieceX 100%);
          }
        }
      }
    }
    
    &.s--closed .popup__piece:nth-child(#{$i}) {
      $rndPieceY: (100 + random(50)) * 1vh;
      
      transform: translate3d(0,$rndPieceY,0);
      
      @for $j from 1 through 3 {
        .popup__piece-inner:nth-child(#{$j}) {
          $x: (-80 + random(160)) * 1vw;
          $z: (900 - random(1800)) * 1px;
          $rtX: 120deg + random(360deg);
          $rtY: 120deg + random(360deg);
          
          transform: translate3d($x, 0, 0) rotateX($rtX) rotateY($rtY);
        }
      }
    }
  }

}

.popups-cont {
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: -10;
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100vh;
  perspective: 1000px;
  pointer-events: none;
  transition: z-index 0s $contentFadeAT/2 + $pieceAT;
  
  &.s--popup-active {
    z-index: 1000;
    pointer-events: auto;
    transition: z-index 0s 0s;
  }
  
  &__overlay {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    //background: rgba(0,0,0,0.2);
    opacity: 0;
    transition: opacity $pieceAT/2;
    
    .popups-cont.s--popup-active & {
      opacity: 1;
      transition: opacity $pieceAT/2 $pieceAT/2;
    }
  }
}

.popup {
  z-index: 2;
  position: relative;
  width: 500px;
  height: 500px;
  transform-style: preserve-3d;
  @include shatteredPopup;

  &__pieces {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
  }

  &__piece {
    float: left;
    position: relative;
    transform-style: preserve-3d;
    transition: transform 0s 0s;
    
    .popup.s--closed & {
      transition: transform $pieceAT $contentFadeAT/2 cubic-bezier(.69,-0.47,.97,1.04);
    }

    &:after {
      content: "";
      display: table;
      clear: both;
    }

    &-inner {
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      background: rgba(0,0,0,0.8);
      opacity: 0;
      transition: opacity $pieceAT/2 * 0.8 $pieceAT/2 + $contentFadeAT ease-in, transform $pieceAT $contentFadeAT/2 ease-out;

      .popup.s--active & {
        transition: opacity $pieceAT/2, transform $pieceAT ease-in-out;
        transform: translate3d(0,0,0) rotateX(0) rotateY(0) !important;
        opacity: 1;
      }
      
    }
  }

  &__content {
    position: relative;
    min-height: 500px;
    padding: 30px;
    background: #000;
    color: #fff;
    transition: opacity $contentFadeAT;
    opacity: 0;

    .popup.s--active & {
      transition-delay: $pieceAT - $contentFadeAT/2;
      opacity: 1;
    }
  }
  
  &__close {
    position: absolute;
    right: 30px;
    top: 30px;
    width: 30px;
    height: 30px;
    cursor: pointer;
    
    &:before,
    &:after {
      content: '';
      position: absolute;
      left: 0;
      top: 14px;
      width: 100%;
      height: 2px;
      background: #fff;
    }
    
    &:before {
      transform: rotate(45deg);
    }
    
    &:after {
      transform: rotate(-45deg);
    }
  }
  
  &__heading {
    margin-bottom: 20px;
    font-size: 30px;
    letter-spacing: 1px;
    text-transform: uppercase;
  }
  
  &__text {
    font-size: 18px;
    line-height: 1.5;
  }

}

.popup-btn {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 200px;
  height: 50px;
  margin-left: -100px;
  margin-top: -25px;
  background: transparent;
  outline: none;
  font-size: 20px;
  text-transform: uppercase;
  font-weight: bold;
  color: #000;
  border: 2px solid #000;
  transition: all 0.3s;
  cursor: pointer;
  
  &:hover {
    background-color: #000;
    color: #fff;
    letter-spacing: 2px;
  }
}
View Compiled
// js only creates and insert parts + click events adds classes

var numOfPieces = 6 * 6;

var frag = document.createDocumentFragment();

function insertInnerPieces($el, innerPieces) {
  for (var i = 0; i < innerPieces; i++) {
    var $inner = document.createElement('div');
    $inner.classList.add('popup__piece-inner');
    $el.appendChild($inner);
  }
};

for (var i = 1; i <= numOfPieces; i++) {
  var $piece = document.createElement('div');
  $piece.classList.add('popup__piece');
  
  insertInnerPieces($piece, 3);
  frag.appendChild($piece);
}

document.querySelector('.popup__pieces').appendChild(frag);

var $popupsCont = document.querySelector('.popups-cont');
var $popup = document.querySelector('.popup');
var popupAT = 900;

document.querySelector('.popup-btn').addEventListener('click', function() {
  $popupsCont.classList.add('s--popup-active');
  $popup.classList.add('s--active');
});

function closeHandler() {
  $popupsCont.classList.remove('s--popup-active');
  $popup.classList.remove('s--active');
  $popup.classList.add('s--closed');
  
  setTimeout(function() {
    $popup.classList.remove('s--closed');
  }, popupAT);
}

document.querySelector('.popup__close').addEventListener('click', closeHandler);

document.querySelector('.popups-cont__overlay').addEventListener('click', closeHandler);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.