JSFiddle - React, Tailwind, and code Playground

by JMPerez

HTML

<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">

<form role="form">
  <div class="form-group">
    <input type="text" class="form-control" placeholder="Type something" />
  </div>
    <a class="btn button button-disabled step1">
			<span class="button-label step1">Save</span>
			<span class="button-label step2">
                <div class="throbber"></div>Saving</span>
			<span class="button-label step3">✔ Saved</span>
	</a>
</form>

CSS

.btn {
    padding: 0;
}

.btn > div {
    padding: 0px;
    transition: top 0.35s;
    -webkit-transition: top 0.35s;
    display: block;
    height: 100%;
    position: relative;
    top: 0%;
}

.btn {
    height: 40px;
    line-height: 40px;
    overflow: hidden;
    display: inline-block;
    width: 200px;
    position: relative;
}


body {
    padding: 10px;
}

.btn.loading > div {
    top: -100%;
}
/*
.btn .loading {
    display: none;
}

.btn.loading .loading {
    display: inline-block;
}

.btn .end{
    display:none;
}

.btn.end .end {
    display:inline-block;
}

.btn .default{
    display:none;
}

.btn.default .default {
    display:inline-block;
}
*/

@-webkit-keyframes throbber {
  0% {
    background: #dde2e7;
  }

  10% {
    background: #6b9dc8;
  }

  40% {
    background: #dde2e7;
  }
}

@-moz-keyframes throbber {
  0% {
    background: #dde2e7;
  }

  10% {
    background: #6b9dc8;
  }

  40% {
    background: #dde2e7;
  }
}

@-o-keyframes throbber {
  0% {
    background: #dde2e7;
  }

  10% {
    background: #6b9dc8;
  }

  40% {
    background: #dde2e7;
  }
}

@keyframes throbber {
  0% {
    background: #dde2e7;
  }

  10% {
    background: #6b9dc8;
  }

  40% {
    background: #dde2e7;
  }
}

/* :not(:required) hides these rules from IE9 and below */
.throbber:not(:required) {
  -webkit-animation: throbber 1000ms 300ms infinite ease-out;
  -moz-animation: throbber 1000ms 300ms infinite ease-out;
  -ms-animation: throbber 1000ms 300ms infinite ease-out;
  -o-animation: throbber 1000ms 300ms infinite ease-out;
  animation: throbber 1000ms 300ms infinite ease-out;
  background: #dde2e7;
  display: inline-block;
  position: relative;
  text-indent: -9999px;
  width: 0.7em;
  height: 0.7em;
  margin: 0 1.6em;
    border-radius: 1em;
}
.throbber:not(:required):before, .throbber:not(:required):after {
  background: #dde2e7;
  content: '\x200B';
  display: inline-block;
  width: 0.7em;
  height: 0.7em;
  position: absolute;
  top: 0;
   ...

JavaScript

var button = document.querySelector('.button');

var input = document.querySelector('input');
input.addEventListener('keyup', function() {
    button.classList.remove('button-disabled');
    button.classList.remove('step2', 'step3')
    button.classList.add('step1');
});

button.addEventListener('click', function(e) {
    this.classList.remove('step1');
    this.classList.add('step2');
    
    setTimeout((function() {
      this.classList.remove('step2');
      this.classList.add('step3');
      this.classList.add('button-disabled');        
    }).bind(this), 2500);
});

button.classList.add('button-disabled');