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

              
                   <div class="container">

      <div id="score">
         <div class="timer">
            <button id="play-pause">
               <i id="play">Play</i>
               <i id="pause">Pause</i>
            </button>
            <label>Timer:</label>
            <span>00:00</span>
         </div>
         <div class="move-count" data-moves="0">
            <label>Moves:</label>
            <span>0</span>
         </div>
         <div class="score" data-score="0">
            <label>Score:</label>
            <span>0</span>
         </div>
      </div>

      <div id="table">

         <div class="upper-row">
            <div id="stock" class="stock pile" data-pile="stock">
               <i class="reload-icon" data-action="reload">
                  <span></span>
               </i>
               <ul></ul>
            </div>

            <div id="waste" class="waste pile" data-pile="waste">
               <ul></ul>
            </div>

            <ul id="fnd" class="fnd">
               <li id="spades" class="spades pile" data-pile="spades" data-empty="true"><ul></ul></li>
               <li id="hearts" class="hearts pile" data-pile="hearts" data-empty="true"><ul></ul></li>
               <li id="diamonds" class="diamonds pile" data-pile="diamonds" data-empty="true"><ul></ul></li>
               <li id="clubs" class="clubs pile" data-pile="clubs" data-empty="true"><ul></ul></li>
            </ul>
         </div>

         <div class="lower-row">
            <ul id="tab" class="tab">
               <li class="pile" data-pile="1"><ul></ul></li>
               <li class="pile" data-pile="2"><ul></ul></li>
               <li class="pile" data-pile="3"><ul></ul></li>
               <li class="pile" data-pile="4"><ul></ul></li>
               <li class="pile" data-pile="5"><ul></ul></li>
               <li class="pile" data-pile="6"><ul></ul></li>
               <li class="pile" data-pile="7"><ul></ul></li>
            </ul>
         </div>

      </div>

   </div><!-- /.container -->

   <button id="auto-win">Auto Win</button>

   <canvas id="confetti"></canvas>

   <ul class="template">
      <li data-rank="2">
         <div class="two {{suit}}">
            <div class="corner top">
               <span class="rank">2</span>
               <span class="suit"></span>
            </div>
            <span class="suit top_center"></span>
            <span class="suit bottom_center"></span>
            <div class="corner bottom">
               <span class="rank">2</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="3">
         <div class="three {{suit}}">
            <div class="corner top">
               <span class="rank">3</span>
               <span class="suit"></span>
            </div>
            <span class="suit top_center"></span>
            <span class="suit middle_center"></span>
            <span class="suit bottom_center"></span>
            <div class="corner bottom">
               <span class="rank">3</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="4">
         <div class="four {{suit}}">
            <div class="corner top">
               <span class="rank">4</span>
               <span class="suit"></span>
            </div>
            <span class="suit top_left"></span>
            <span class="suit top_right"></span>
            <span class="suit bottom_left"></span>
            <span class="suit bottom_right"></span>
            <div class="corner bottom">
               <span class="rank">4</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="5">
         <div class="five {{suit}}">
            <div class="corner top">
               <span class="rank">5</span>
               <span class="suit"></span>
            </div>
            <span class="suit top_left"></span>
            <span class="suit top_right"></span>
            <span class="suit middle_center"></span>
            <span class="suit bottom_left"></span>
            <span class="suit bottom_right"></span>
            <div class="corner bottom">
               <span class="rank">5</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="6">
         <div class="six {{suit}}">
            <div class="corner top">
               <span class="rank">6</span>
               <span class="suit"></span>
            </div>
            <span class="suit top_left"></span>
            <span class="suit top_right"></span>
            <span class="suit middle_left"></span>
            <span class="suit middle_right"></span>
            <span class="suit bottom_left"></span>
            <span class="suit bottom_right"></span>
            <div class="corner bottom">
               <span class="rank">6</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="7">
         <div class="seven {{suit}}">
            <div class="corner top">
               <span class="rank">7</span>
               <span class="suit"></span>
            </div>
            <span class="suit top_left"></span>
            <span class="suit top_right"></span>
            <span class="suit middle_left"></span>
            <span class="suit middle_top"></span>
            <span class="suit middle_right"></span>
            <span class="suit bottom_left"></span>
            <span class="suit bottom_right"></span>
            <div class="corner bottom">
               <span class="rank">7</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="8">
         <div class="eight {{suit}}">
            <div class="corner top">
               <span class="rank">8</span>
               <span class="suit"></span>
            </div>
            <span class="suit top_left"></span>
            <span class="suit top_right"></span>
            <span class="suit middle_left"></span>
            <span class="suit middle_top_center"></span>
            <span class="suit middle_right"></span>
            <span class="suit middle_bottom_center"></span>
            <span class="suit bottom_left"></span>
            <span class="suit bottom_right"></span>
            <div class="corner bottom">
               <span class="rank">8</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="9">
         <div class="nine {{suit}}">
            <div class="corner top">
               <span class="rank">9</span>
               <span class="suit"></span>
            </div>
            <span class="suit top_left"></span>
            <span class="suit top_right"></span>
            <span class="suit middle_top_left"></span>
            <span class="suit middle_center"></span>
            <span class="suit middle_top_right"></span>
            <span class="suit bottom_left"></span>
            <span class="suit bottom_right"></span>
            <span class="suit middle_bottom_left"></span>
            <span class="suit middle_bottom_right"></span>
            <div class="corner bottom">
               <span class="rank">9</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="10">
         <div class="ten {{suit}}">
            <div class="corner top">
               <span class="rank">10</span>
               <span class="suit"></span>
            </div>
            <span class="suit top_left"></span>
            <span class="suit top_right"></span>
            <span class="suit middle_top_left"></span>
            <span class="suit middle_top_center"></span>
            <span class="suit middle_top_right"></span>
            <span class="suit bottom_left"></span>
            <span class="suit bottom_right"></span>
            <span class="suit middle_bottom_center"></span>
            <span class="suit middle_bottom_left"></span>
            <span class="suit middle_bottom_right"></span>
            <div class="corner bottom">
               <span class="rank">10</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="J">
         <div class="jack {{suit}}">
            <div class="corner top">
               <span class="rank">J</span>
               <span class="suit"></span>
            </div>
            <span class="face middle_center"></span>
            <div class="corner bottom">
               <span class="rank">J</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="Q">
         <div class="queen {{suit}}">
            <div class="corner top">
               <span class="rank">Q</span>
               <span class="suit"></span>
            </div>
            <span class="face middle_center"></span>
            <div class="corner bottom">
               <span class="rank">Q</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="K">
         <div class="king {{suit}}">
            <div class="corner top">
               <span class="rank">K</span>
               <span class="suit"></span>
            </div>
            <span class="face middle_center"></span>
            <div class="corner bottom">
               <span class="rank">K</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
      <li data-rank="A">
         <div class="ace {{suit}}">
            <div class="corner top">
               <span class="rank">A</span>
               <span class="suit"></span>
            </div>
            <span class="suit middle_center"></span>
            <div class="corner bottom">
               <span class="rank">A</span>
               <span class="suit"></span>
            </div>
         </div>
      </li>
   </ul><!-- /.templates -->
              
            
!

CSS

              
                /* Thanks to
   @donpark on GitHub Scalable CSS Playing Cards
   https://donpark.github.io/scalable-css-playing-cards/ */

@font-face {
  font-family: 'Suit-Regular';
  src: url("https://bfa.github.io/solitaire-js/fonts/suit-regular.eot");
  src: url("https://bfa.github.io/solitaire-js/fonts/suit-regular.eot?#iefix") format('embedded-opentype'), url("https://bfa.github.io/solitaire-js/fonts/suit-regular.woff") format('woff'), url("https://bfa.github.io/solitaire-js/fonts/suit-regular.ttf") format('truetype'), url("https://bfa.github.io/solitaire-js/fonts/suit-regular.svg#suit-regular") format('svg');
  font-weight: normal;
  font-style: normal;
}
html, body {
   width: 100%;
   height: 100%;
}
body {
   position: relative;
   color: #f3f5f7;
   background: #0e8b44 url('https://bfa.github.io/solitaire-js/img/green_felt.jpg');
   background-size: cover;
   background-position: center;
}
.navbar {
   background: rgba(8,8,8,.75);
}
.navbar a {
   color: #fff;
}
.navbar li[data-active="false"] {
   opacity: 0.5;
   pointer-events: none;
}
.navbar li[data-active="true"] {
   opacity: 1;
   pointer-events: auto;
}
.template {
   display: none;
}
#score {
   position: relative;
   background: rgba(0,0,0,.15);
   margin-top: 1.5em;
   margin-bottom: 0.5em;
   padding: 1em;
   line-height: 1;
}
#score > * {
   display: inline-block;
   min-width: 100px;
}
#score > *:last-child {
   float: right;
   text-align: right;
}
#score label {
   margin-bottom: 0;
}
#score .timer button {
   display: none;
   position: absolute;
   -webkit-appearance: none;
   background: transparent;
   outline: none;
   border: 0;
   padding: 0;
   top: 0;
   left: 0;
   width: 3em;
   height: 100%;
}
#score .timer button i {
   position: absolute;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
   font-size: 0;
}
#score .timer button i::before {
   position: absolute;
   top: 50%;
   left: 50%;
   width: 0.75em;
   height: 1em;
   margin-left: -0.375em;
   margin-top: -0.5em;
   font-size: 1.5rem;
   content: none;
}
#score .timer button i#play::before {
   border-top: 8px solid transparent;
   border-left: 12px solid #fff;
   border-bottom: 8px solid transparent;
}
#score .timer button i#pause::before {
   border-right: 4px solid #fff;
   border-left: 4px solid #fff;
}
[data-gameplay="active"] #score .timer button,
[data-gameplay="paused"] #score .timer button {
   display: inline-block;
}
[data-gameplay="active"] #score .timer button i#pause::before,
[data-gameplay="paused"] #score .timer button i#play::before {
   content: '';
}
[data-gameplay="active"] #score,
[data-gameplay="paused"] #score {
   padding-left: 3em;
}
#auto-win {
   display: none;
   -webkit-appearance: none;
   outline: none;
   border: 0;
   position: absolute;
   z-index: 1;
   bottom: 0;
   width: 100%;
   background: rgba(0,0,0,.8);
   padding: 1em;
   line-height: 1;
}
#table {
   opacity: 0;
   width: 100%;
   padding: 15px 0;
}
#table > div {
   position: relative;
   margin-bottom: 10px;
}
#table > div:last-child {
   margin-bottom: 0;
}
#table > div::after {
   content: '';
   display: table-cell;
   clear: both;
}
#table ul {
   display: inline-block;
   padding: 0;
}
#table > div > ul {
   position: absolute;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
}
.pile {
   display: block;
   position: relative;
   float: left;
   width: 13%;
   margin-right: 1.5%;
   margin-bottom: 10px;
   padding: 5px;
}
.pile:last-child {
   margin-right: 0;
}
.pile::after {
   content: '';
   position: absolute;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
   border: 2px dotted rgba(0,0,0,.25);
   border-radius: 5px;
}
.card {
   display: block;
   position: absolute;
   top: 0;
   left: 0;
   background-color: #ddd;
   background-image: url('https://bfa.github.io/solitaire-js/img/card_back_bg.png');
   background-size: contain;
   background-repeat: no-repeat;
   width: 100%;
   height: 100%;
   font-family: 'Suit-Regular', sans-serif;
   font-size: 0.6vw;
   border-radius: 5px;
   z-index: -1;
}
.card * {
   pointer-events: none;
}
@media screen and (min-width: 768px) {
   .card {
      font-size: 0.325em;
   }
   .pile::after, .card {
      border-radius: 10px;
   }
}
@media screen and (min-width: 992px) {
   .card {
      font-size: 0.425em;
   }
}
@media screen and (min-width: 1200px) {
   .card {
      font-size: 0.525em;
   }
}
.card > div {
   display: none;
}
.card.up {
   background-image: url('https://bfa.github.io/solitaire-js/img/card_face_bg.png');
   background-repeat: repeat;
   color: #111;
}
.card.up > div {
   display: block;
}
.card::before {
   content: '';
   display: block;
   position: absolute;
   width: 100%;
   height: 100%;
   border-radius: 5px;
}
.card[data-selected="true"]::before {
   box-shadow: 0 0 6px 3px #FCDB1A;
}
@media screen and (min-width: 768px) {
   .card::before {
      border-radius: 10px;
   }
}
.card .suit {
   font-size: 5.8em;
   font-weight: normal;
   width: 0.6896551724137931em;
   height: 0.786206896551724em;
   line-height: 0.786206896551724em;
   position: absolute;
   text-align: center;
}
@media screen and (max-width: 767px) {
   .card > div > .suit {
      display: none;
   }
}
.card .heart,
.card .diamond {
   color: #cc0000;
}
.card .spade .suit::before { content: '♠' }
.card .heart .suit::before { content: '♥' }
.card .diamond .suit::before { content: '♦' }
.card .club .suit::before { content: '♣' }
.card .corner {
   line-height: 1;
   position: absolute;
   text-align: center;
}
.card .corner span {
   display: block;
   font-size: 9em;
   font-weight: bold;
   width: 1em;
   text-align: center;
}
@media screen and (min-width: 768px) {
   .card .corner span {
      font-size: 3em;
   }
}
.card .corner .suit {
   margin-top: 0;
   margin-left: 0;
}
.card .corner.top {
   left: 0.64em;
   top: 0.96em;
}
.card .corner.bottom {
   bottom: 0.96em;
   right: 0.64em;
   -ms-transform: rotate(180deg);
   -moz-transform: rotate(180deg);
   -webkit-transform: rotate(180deg);
   transform: rotate(180deg);
}
.card .ace span.suit.middle_center {
   font-size: 10.24em;
   left: 50%;
   top: 50%;
   margin-top: -0.5em;
   margin-left: -0.35em;
}
.card .face::before {
   display: none;
   content: '';
   position: absolute;
   top: 15.25%;
   left: 19%;
   width: 62%;
   height: 70.5%;
   background-repeat: no-repeat;
   background-size: contain;
}
@media screen and (min-width: 768px) {
   .card .face::before {
      display: block;
   }
}
.card .spade.king .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-king-spade.png');
}
.card .spade.queen .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-queen-spade.png');
}
.card .spade.jack .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-jack-spade.png');
}
.card .heart.king .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-king-heart.png');
}
.card .heart.queen .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-queen-heart.png');
}
.card .heart.jack .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-jack-heart.png');
}
.card .diamond.king .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-king-diamond.png');
}
.card .diamond.queen .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-queen-diamond.png');
}
.card .diamond.jack .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-jack-diamond.png');
}
.card .club.king .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-king-club.png');
}
.card .club.queen .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-queen-club.png');
}
.card .club.jack .face::before {
   background-image: url('https://bfa.github.io/solitaire-js/img/face-jack-club.png');
}
.card .suit.top_center {
   left: 50%;
   top: 0;
   margin-left: -0.35em;
   margin-top: 0.65em;
}
.card .suit.top_left {
   left: 0;
   top: 0;
   margin-left: 0.65em;
   margin-top: 0.65em;
}
.card .suit.top_right {
   right: 0;
   top: 0;
   margin-right: 0.65em;
   margin-top: 0.65em;
}
.card .suit.middle_center {
   left: 50%;
   top: 50%;
   margin-left: -0.35em;
   margin-top: -0.5em;
}
.card .suit.middle_top {
   left: 50%;
   top: 0;
   margin-left: -0.35em;
   margin-top: 1.25em;
}
.card .suit.middle_bottom {
   bottom: 0;
   left: 50%;
   margin-bottom: 0.65em;
   margin-left: -0.35em;
   -ms-transform: rotate(180deg);
   -moz-transform: rotate(180deg);
   -webkit-transform: rotate(180deg);
   transform: rotate(180deg);
}
.card .suit.middle_left {
   left: 0;
   top: 50%;
   margin-left: 0.65em;
   margin-top: -0.5em;
}
.card .suit.middle_right {
   right: 0;
   top: 50%;
   margin-right: 0.65em;
   margin-top: -0.5em;
}
.card .suit.middle_top_center {
   left: 50%;
   top: 50%;
   margin-left: -0.35em;
   margin-top: -1.35em;
}
.card .suit.middle_top_left {
   left: 0;
   top: 50%;
   margin-left: 0.65em;
   margin-top: -1em;
}
.card .suit.middle_top_right {
   right: 0;
   top: 50%;
   margin-right: 0.65em;
   margin-top: -1em;
}
.card .suit.middle_bottom_left {
   bottom: 50%;
   left: 0;
   margin-left: 0.65em;
   margin-bottom: -1em;
   -ms-transform: rotate(180deg);
   -moz-transform: rotate(180deg);
   -webkit-transform: rotate(180deg);
   transform: rotate(180deg);
}
.card .suit.middle_bottom_right {
   bottom: 50%;
   right: 0;
   margin-bottom: -1em;
   margin-right: 0.65em;
   -ms-transform: rotate(180deg);
   -moz-transform: rotate(180deg);
   -webkit-transform: rotate(180deg);
   transform: rotate(180deg);
}
.card .suit.middle_bottom_center {
   bottom: 50%;
   left: 50%;
   margin-bottom: -1.35em;
   margin-left: -0.35em;
   -ms-transform: rotate(180deg);
   -moz-transform: rotate(180deg);
   -webkit-transform: rotate(180deg);
   transform: rotate(180deg);
}
.card .suit.bottom_center {
   bottom: 0;
   left: 50%;
   margin-bottom: 0.65em;
   margin-left: -0.35em;
   -ms-transform: rotate(180deg);
   -moz-transform: rotate(180deg);
   -webkit-transform: rotate(180deg);
   transform: rotate(180deg);
}
.card .suit.bottom_left {
   bottom: 0;
   left: 0;
   margin-bottom: 0.65em;
   margin-left: 0.65em;
   -ms-transform: rotate(180deg);
   -moz-transform: rotate(180deg);
   -webkit-transform: rotate(180deg);
   transform: rotate(180deg);
}
.card .suit.bottom_right {
   bottom: 0;
   right: 0;
   margin-bottom: 0.65em;
   margin-right: 0.65em;
   -ms-transform: rotate(180deg);
   -moz-transform: rotate(180deg);
   -webkit-transform: rotate(180deg);
   transform: rotate(180deg);
}
.card:nth-child(1),
.card:nth-child(2),
.card:nth-child(3),
.card:nth-child(4),
.card:nth-child(5) {
   box-shadow: 0 0 5px rgba(0,0,0,.25), 0 2px 1px rgba(0,0,0,.5);
   z-index: 1;
}
.card:nth-child(1) { top: 0; z-index: 5; }
.card:nth-child(2) { top: 2px; z-index: 4; }
.card:nth-child(3) { top: 4px; z-index: 3; }
.card:nth-child(4) { top: 6px; z-index: 2; }
.card:nth-child(5) { top: 8px; z-index: 1; }

/* stock */
   .stock {
      z-index: 1;
   }
   .stock .reload-icon {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      font-size: 3vw;
      font-weight: bold;
      line-height: 1;
      opacity: 0.25;
      z-index: 1;
   }
   @media screen and (max-width: 767px) {
      .stock .reload-icon {
         font-size: 5vw;
      }
   }
   .stock .reload-icon span {
      display: block;
      position: absolute;
      top: 50%;
      left: 0;
      width: 100%;
      text-align: center;
      padding: 0.25em;
      margin-top: -0.75em;
      pointer-events: none;
   }
   .stock .reload-icon span::before,
   .stock .reload-icon span::after {
      content: '';
      display: inline-block;
      border-style: solid;
   }
   .stock .reload-icon span::before {
      width: 1.25em;
      height: 1.25em;
      border-color: transparent black black black;
      border-radius: 50%;
      border-width: .125em;
      -webkit-transform: rotate(45deg);
      transform: rotate(45deg);
   }
   .stock .reload-icon span::after {
      position: absolute;
      top: 0;
      left: 50%;
      width: 0;
      height: 0;
      border-color: transparent transparent transparent black;
      border-width: .3125em 0 .3125em .5em;
   }


/* waste */
   .waste {
      z-index: 1;
   }

/* foundation */
   .fnd .pile {
      left: 43.5%;
   }
   .fnd .pile::before {
      content '';
      position: absolute;
      top: 50%;
      left: 0;
      width: 100%;
      color: #000;
      font-family: 'Suit-Regular', sans-serif;
      font-size: 10vw;
      margin-top: -0.6em;
      line-height: 1;
      text-align: center;
      opacity: 0.25;
   }
   @media screen and (min-width: 768px) {
      .fnd .pile::before {
         font-size: 6em;
      }
   }
   @media screen and (min-width: 992px) {
      .fnd .pile::before {
         font-size: 7em;
      }
   }
   @media screen and (min-width: 1200px) {
      .fnd .pile::before {
         font-size: 8em;
      }
   }
   .fnd .pile.spades::before { content: '♠' }
   .fnd .pile.hearts::before { content: '♥' }
   .fnd .pile.diamonds::before { content: '♦' }
   .fnd .pile.clubs::before { content: '♣' }

/* tableau */
   .tab .card {
      box-shadow: 0 0 5px rgba(0,0,0,.5);
      z-index: 1;
      margin-bottom: 10em;
   }
   /* face up */
   .tab .card:nth-child(2) { top: 6em; left: 0; }
   .tab .card:nth-child(3) { top: 12em; left: 0; }
   .tab .card:nth-child(4) { top: 18em; left: 0; }
   .tab .card:nth-child(5) { top: 24em; left: 0; }
   .tab .card:nth-child(6) { top: 30em; left: 0; }
   .tab .card:nth-child(7) { top: 36em;  left: 0; }
   .tab .card:nth-child(8) { top: 42em;  left: 0; }
   .tab .card:nth-child(9) { top: 48em;  left: 0; }
   .tab .card:nth-child(10) { top: 54em;  left: 0; }
   .tab .card:nth-child(11) { top: 60em;  left: 0; }
   .tab .card:nth-child(12) { top: 66em;  left: 0; }
   .tab .card:nth-child(13) { top: 72em;  left: 0; }
   .tab .card:nth-child(14) { top: 78em;  left: 0; }
   .tab .card:nth-child(15) { top: 84em;  left: 0; }
   .tab .card:nth-child(16) { top: 90em;  left: 0; }
   .tab .card:nth-child(17) { top: 96em;  left: 0; }
   .tab .card:nth-child(18) { top: 102em;  left: 0; }
   .tab .card:nth-child(19) { top: 108em;  left: 0; }
   .tab .card:nth-child(20) { top: 114em;  left: 0; }
   .tab .card:nth-child(21) { top: 120em;  left: 0; }
   /* face down */
   .tab .pile[data-unplayed='1'] .card:nth-child(2),
   .tab .pile[data-unplayed='2'] .card:nth-child(2),
   .tab .pile[data-unplayed='3'] .card:nth-child(2),
   .tab .pile[data-unplayed='4'] .card:nth-child(2),
   .tab .pile[data-unplayed='5'] .card:nth-child(2),
   .tab .pile[data-unplayed='6'] .card:nth-child(2) { top: 3em; }
   .tab .pile[data-unplayed='2'] .card:nth-child(3),
   .tab .pile[data-unplayed='3'] .card:nth-child(3),
   .tab .pile[data-unplayed='4'] .card:nth-child(3),
   .tab .pile[data-unplayed='5'] .card:nth-child(3),
   .tab .pile[data-unplayed='6'] .card:nth-child(3) { top: 6em; }
   .tab .pile[data-unplayed='3'] .card:nth-child(4),
   .tab .pile[data-unplayed='4'] .card:nth-child(4),
   .tab .pile[data-unplayed='5'] .card:nth-child(4),
   .tab .pile[data-unplayed='6'] .card:nth-child(4) { top: 9em; }
   .tab .pile[data-unplayed='4'] .card:nth-child(5),
   .tab .pile[data-unplayed='5'] .card:nth-child(5),
   .tab .pile[data-unplayed='6'] .card:nth-child(5) { top: 12em; }
   .tab .pile[data-unplayed='5'] .card:nth-child(6),
   .tab .pile[data-unplayed='6'] .card:nth-child(6) { top: 15em; }
   .tab .pile[data-unplayed='6'] .card:nth-child(7) { top: 18em; }
   /* piles with odd # of face down cards */
   .tab .pile[data-unplayed='1'] .card:nth-child(3) { top: 9em; left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(4),
   .tab .pile[data-unplayed='3'] .card:nth-child(5) { top: 15em; left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(5),
   .tab .pile[data-unplayed='3'] .card:nth-child(6),
   .tab .pile[data-unplayed='5'] .card:nth-child(7) { top: 21em; left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(6),
   .tab .pile[data-unplayed='3'] .card:nth-child(7),
   .tab .pile[data-unplayed='5'] .card:nth-child(8) { top: 27em; left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(7),
   .tab .pile[data-unplayed='3'] .card:nth-child(8),
   .tab .pile[data-unplayed='5'] .card:nth-child(9) { top: 33em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(8),
   .tab .pile[data-unplayed='3'] .card:nth-child(9),
   .tab .pile[data-unplayed='5'] .card:nth-child(10) { top: 39em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(9),
   .tab .pile[data-unplayed='3'] .card:nth-child(10),
   .tab .pile[data-unplayed='5'] .card:nth-child(11) { top: 45em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(10),
   .tab .pile[data-unplayed='3'] .card:nth-child(11),
   .tab .pile[data-unplayed='5'] .card:nth-child(12) { top: 51em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(11),
   .tab .pile[data-unplayed='3'] .card:nth-child(12),
   .tab .pile[data-unplayed='5'] .card:nth-child(13) { top: 57em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(12),
   .tab .pile[data-unplayed='3'] .card:nth-child(13),
   .tab .pile[data-unplayed='5'] .card:nth-child(14) { top: 63em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(13),
   .tab .pile[data-unplayed='3'] .card:nth-child(14),
   .tab .pile[data-unplayed='5'] .card:nth-child(15) { top: 69em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(14),
   .tab .pile[data-unplayed='3'] .card:nth-child(15),
   .tab .pile[data-unplayed='5'] .card:nth-child(16) { top: 75em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(15),
   .tab .pile[data-unplayed='3'] .card:nth-child(16),
   .tab .pile[data-unplayed='5'] .card:nth-child(17) { top: 81em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(16),
   .tab .pile[data-unplayed='3'] .card:nth-child(17),
   .tab .pile[data-unplayed='5'] .card:nth-child(18) { top: 87em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(17),
   .tab .pile[data-unplayed='3'] .card:nth-child(18),
   .tab .pile[data-unplayed='5'] .card:nth-child(19) { top: 93em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(18),
   .tab .pile[data-unplayed='3'] .card:nth-child(19),
   .tab .pile[data-unplayed='5'] .card:nth-child(20) { top: 99em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(19),
   .tab .pile[data-unplayed='3'] .card:nth-child(20),
   .tab .pile[data-unplayed='5'] .card:nth-child(21) { top: 105em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(20),
   .tab .pile[data-unplayed='3'] .card:nth-child(21) { top: 111em;  left: 0; }
   .tab .pile[data-unplayed='1'] .card:nth-child(21) { top: 117em;  left: 0; }
   /* piles with even # of face down cards */
   .tab .pile[data-unplayed='2'] .card:nth-child(4) { top: 12em; left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(5),
   .tab .pile[data-unplayed='4'] .card:nth-child(6) { top: 18em; left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(6),
   .tab .pile[data-unplayed='4'] .card:nth-child(7),
   .tab .pile[data-unplayed='6'] .card:nth-child(8) { top: 24em; left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(7),
   .tab .pile[data-unplayed='4'] .card:nth-child(8),
   .tab .pile[data-unplayed='6'] .card:nth-child(9) { top: 30em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(8),
   .tab .pile[data-unplayed='4'] .card:nth-child(9),
   .tab .pile[data-unplayed='6'] .card:nth-child(10) { top: 36em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(9),
   .tab .pile[data-unplayed='4'] .card:nth-child(10),
   .tab .pile[data-unplayed='6'] .card:nth-child(11) { top: 42em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(10),
   .tab .pile[data-unplayed='4'] .card:nth-child(11),
   .tab .pile[data-unplayed='6'] .card:nth-child(12) { top: 48em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(11),
   .tab .pile[data-unplayed='4'] .card:nth-child(12),
   .tab .pile[data-unplayed='6'] .card:nth-child(13) { top: 54em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(12),
   .tab .pile[data-unplayed='4'] .card:nth-child(13),
   .tab .pile[data-unplayed='6'] .card:nth-child(14) { top: 60em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(13),
   .tab .pile[data-unplayed='4'] .card:nth-child(14),
   .tab .pile[data-unplayed='6'] .card:nth-child(15) { top: 66em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(14),
   .tab .pile[data-unplayed='4'] .card:nth-child(15),
   .tab .pile[data-unplayed='6'] .card:nth-child(16) { top: 72em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(15),
   .tab .pile[data-unplayed='4'] .card:nth-child(16),
   .tab .pile[data-unplayed='6'] .card:nth-child(17) { top: 78em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(16),
   .tab .pile[data-unplayed='4'] .card:nth-child(17),
   .tab .pile[data-unplayed='6'] .card:nth-child(18) { top: 84em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(17),
   .tab .pile[data-unplayed='4'] .card:nth-child(18),
   .tab .pile[data-unplayed='6'] .card:nth-child(19) { top: 90em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(18),
   .tab .pile[data-unplayed='4'] .card:nth-child(19),
   .tab .pile[data-unplayed='6'] .card:nth-child(20) { top: 96em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(19),
   .tab .pile[data-unplayed='4'] .card:nth-child(20),
   .tab .pile[data-unplayed='6'] .card:nth-child(21) { top: 102em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(20),
   .tab .pile[data-unplayed='4'] .card:nth-child(21) { top: 108em;  left: 0; }
   .tab .pile[data-unplayed='2'] .card:nth-child(21) { top: 114em;  left: 0; }
   /* Confetti */
   #confetti {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 10000;
      pointer-events: none;
      opacity: 0;
   }
   /* Disable Grammarly */
   grammarly-card {
       display: none;
   }
              
            
!

JS

              
                /* ### TODO ###
- Refactor code :) Always

Optional Features:
- HTML Drag & Drop API
- Limit How Many Times Stock Can Be Reloaded (3x)
- 3 Card Draw
- High score
- Options panel for user
- Sound Fx

*/

// 0. DECLARE VARS

   // document
   var d = document;

   // build deck
   var deck = [];

   // build suits
   var suits = [];
   suits['spades'] = [
      // spades
      ['A','spade'],
      ['2','spade'],
      ['3','spade'],
      ['4','spade'],
      ['5','spade'],
      ['6','spade'],
      ['7','spade'],
      ['8','spade'],
      ['9','spade'],
      ['10','spade'],
      ['J','spade'],
      ['Q','spade'],
      ['K','spade']
   ];
   suits['hearts'] = [
      // hearts
      ['A','heart'],
      ['2','heart'],
      ['3','heart'],
      ['4','heart'],
      ['5','heart'],
      ['6','heart'],
      ['7','heart'],
      ['8','heart'],
      ['9','heart'],
      ['10','heart'],
      ['J','heart'],
      ['Q','heart'],
      ['K','heart']
   ];
   suits['diamonds'] = [
      // diamonds
      ['A','diamond'],
      ['2','diamond'],
      ['3','diamond'],
      ['4','diamond'],
      ['5','diamond'],
      ['6','diamond'],
      ['7','diamond'],
      ['8','diamond'],
      ['9','diamond'],
      ['10','diamond'],
      ['J','diamond'],
      ['Q','diamond'],
      ['K','diamond']
   ];
   suits['clubs'] = [
      // clubs
      ['A','club'],
      ['2','club'],
      ['3','club'],
      ['4','club'],
      ['5','club'],
      ['6','club'],
      ['7','club'],
      ['8','club'],
      ['9','club'],
      ['10','club'],
      ['J','club'],
      ['Q','club'],
      ['K','club']
   ];

   // build stock pile
   var s = [];

   // build waste pile
   var w = [];

   // build foundations
   var spades = [];
   var hearts = [];
   var diamonds = [];
   var clubs = [];

   // build tableau
   var t = [];
   t[1] = t[2] = t[3] = t[4] = t[5] = t[6] = t[7] = [];

   // build table
   var table = [];
   table['stock'] = s;
   table['waste'] = w;
   table['spades'] = spades;
   table['hearts'] = hearts;
   table['diamonds'] = diamonds;
   table['clubs'] = clubs;
   table['tab'] = t;

   // initial face up cards
   var playedCards =
   '#waste .card,' +
   '#fnd .card,' +
   '#tab .card:last-child';

   // cache selectors
   var $timer = d.querySelector('#score .timer');
   var $timerSpan = d.querySelector('#score .timer span');
   var $moveCount = d.querySelector('#score .move-count');
   var $moveCountSpan = d.querySelector('#score .move-count span');
   var $score = d.querySelector('#score .score');
   var $scoreSpan = d.querySelector('#score .score span');
   var $playPause = d.querySelector('#play-pause');
   var $table = d.querySelector('#table');
   var $upper = d.querySelector('#table .upper-row');
   var $lower = d.querySelector('#table .lower-row');
   var $stock = d.querySelector('#stock');
   var $waste = d.querySelector('#waste');
   var $fnd = d.querySelector('#fnd');
   var $tab = d.querySelector('#tab');
   var $autoWin = d.querySelector('#auto-win');

   // other global vars
   var clock = 0;
   var time = 0;
   var moves = 0;
   var score = 0;
   var bonus = 0;
   var lastEventTime = 0;
   var unplayedTabCards = [];

// 1. CREATE DECK
   deck = create(deck, suits);

// 2. SHUFFLE DECK
   deck = shuffle(deck);

// 3. DEAL DECK
   table = deal(deck, table);

// 4. RENDER TABLE
   render(table, playedCards);

// 5. START GAMEPLAY
   play(table);

// ### EVENT HANDLERS ###
   window.onresize = function(event) {
      sizeCards();
   };

// ### FUNCTIONS ###

   // create deck
      function create(deck, suits) {
         console.log('Creating Deck...');
         // loop through each suit
         for (var suit in suits) {
            suit = suits[suit];
            // loop through each card in suit
            for (var card in suit) {
               card = suit[card];
               deck.push(card); // push card to deck
            }
         }
         return deck;
      }

   // shuffle deck
      function shuffle(deck) {
         console.log('Shuffling Deck...');
         // declare vars
         var i = deck.length, temp, rand;
         // while there remain elements to shuffle
         while (0 !== i) {
            // pick a remaining element
            rand = Math.floor(Math.random() * i);
            i--;
            // and swap it with the current element
            temp = deck[i];
            deck[i] = deck[rand];
            deck[rand] = temp;
         }
         return deck;
      }

   // deal deck
      function deal(deck, table) {
         console.log('Dealing Deck...');
         // move all cards to stock
         table['stock'] = deck;
         // build tableau
            var tabs = table['tab'];
            // loop through 7 tableau rows
            for (var row = 1; row <= 7; row++) {
               // loop through 7 piles in row
               for (var pile = row; pile <= 7; pile++) {
                  // build blank pile on first row
                  if (row === 1) tabs[pile] = [];
                  // deal card to pile
                  move(table['stock'], tabs[pile], false);
               }
            }
         return table;
      }

   // move card
      function move(source, dest, pop, selectedCards = 1) {
         if (pop !== true) {
            var card = source.shift(); // take card from bottom
            dest.push(card); // push card to destination pile
         } else {
            while (selectedCards) {
               // take card from the top of selection
               var card = source[source.length - selectedCards];
               // remove it from the selected pile
               source.splice(source.length - selectedCards, 1);
               // put it in the destination pile
               dest.push(card);
               // decrement
               selectedCards--; 
            }
         }
         return;
      }

   // render table
      function render(table, playedCards) {
         console.log('Rendering Table...');

         // check for played cards
         playedCards = checkForPlayedCards(playedCards);

         // check for empty piles
         emptyPiles = checkForEmptyPiles(table);

         // update stock pile
         update(table['stock'], '#stock ul', playedCards, true);
         // update waste pile
         update(table['waste'], '#waste ul', playedCards);
         // update spades pile
         update(table['spades'], '#spades ul', playedCards);
         // update hearts pile
         update(table['hearts'], '#hearts ul', playedCards);
         // update diamonds pile
         update(table['diamonds'], '#diamonds ul', playedCards);
         // update clubs pile
         update(table['clubs'], '#clubs ul', playedCards);
         // update tableau
         var tabs = table['tab'];
         // loop through tableau piles
         for (var i = 1; i <= 7; i++) {
            // update tableau pile
            update(tabs[i], '#tab li:nth-child('+i+') ul', playedCards, true);
         }

         // get unplayed tab cards
         unplayedTabCards = getUnplayedTabCards();

         // size cards
         sizeCards();

         // show table
         $table.style.opacity = '100';

         console.log('Table Rendered:', table);
         return;
      }

   // update piles
      function update(pile, selector, playedCards, append) {
         var e = d.querySelector(selector);
         var children = e.children; // get children
         var grandParent = e.parentElement.parentElement; // get grand parent
         // reset pile
         e.innerHTML = '';
         // loop through cards in pile
         for (var card in pile) {
            card = pile[card];
            // get html template for card
            var html = getTemplate(card);
            // create card in pile
            createCard(card, selector, html, append);
         }
         // turn cards face up
         flipCards(playedCards, 'up');
         // count played cards
         var played = countPlayedCards(children);
         e.parentElement.dataset.played = played;
         // count all played cards for #tab and #fnd piles
         if ( grandParent.id === 'tab' || grandParent.id === 'fnd' ) {
            var playedAll = parseInt(grandParent.dataset.played);
            if ( isNaN(playedAll) ) playedAll = 0;
            grandParent.dataset.played = playedAll + played;
         }
         // count unplayed cards
         var unplayed = countUnplayedCards(children);
         e.parentElement.dataset.unplayed = unplayed;
         // count all unplayed cards for #tab and #fnd piles
         if ( grandParent.id === 'tab' || grandParent.id === 'fnd' ) {
            var unplayedAll = parseInt(grandParent.dataset.unplayed);
            if ( isNaN(unplayedAll) ) unplayedAll = 0;
            grandParent.dataset.unplayed = unplayedAll + unplayed;
         }
         return pile;
      }

   // get html template for card
      function getTemplate(card) {
         var r = card[0]; // get rank
         var s = card[1]; // get suit
         // get html template
         var html = d.querySelector('.template li[data-rank="'+r+'"]').innerHTML;
         // search and replace suit variable
         html = html.replace('{{suit}}', s);
         return html;
      }

   // create card in pile
      function createCard(card, selector, html, append) {
         var r = card[0]; // get rank
         var s = card[1]; // get suit
         // get pile based on selector
         if ( selector.includes('#stock') ) var p = 'stock';
         if ( selector.includes('#waste') ) var p = 'waste';
         if ( selector.includes('#spades') ) var p = 'spades';
         if ( selector.includes('#hearts') ) var p = 'hearts';
         if ( selector.includes('#diamonds') ) var p = 'diamonds';
         if ( selector.includes('#clubs') ) var p = 'clubs';
         if ( selector.includes('#tab') ) var p = 'tab';
         var e = d.createElement('li'); // create li element
         e.className = 'card'; // add .card class to element
         e.dataset.rank = r; // set rank atribute
         e.dataset.suit = s; // set suit attribute
         e.dataset.pile = p; // set pile attribute;
         e.dataset.selected = 'false'; // set selected attribute
         e.innerHTML = html; // insert html to element
         // query for pile
         var pile = d.querySelector(selector);
         // append to pile
         if (append) pile.appendChild(e);
         // or prepend to pile
         else pile.insertBefore(e, pile.firstChild);
         return;
      }

   // check for played cards
      function checkForPlayedCards(playedCards) {
         // query
         var els = d.querySelectorAll('.card[data-played="true"]');
         for (var e in els) { // loop through elements
            e = els[e];
            if (e.nodeType) {
               var r = e.dataset.rank;
               var s = e.dataset.suit;
               playedCards += ', .card[data-rank="'+r+'"][data-suit="'+s+'"]' ;
            }
         }
         return playedCards;
      }

   // check for empty piles
      function checkForEmptyPiles(table) {
         // reset empty data on all piles
         var els = d.querySelectorAll('.pile'); // query elements
         for (var e in els) { // loop through elements
            e = els[e];
            if (e.nodeType) {
               delete e.dataset.empty;
            }
         }
         // declare var with fake pile so we always have one
         var emptyPiles = '#fake.pile';
         // check spades pile
         if ( table['spades'].length === 0 ) {
            emptyPiles += ', #fnd #spades.pile';
         }
         // check hearts pile
         if ( table['hearts'].length === 0 ) {
            emptyPiles += ', #fnd #hearts.pile';
         }
         // check diamonds pile
         if ( table['diamonds'].length === 0 ) {
            emptyPiles += ', #fnd #diamonds.pile';
         }
         // check clubs pile
         if ( table['clubs'].length === 0 ) {
            emptyPiles += ', #fnd #clubs.pile';
         }
         // check tableau piles
         var tabs = table['tab'];
            // loop through tableau piles
            for (var i = 1; i <= 7; i++) {
               // check tabeau pile
               if ( tabs[i].length === 0 ) {
                  emptyPiles += ', #tab li:nth-child('+i+').pile';
               }
            }
         // mark piles as empty
         els = d.querySelectorAll(emptyPiles); // query elements
         for (var e in els) { // loop through elements
            e = els[e];
            if (e.nodeType) {
               e.dataset.empty = 'true'; // mark as empty
            }
         }
         return emptyPiles;
      }

   // count played cards
      function countPlayedCards(cards) {
         var played = 0;
            // loop through cards
            for (var card in cards) {
               card = cards[card];
               if (card.nodeType) {
                  // check if card has been played
                  if (card.dataset.played === 'true') played++;
               }
            }
         return played;
      }

   // count unplayed cards
      function countUnplayedCards(cards) {
         var unplayed = 0;
            // loop through cards
            for (var card in cards) {
               card = cards[card];
               if (card.nodeType) {
                  // check if card has been played
                  if (card.dataset.played !== 'true') unplayed++;
               }
            }
         return unplayed;
      }

   // flip cards
      function flipCards(selectors, direction) {
         var els = d.querySelectorAll(selectors); // query all elements
         for (var e in els) { // loop through elements
            e = els[e];
            if (e.nodeType) {
               switch(direction) {
                  case 'up' :
                     if (e.dataset.played !== 'true') {
                        // if flipping over tableau card
                        if (e.dataset.pile === 'tab') {
                           // loop through unplayed cards
                           for (var card in unplayedTabCards) {
                              card = unplayedTabCards[card];
                              // if rank and suit matches
                              if (  e.dataset.rank === card[0] &&
                                    e.dataset.suit === card[1] )
                              // score 5 points
                              updateScore(5);
                           }
                        }
                        e.className += ' up'; // add class
                        e.dataset.played = 'true'; // mark as played
                     }
                     break;
                  case 'down' :
                     e.className = 'card'; // reset class
                     delete e.dataset.played; // reset played data attribute
                  default : break;
               }
            }
         }
         return;
      }

   // get face down cards in tableau pile
      function getUnplayedTabCards() {
         // reset array
         unplayedTabCards = [];
         // get all face down card elements
         var els = d.querySelectorAll('#tab .card:not([data-played="true"])');
         for (var e in els) { // loop through elements
            e = els[e];
            if (e.nodeType) {
               unplayedTabCards.push( [ e.dataset.rank, e.dataset.suit ] );
            }
         }
         return unplayedTabCards;
      }

   // size cards
      function sizeCards(selector = '.pile', ratio = 1.4) {
         var s = selector;
         var r = ratio;
         var e = d.querySelector(s); // query element
         var h = e.offsetWidth * r; // get height of element
         // set row heights
         $upper.style.height = h + 10 + 'px';
         $lower.style.height = h + 120 + 'px';
         // set height of elements
         var els = d.querySelectorAll(s); // query all elements
         for (var e in els) { // loop through elements
            e = els[e];
            if (e.nodeType) e.style.height = h + 'px'; // set height in css
         }
      }

   // gameplay
      function play(table) {
         // check for winning table
         if ( checkForWin(table) ) return;
         // check for autowin
         checkForAutoWin(table);
         // bind click events
         bindClick(
            '#stock .card:first-child,' +
            '#waste .card:first-child,' +
            '#fnd .card:first-child,' +
            '#tab .card[data-played="true"]'
         );
         // bind dbl click events
         bindClick(
            '#waste .card:first-child,' +
            '#tab .card:last-child',
            'double'
         );
         console.log('Make Your Move...');
         console.log('......');
      }

   // bind click events
      function bindClick(selectors, double) {
         var elements = d.querySelectorAll(selectors); // query all elements
         // loop through elements
         for (var e in elements) {
            e = elements[e];
            // add event listener
            if (e.nodeType) {
               if (!double) e.addEventListener('click', select);
               else e.addEventListener('dblclick', select);
            }
         }
         return;
      }

   // unbind click events
      function unbindClick(selectors, double) {
         var elements = d.querySelectorAll(selectors); // query all elements
         // loop through elements
         for (var e in elements) {
            e = elements[e];
            // remove event listener
            if (e.nodeType) {
               if (!double) e.removeEventListener('click', select);
               else e.removeEventListener('dblclick', select);
            }
         }
         return;
      }

   // on click handler: select
      var clicks = 0; // set counter for counting clicks
      var clickDelay = 200; // set delay for double click
      var clickTimer = null; // set timer for timeout function
      function select(event) {

         // prevent default
         event.preventDefault();

         // start timer
         if ( $timer.dataset.action !== 'start' ) {
            timer('start');
         }

         // if timestamp matches then return false
         var time = event.timeStamp; // get timestamp
         if ( time === lastEventTime ) {
            console.log('Status: Timestamp Matches, False Click');
            return false;
         }
         else {
            lastEventTime = time; // cache timestamp
         }

         // get variables
         var e = event.target; // get element
         var isSelected = e.dataset.selected; // get selected attribute
         var rank = e.dataset.rank; // get rank attribute
         var suit = e.dataset.suit; // get suit attribute
         var pile = e.dataset.pile; // get pile attribute
         var action = e.dataset.action; // get action attribute

         // create card array
         if (rank && suit) var card = [rank,suit];

         // count clicks
         clicks++;

         // single click
         if (clicks === 1 && event.type === 'click') {
            clickTimer = setTimeout(function() {
               console.log('Single Click Detected', event);

               // reset click counter
               clicks = 0;

               // if same card is clicked
               if (e.dataset.selected === 'true') {
                  console.log('Status: Same Card Clicked');
                  // deselect card
                  delete e.dataset.selected;
                  delete $table.dataset.move;
                  delete $table.dataset.selected;
                  delete $table.dataset.source;
                  console.log('Card Deselected', card, e);
               }

               // if move is in progress
               else if ($table.dataset.move) {
                  console.log('Status: A Move Is In Progess');
                  // get selected
                  var selected = $table.dataset.selected.split(',');
                  // update table dataset with destination pile
                  $table.dataset.dest = e.closest('.pile').dataset.pile;
                  // get destination card or pile
                  if ( card ) var dest = card;
                  else var dest = $table.dataset.dest;
                  // validate move
                  if ( validateMove(selected, dest) ) {
                     // make move
                     makeMove();
                     reset(table);
                     render(table, playedCards);
                     play(table);
                  } else {
                     console.log('Move is Invalid. Try again...');
                     reset(table);
                     render(table, playedCards);
                     play(table);
                     console.log('Card Deselected', card, e);
                  }
               }

               // if stock is clicked
               else if (pile === 'stock') {
                  console.log('Status: Stock Pile Clicked');
                  // if stock isn't empty
                  if (table['stock'].length) {
                     // move card from stock to waste
                     move(table['stock'], table['waste']);
                     reset(table);
                     render(table, playedCards);
                     // if empty, then bind click to stock pile element
                     if (table['stock'].length === 0) bindClick('#stock .reload-icon');
                     // count move
                     countMove(moves++);
                     // return to play
                     play(table);
                  }
               }

               // if stock reload icon is clicked
               else if (action === 'reload') {
                  console.log('Reloading Stock Pile');
                  // remove event listener
                  unbindClick('#stock .reload-icon');
                  // reload stock pile
                  if (table['waste'].length) {
                     table['stock'] = table['waste']; // move waste to stock
                     table['waste'] = [] // empty waste
                  }
                  // render table
                  render(table, playedCards);
                  // turn all stock cards face down
                  flipCards('#stock .card', 'down');
                  // update score by -100 pts
                  updateScore(-100);
                  // return to play
                  play(table);
               }

               // if no move is in progress
               else {
                  // select card
                  e.dataset.selected = 'true';
                  $table.dataset.move = 'true';
                  $table.dataset.selected = card;
                  $table.dataset.source = e.closest('.pile').dataset.pile;
                  // if ace is selected
                  if (rank === 'A') {
                     console.log('Ace Is Selected');
                     bindClick('#fnd #'+suit+'s.pile[data-empty="true"]');
                  }
                  if (rank === 'K') {
                     console.log('King Is Selected');
                     bindClick('#tab .pile[data-empty="true"]');
                  }
               }

            }, clickDelay);
         }

         // double click
         else if (event.type === 'dblclick') {
            console.log('Double Click Detected', event);
            clearTimeout(clickTimer); // prevent single click
            clicks = 0; // reset click counter
            // select card
            e.dataset.selected = 'true';
            $table.dataset.move = 'true';
            $table.dataset.selected = card;
            $table.dataset.source = e.closest('.pile').dataset.pile;
            // get destination pile
            if ( card) var dest = card[1]+'s';
            // update table dataset with destination
            $table.dataset.dest = dest;
            // validate move
            if ( validateMove(card, dest) ) {
               // make move
               makeMove();
               reset(table);
               render(table, playedCards);
               play(table);
            } else {
               console.log('Move is Invalid. Try again...');
               reset(table);
               render(table, playedCards);
               play(table);
               console.log('Card Deselected', card, e);
            }

         }

      }

   // validate move
      function validateMove(selected, dest) {
         console.log ('Validating Move...', selected, dest);

         // if selected card exists
         if (selected) {
            var sRank = parseRankAsInt(selected[0]);
            var sSuit = selected[1];
         }

         // if destination is another card
         if (dest.constructor === Array) {
            console.log('Desitination appears to be a card');
            var dRank = parseRankAsInt(dest[0]);
            var dSuit = dest[1];
            var dPile = $table.dataset.dest;
            // if destination pile is foundation
            if (['spades','hearts','diamonds','clubs'].indexOf(dPile) >= 0) {
               // if rank isn't in sequence then return false
               if (dRank - sRank !== -1) {
                 console.log('Rank sequence invalid');
                 console.log(dRank - sRank)
                 return false;
               }
               // if suit isn't in sequence then return false
               if ( sSuit !== dSuit ) {
                  console.log('Suit sequence invalid');
                  return false;
               }
            }
            // if destination pile is tableau
            else {
               // if rank isn't in sequence then return false
               if (dRank - sRank !== 1) {
                 console.log('Rank sequence invalid');
                 return false;
               }
               // if suit isn't in sequence then return false
               if ( ( (sSuit === 'spade' || sSuit === 'club') &&
                  (dSuit === 'spade' || dSuit === 'club') ) ||
                  ( (sSuit === 'heart' || sSuit === 'diamond') &&
                  (dSuit === 'heart' || dSuit === 'diamond') ) ) {
                 console.log('Suit sequence invalid');
                 return false;
               }
            }
            // else return true
            console.log('Valid move');
            return true;

         }

         // if destination is foundation pile
         if (['spades','hearts','diamonds','clubs'].indexOf(dest) >= 0) {
            console.log('Destination appears to be empty foundation');

            // get last card in destination pile
            var lastCard = d.querySelector('#'+dest+' .card:first-child');
            if (lastCard) {
               var dRank = parseRankAsInt(lastCard.dataset.rank);
               var dSuit = lastCard.dataset.suit;
            }
            // if suit doesn't match pile then return false
            if ( sSuit + 's' !== dest ) {
               console.log('Suit sequence invalid');
               return false;
            }
            // if rank is ace then return true
            else if ( sRank === 1 ) {
               console.log('Valid Move');
               return true;
            }
            // if rank isn't in sequence then return false
            else if ( sRank - dRank !== 1 ) {
               console.log('Rank sequence invalid');
               return false;
            }
            // else return true
            else {
               console.log('Valid move');
               return true;
            }
         }

         // if destination is empty tableau pile
         if ( dest >= 1 && dest <= 7 ) {
            console.log('Destination appears tp be empty tableau');
            return true;
         }

      }

   // make move
      function makeMove() {
         console.log('Making Move...');

         // get source and dest
         var source = $table.dataset.source;
         var dest = $table.dataset.dest;
         console.log('From '+source+' pile to '+dest+' pile');

         // if pulling card from waste pile
         if ( source === 'waste') {
            // if moving card to foundation pile
            if ( isNaN(dest) ) {
               console.log('Moving To Foundation Pile');
               move(table[source], table[dest], true);
               updateScore(10); // score 10 pts
            }
            // if moving card to tableau pile
            else {
               console.log('Moving To Tableau Pile');
               move(table[source], table['tab'][dest], true);
               updateScore(5); // score 5 pts
            }
         }

         // if pulling card from foundation pile
         else if (['spades','hearts','diamonds','clubs'].indexOf(source) >= 0) {
            // only allow moves to tableau piles
            if ( isNaN(dest) ) {
               console.log('That move is not allowed');
               return false;
            }
            // if moving card to tableau pile
            else {
               console.log('Moving To Tableau Pile');
               move(table[source], table['tab'][dest], true);
               updateScore(-15); // score -15 pts
            }
         }

         // if pulling card from tableau pile
         else {
            // if moving card to foundation pile
            if ( isNaN(dest) ) {
               console.log('Moving To Foundation Pile');
               move(table['tab'][source], table[dest], true);
               updateScore(10); // score 10 pts
            }
            // if moving card to tableau pile
            else {
               console.log('Moving To Tableau Pile');
               // get selected card
               var selected = d.querySelector('.card[data-selected="true"');
               // get cards under selected card
               var selectedCards = [selected];
               while ( selected = selected['nextSibling'] ) {
                  if (selected.nodeType) selectedCards.push(selected);
               }
               // move card(s)
               move(
                  table['tab'][source],
                  table['tab'][dest],
                  true,
                  selectedCards.length
               );
            }
         }

         // unbind click events
         unbindClick(
            '#stock .card:first-child,' +
            '#waste .card:first-child,' +
            '#fnd .card:first-child,' +
            '#fnd #spades.pile[data-empty="true"],' +
            '#fnd #hearts.pile[data-empty="true"],' +
            '#fnd #diamonds.pile[data-empty="true"],' +
            '#fnd #clubs.pile[data-empty="true"],' +
            '#tab .card[data-played="true"],' +
            '#tab .pile[data-empty="true"]'
         );
         // unbind double click events
         unbindClick(
            '#waste .card:first-child' +
            '#tab .card:last-child',
            'double'
         )

         // count move
         countMove(moves++);

         // reset table
         console.log('Ending Move...');

         return;
      }

   // parse rank as integer
      function parseRankAsInt(rank) {
         // assign numerical ranks to letter cards
         switch (rank) {
            case 'A' : rank = '1'; break;
            case 'J' : rank = '11'; break;
            case 'Q' : rank = '12'; break;
            case 'K' : rank = '13'; break;
            default : break;
         }
         // return integer value for rank
         return parseInt(rank);
      }

   // parse integer as rank
      function parseIntAsRank(int) {
         // parse as integer
         rank = parseInt(int);
         // assign letter ranks to letter cards
         switch(rank) {
            case 1 : rank = 'A'; break;
            case 11 : rank = 'J'; break;
            case 12 : rank = 'Q'; break;
            case 13 : rank = 'K'; break;
            default : break;
         }
         return rank;
      }

   // reset table
      function reset(table) {
         delete $table.dataset.move;
         delete $table.dataset.selected;
         delete $table.dataset.source;
         delete $table.dataset.dest;
         delete $fnd.dataset.played;
         delete $fnd.dataset.unplayed;
         delete $tab.dataset.played;
         delete $tab.dataset.unplayed;
         console.log('Table reset');
      }

   // timer funcion
      function timer(action) {
         // declare timer vars
         var minutes = 0;
         var seconds = 0;
         var gameplay = d.body.dataset.gameplay;
         // set timer attribute
         $timer.dataset.action = action;
         // switch case
         switch (action) {
            // start timer
            case 'start' :
               console.log('Starting Timer...');
               // looping function
               clock = setInterval(function() {
                  // increment
                  time++;
                  // parse minutes and seconds
                  minutes = parseInt(time / 60, 10);
                  seconds = parseInt(time % 60, 10);
                  minutes = minutes < 10 ? "0" + minutes : minutes;
                  seconds = seconds < 10 ? "0" + seconds : seconds;
                  // output to display
                  $timerSpan.textContent = minutes + ':' + seconds;
                  // if 10 seconds has passed decrement score by 2 pts
                  if ( time % 10 === 0 ) updateScore(-2);
               }, 1000);
               // add dataset to body
               d.body.dataset.gameplay = 'active';
               // unbind click to play button
               if ( gameplay === 'paused')
               $playPause.removeEventListener('click', playTimer);
               // bind click to pause button
               $playPause.addEventListener('click', pauseTimer = function(){
                  timer('pause');
               });
            break;
            // pause timer
            case 'pause' :
               console.log('Pausing Timer...');
               clearInterval(clock);
               d.body.dataset.gameplay = 'paused';
               // unbind click to pause button
               if ( gameplay === 'active')
               $playPause.removeEventListener('click', pauseTimer);
               // bind click tp play button
               $playPause.addEventListener('click', playTimer = function(){
                  timer('start');
               });
            break;
            // stop timer
            case 'stop' :
               console.log('Stoping Timer...');
               clearInterval(clock);
               d.body.dataset.gameplay = 'over';
            break;
            // default
            default : break;
         }
         console.log(time);
         return;
      }

   // move counter
      function countMove(moves) {
         console.log('Move Counter', moves);
         // set move attribute
         $moveCount.dataset.moves = moves + 1;
         // output to display
         $moveCountSpan.textContent = moves + 1;
         return;
      }

   // scoring function
      /*
         Standard scoring is determined as follows:
         - Waste to Tableau  5
         - Waste to Foundation  10
         - Tableau to Foundation   10
         - Turn over Tableau card  5
         - Foundation to Tableau   −15
         - Recycle waste when playing by ones  −100
         (minimum score is 0)

         Moving cards directly from the Waste stack to a Foundation awards 10 points. However, if the card is first moved to a Tableau, and then to a Foundation, then an extra 5 points are received for a total of 15. Thus in order to receive a maximum score, no cards should be moved directly from the Waste to Foundation.

         For every 10 seconds of play, 2 points are taken away. Bonus points are calculated with the formula of 700,000 / (seconds to finish) if the game takes more than 30 seconds. If the game takes less than 30 seconds, no bonus points are awarded.
      */
      function updateScore(points) {
         console.log('Updating Score', points);
         // get score
         score = parseInt($score.dataset.score) + points;
         // set minimum score to 0
         score = score < 0 ? 0 : score;
         // parse as integer
         score = parseInt(score);
         // set score attribute
         $score.dataset.score = score;
         // output to display
         $score.children[1].textContent = score;
         return score;
      }

   // calculate bonus points
      function getBonus() {
         if (time >= 30) bonus = parseInt(700000 / time);
         console.log(bonus);
         return bonus;
      }

   // check for win
      function checkForWin(table) {
         // if all foundation piles are full
         if (  table['spades'].length +
               table['hearts'].length +
               table['diamonds'].length +
               table['clubs'].length
               === 52 ) {
            console.log('Game Has Been Won');
            // stop timer
            timer('stop');
            // bonus points for time
            updateScore(getBonus());
            // throw confetti
            throwConfetti();
            // return true
            return true;
         }
         else return false;
      }

   // check for auto win
      function checkForAutoWin(table) {
         // if all tableau cards are played and stock is empty
         if (  parseInt($tab.dataset.unplayed) +
               table['stock'].length +
               table['waste'].length === 0) {
            // show auto win button
            $autoWin.style.display = 'block';
            // bind click to auto win button
            $autoWin.addEventListener('click', autoWin);
         }
         return;
      }

   // auto win
      function autoWin() {
         console.log('Huzzah!');
         // hide auto win button
         $autoWin.style.display = 'none';
         // unbind click to auto win button
         $autoWin.removeEventListener('click', autoWin);
         // unbind click events
         unbindClick(
            '#stock .card:first-child,' +
            '#waste .card:first-child,' +
            '#fnd .card:first-child,' +
            '#fnd #spades.pile[data-empty="true"],' +
            '#fnd #hearts.pile[data-empty="true"],' +
            '#fnd #diamonds.pile[data-empty="true"],' +
            '#fnd #clubs.pile[data-empty="true"],' +
            '#tab .card[data-played="true"],' +
            '#tab .pile[data-empty="true"]'
         );
         // unbind double click events
         unbindClick(
            '#waste .card:first-child' +
            '#tab .card:last-child',
            'double'
         );
         // reset table
         reset(table);
         render(table);
         // animate cards to foundation piles
         autoWinAnimation(table);
         // stop timer
         timer('stop');
         // bonus points for time
         updateScore(getBonus());
      }

   // auto win animation
      function autoWinAnimation(table) {
         // set number of iterations
         var i = parseInt($tab.dataset.played);
         // create animation loop
         function animation_loop() {
            // get lowest ranking card
               var bottomCards = []; // create array for the bottom cards
               var els = d.querySelectorAll('#tab .card:last-child');
               for (var e in els) { // loop through elements
                  e = els[e];
                  if (e.nodeType)
                     bottomCards.push( parseRankAsInt(e.dataset.rank) );
               }
               // get the lowest rank from array of bottom cards
               var lowestRank = Math.min.apply(Math, bottomCards);
               // parse integer as rank
               var rank = parseIntAsRank(lowestRank);
               // get element with rank
               var e = d.querySelector('#tab .card[data-rank="'+rank+'"]');

            // setup move
               // get suit of card
               var suit = e.dataset.suit;
               // create card array with rank and suit
               var card = [rank, suit];
               // get destination pile
               var dest = suit+'s';

            // make move
               if ( validateMove(card, dest) ) {
                  // set source pile
                  var pile = e.parentElement.parentElement;
                  $table.dataset.source = pile.dataset.pile;
                  // set dest pile
                  $table.dataset.dest = dest;
                  // make move
                  makeMove();
                  reset(table);
                  render(table, playedCards);
               } else {
                  console.log('Move is Invalid. Try again...');
                  reset(table);
                  render(table, playedCards);
               }
            // let's do it again in 100ms
            setTimeout(function() {
               i--;
               if (i !== 0) animation_loop();
               // at the end lets celebrate!
               else throwConfetti();
            }, 100);
         };
         // run animation loop
         animation_loop();
      }

   // throw confetti
      /* Thanks to @gamanox
         https://codepen.io/gamanox/pen/FkEbH
      */
      function throwConfetti() {
         console.log('Confetti!');

         var COLORS, Confetti, NUM_CONFETTI, PI_2, canvas, confetti, context, drawCircle, drawCircle2, drawCircle3, i, range, xpos;

         NUM_CONFETTI = 60;

         COLORS = [[255, 255, 255], [255, 144, 0], [255, 255, 255], [255, 144, 0], [0, 277, 235]];

         PI_2 = 2 * Math.PI;

         canvas = d.getElementById("confetti");

         context = canvas.getContext("2d");

         window.w = 0;

         window.h = 0;

         window.resizeWindow = function() {
            window.w = canvas.width = window.innerWidth;
            return window.h = canvas.height = window.innerHeight;
         };

         window.addEventListener('resize', resizeWindow, false);

         window.onload = function() {
            return setTimeout(resizeWindow, 0);
         };

         range = function(a, b) {
            return (b - a) * Math.random() + a;
         };

         drawCircle = function(x, y, r, style) {
            context.beginPath();
            context.moveTo(x, y);
            context.bezierCurveTo(x - 17, y + 14, x + 13, y + 5, x - 5, y + 22);
            context.lineWidth = 3;
            context.strokeStyle = style;
            return context.stroke();
         };

         drawCircle2 = function(x, y, r, style) {
            context.beginPath();
            context.moveTo(x, y);
            context.lineTo(x + 10, y + 10);
            context.lineTo(x + 10, y);
            context.closePath();
            context.fillStyle = style;
            return context.fill();
         };

         drawCircle3 = function(x, y, r, style) {
            context.beginPath();
            context.moveTo(x, y);
            context.lineTo(x + 10, y + 10);
            context.lineTo(x + 10, y);
            context.closePath();
            context.fillStyle = style;
            return context.fill();
         };

         xpos = 0.9;

         d.onmousemove = function(e) {
            return xpos = e.pageX / w;
         };

         window.requestAnimationFrame = (function() {
            return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
                  return window.setTimeout(callback, 100 / 20);
            };
         })();

         Confetti = (function() {
            function Confetti() {
               this.style = COLORS[~~range(0, 5)];
               this.rgb = "rgba(" + this.style[0] + "," + this.style[1] + "," + this.style[2];
               this.r = ~~range(2, 6);
               this.r2 = 2 * this.r;
               this.replace();
            }

            Confetti.prototype.replace = function() {
               this.opacity = 0;
               this.dop = 0.03 * range(1, 4);
               this.x = range(-this.r2, w - this.r2);
               this.y = range(-20, h - this.r2);
               this.xmax = w - this.r;
               this.ymax = h - this.r;
               this.vx = range(0, 2) + 8 * xpos - 5;
               return this.vy = 0.7 * this.r + range(-1, 1);
            };

            Confetti.prototype.draw = function() {
               var ref;
               this.x += this.vx;
               this.y += this.vy;
               this.opacity += this.dop;
               if (this.opacity > 1) {
                 this.opacity = 1;
                 this.dop *= -1;
               }
               if (this.opacity < 0 || this.y > this.ymax) {
                 this.replace();
               }
               if (!((0 < (ref = this.x) && ref < this.xmax))) {
                 this.x = (this.x + this.xmax) % this.xmax;
               }
               drawCircle(~~this.x, ~~this.y, this.r, this.rgb + "," + this.opacity + ")");
               drawCircle3(~~this.x * 0.5, ~~this.y, this.r, this.rgb + "," + this.opacity + ")");
               return drawCircle2(~~this.x * 1.5, ~~this.y * 1.5, this.r, this.rgb + "," + this.opacity + ")");
            };

            return Confetti;

         })();

         confetti = (function() {
            var j, ref, results;
            results = [];
            for (i = j = 1, ref = NUM_CONFETTI; 1 <= ref ? j <= ref : j >= ref; i = 1 <= ref ? ++j : --j) {
               results.push(new Confetti);
            }
            return results;
         })();

         window.step = function() {
            var c, j, len, results;
            requestAnimationFrame(step);
            context.clearRect(0, 0, w, h);
            results = [];
            for (j = 0, len = confetti.length; j < len; j++) {
               c = confetti[j];
               results.push(c.draw());
            }
            return results;
         };

         step();

         // fix initial bug when firing
         resizeWindow();

         // fade in
         canvas.style.opacity = 0;
         var tick = function() {
            canvas.style.opacity = +canvas.style.opacity + 0.01;
            if ( +canvas.style.opacity < 1 ) {
               ( window.requestAnimationFrame &&
                 requestAnimationFrame(tick) ) ||
               setTimeout(tick, 100)
            }
         };
         tick();

      }
              
            
!
999px

Console