Skip to content

WPShout Newsletter

Sign up for news, tips, and insights to help you build better websites.

Newsletter - Top banner

Pure CSS Submit Button with Loading Spinner

Nobody likes clicking submit and wondering if anything happened.

When a user clicks a submit button, it’s good practice to give them instant feedback that something is happening. A common approach is to temporarily disable the button and show a loading animation while the action runs in the background.

In this article, we’ll build a button with a loading spinner using CSS for the animation and just a little JavaScript to handle the button state and form submission.

Here is the HTML for the button:

<button>SUBMIT FORM<span class="spinner"></span></button>Code language: HTML, XML (xml)

Here is the CSS (commented):

/* This is the submit button styles */
button {
  display: block;
  margin: 0 auto;
  padding: .6em .8em;
  /* Font-size is the root value that determines size of spinner parts. 
Change this to whatever you want and spinner elements will size to match. */
  font-size: 20px;
  font-weight: bold;
  border-radius: .4em;
  border: none;
  overflow: hidden;
  cursor: pointer;
  position: relative;
  transition: all 1s;
}

/* focus/disabled styles, you can change this for accessibility */
button:focus, button:disabled {
  outline: none;
  background: #aaa;
}

/* This is the space for the spinner to appear, applied to the button */
.spin {
  padding-left: 2.5em;
  display: block;
}

/* position of the spinner when it appears, you might have to change these values */
.spin .spinner {
  left: -.6em;
  top: .4em;
  width: 2.5em;
  display: block;
  position: absolute;
}

/* spinner animation */
@keyframes spinner {
  0% {
    transform: rotate(0deg);
  }
  
  100% {
    transform: rotate(360deg);
  }
}

/* The actual spinner element is a pseudo-element */
.spin .spinner::before {
  content: "";
  width: 1.5em; /* Size of the spinner */
  height: 1.5em; /* Change as desired */
  position: absolute;
  top: 50%;
  left: 50%;
  border-radius: 50%;
  border: solid .35em #999; /* Thickness/color of spinner track */
  border-bottom-color: #555; /* Color of variant spinner piece */
  animation: .8s linear infinite spinner; /* speed of spinner */
  transform: translate(-50%, -50%);
  will-change: transform;
}

/* optional, but it will affect the size if changed */
*, *::before, *::after {
  box-sizing: border-box;
}
Code language: CSS (css)

Finally, the JavaScript:

let btn = document.querySelector('button');

btn.addEventListener('click', function () {
  // form submission starts
  // button is disabled
  btn.classList.add('spin');
  btn.disabled = true;
  
  // This disables the whole form via the fieldset
  btn.form.firstElementChild.disabled = true;
  
  // this setTimeout call mimics some asyncronous action
  // you would have something else here
  window.setTimeout(function () {
    // when asyncronous action is done, remove the spinner
    // re-enable button/fieldset
    btn.classList.remove('spin');
    btn.disabled = false;
    btn.form.firstElementChild.disabled = false;
  }, 4000);
}, false);
Code language: JavaScript (javascript)

And here is a live demo:

☝️Some things worth noting:

  • The JavaScript uses a setTimeout() call to mimic what might happen using Ajax. This would be removed and replaced with whatever you code is doing during the button submission process.
  • The size of the spinner is based on the font-size set on the <button> element using pixels. Adjust this and the spinner size and position will change automatically to match via em units.
  • The button also disables the parent fieldset. This is optional, but it’s an added feature. You can disable this by removing the appropriate commented line in the code.
  • The HTML in the demo disables the form from being submitted. This is only for demo purposes.

For more great examples see: Ladda spinners by Hakim El Hattab

Note: To the best of our knowledge, the information above and the snippet are accurate and up to date. However, in case you notice something wrong, please report snippet or leave a comment below.

View All Code Snippets
Louis Lazaris
Share:

0 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

Or start the conversation in our Facebook group for WordPress professionals. Find answers, share tips, and get help from other WordPress experts. Join now (it’s free)!