JSFiddle - React, Tailwind, and code Playground

by rhumaric

HTML

<script src="http://fonts.googleapis.com/css?family=Peralta"></script>
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Peralta">
<h1>What was under the tree?</h1>
<ul class="gifts">
    <li class="gift">A yummy gift</li><!--
 --><li class="gift">A funny gift</li><!--
 --><li class="gift">A noisy gift</li>
</ul>
<div class="buttons">
  <button name="add-gift">Even more!</button>
</div>
<script type="application/x-template">
@keyframes {{animationId}} {
    0% {
        height: 0;
        margin-bottom: 0;
        -webkit-transform: translateX(-200%);
        transform: translateX(-200%);
    }
    
    60% {
        height: {{height}}px;
        margin-bottom: 1em;
        -webkit-transform: translateX(-200%);
        transform: translateX(-200%);
    }
    
    100% {
        height: {{height}}px;
        margin-bottom: 1em;
        -webkit-transform: translateX(0);
        transform: translateX(0);
    }
}
    
@-webkit-keyframes {{animationId}} {
    0% {
        height: 0;
        margin-bottom: 0;
        -webkit-transform: translateX(-200%);
        transform: translateX(-200%);
    }
    
    60% {
        height: {{height}}px;
        margin-bottom: 1em;
        -webkit-transform: translateX(-200%);
        transform: translateX(-200%);
    }
    
    100% {
        height: {{height}}px;
        margin-bottom: 1em;
        -webkit-transform: translateX(0);
        transform: translateX(0);
    }
}
</script>

CSS

@import url('https://fonts.googleapis.com/css?family=Peralta');

* {
  box-sizing: border-box;
}

body {
  font-family: 'Peralta', cursive;
  line-height: 2;
  color: hsl(45,95%,75%);
  background:  hsl(150, 90%, 30%);
  margin: 0;
  padding-top: 1em;
  text-align: center;
}

h1 {
  text-align: center;
  font-weight: normal;
  margin: 0;
  line-height: 1.5;
}

.clone {
  position: absolute;
  top: 0;
  left: 0;
  visibility: hidden;
}

.gifts {
  text-align: left;
  padding: 0 1em;    
}

.gift {
  list-style-type: none;
  margin-bottom: 1em;
  background: rgba(255,255,255, .2);
  padding-left: 1em;
}

.gift::before {
  content: '';
  display: inline-block;
  width: 2em;
  text-align: center;
  position: relative;
  top: .25em;
  left: -.5em;
  color: hsl(202, 80%, 30%);
  background-image: url("...

JavaScript

var gifts = document.querySelector('.gifts');
var firstElement = document.querySelector('.gift:nth-child(1)');

var button = document.querySelector('button');
button.addEventListener('click', function (event) {
    
    var gift = createGift('A gift so big<br>one line was<br>not enough');
    console.log('New element measurements (px): ', gift.offsetWidth, 'x', gift.offsetHeight);
    
    var size = measureInsertedElement(gifts, gift);
    
    var styleTag = createAnimation(size.height);
        
    gift.style['animation'] = styleTag.getAttribute('data-animationName') + ' 0.75s';
    gift.style['-webkit-animation'] = styleTag.getAttribute('data-animationName') + ' 0.75s';
    
    // Cleanup the `style` property so that the `animation`
    // CSS property can be set using a class, ID or element
    // selector in a stylesheet    
    gift.addEventListener('animationend', function cleanup(event) {
        this.style.animation = '';
    });
    
    gift.addEventListener('webkitAnimationEnd', function cleanupWebkit(event) {
        this.style['-webkit-animation'];
    });
    
    gifts.insertBefore(gift, firstElement.nextSibling);
});

function createGift(label) {
    var gift = document.createElement('li');
    gift.innerHTML = label;
    gift.classList.add('gift');
    return gift;
}

function cloneContainer(container) {
    
    var clone = container.cloneNode();
    clone.classList.add('clone');
    clone.style.width = container.clientWidth;
    clone.style.height = container.clientHeight;
    return clone;
}

function measureInsertedElement(container, element) {
    
    // We don't want to insert the element in the
    // actual container, so we'll clone it
    var containerClone = cloneContainer(container);
    
    // If the clone is not in the document,
    // the measured size will still be 0x0 :(
    document.body.appendChild(containerClone);
    
    // Depending on how you insert the element,
    // the insertion might need a bit more logic.
    //...