Progress Button Styles

A set of flat and 3D progress button styles where the button itself serves as a progress indicator. 3D styles are used for showing the progress indication on one side of the button while rotating the button in perspective.

From our sponsor: Convert designs into clean, developer-friendly React, Vue and HTML code with Anima.

Today we’d like to share some progress button styles with you. You surely know “Ladda” by Hakim El Hattab, a UI concept that indicates progress directly on the button that invokes a loading action. Some of the buttons have a built-in progress bar and today we’d like to explore that idea with some creative progress button styles. Using perspective will allow us to create some fun 3D effects besides the flat “filling” styles. For a complete solution, please take a better look at Ladda.

Please note that we’ll be using transitions on pseudo-elements which are still not supported in some browsers (e.g. Safari and Mobile Safari).

Also note that we need transform-style: preserve-3d support for the 3D styles, which neither IE10 nor IE11 support.

With the script that we’ve created for showing the button styles, we take a simple button markup

<button class="progress-button" data-style="rotate-angle-bottom" data-perspective data-horizontal>Submit</button>

and transform it into the following structure:

<button class="progress-button" data-style="rotate-angle-bottom" data-perspective data-horizontal>
	<span class="progress-wrap">
		<span class="content">Submit</span>
		<span class="progress">
			<span class="progress-inner"></span>

If we don’t set the data-perspective attribute, then we’ll make this structure:

<button class="progress-button" data-style="fill" data-horizontal>
	<span class="content">Submit</span>
	<span class="progress">
		<span class="progress-inner"></span>

We also indicate if we have a style that needs horizontal or vertical progress bar filling. This will be used in our CSS to specify the regarding styles.
The following styles are the general and common styles for all buttons (note that perspective styles are only needed for the buttons with 3D transforms):

*, *:after, *::before { box-sizing: border-box; }

@font-face {
	font-weight: normal;
	font-style: normal;
	font-family: 'icomoon';
	src:url('../fonts/icomoon/icomoon.eot?#iefix') format('embedded-opentype'),
		url('../fonts/icomoon/icomoon.ttf') format('truetype'),
		url('../fonts/icomoon/icomoon.woff') format('woff'),
		url('../fonts/icomoon/icomoon.svg#icomoon') format('svg');

.progress-button {
	position: relative;
	display: inline-block;
	padding: 0 60px;
	outline: none;
	border: none;
	background: #1d9650;
	color: #fff;
	text-transform: uppercase;
	letter-spacing: 1px;
	font-size: 1em;
	line-height: 4;

.progress-button[disabled].state-loading {
	cursor: default;

.progress-button .content {
	position: relative;
	display: block;

.progress-button .content::before,
.progress-button .content::after  {
	position: absolute;
	right: 20px;
	color: #0e7138;
	font-family: "icomoon";
	opacity: 0;
	transition: opacity 0.3s 0.3s;

.progress-button .content::before {
	content: "e600"; /* Checkmark for success */

.progress-button .content::after {
	content: "e601"; /* Cross for error */

.progress-button.state-success .content::before,
.progress-button.state-error .content::after {
	opacity: 1;

.notransition {
	transition: none !important;

.progress-button .progress {
	background: #148544;

.progress-button .progress-inner {
	position: absolute;
	left: 0;
	background: #0e7138;

.progress-button[data-horizontal] .progress-inner {
	top: 0;
	width: 0;
	height: 100%;
	transition: width 0.3s, opacity 0.3s;

.progress-button[data-vertical] .progress-inner {
	bottom: 0;
	width: 100%;
	height: 0;
	transition: height 0.3s, opacity 0.3s;

/* Necessary styles for buttons with 3D transforms */

.progress-button[data-perspective] {
	position: relative;
	display: inline-block;
	padding: 0;
	background: transparent;
	perspective: 900px;

.progress-button[data-perspective] .content {
	padding: 0 60px;
	background: #1d9650;

.progress-button[data-perspective] .progress-wrap {
	display: block;
	transition: transform 0.2s;
	transform-style: preserve-3d;

.progress-button[data-perspective] .content,
.progress-button[data-perspective] .progress {
	outline: 1px solid rgba(0,0,0,0); /* Smoothen jagged edges in FF */

We are using the pseudo-elements ::before and ::after for the success or error icons which we fade in once loading is finished. The span with the class progress is used as the main wrapper for the progress bar itself which is the span with class progress-inner. Sometimes we use the progress span with a background color, while other times we will just style the child span. We’ll also provide some general styles for the vertical and horizontal case.

Note that for the 3D examples, we’ll use the button as a “shell” that will serve to add the perspective value. The content span will contain the button styles like the background color and the padding, and everything will be wrapped into a span with the class progress-wrap which will be the element that we transform.

An example of an individual button style is the following:

/* Rotate bottom 3d */
/* ====================== */

.progress-button[data-style="rotate-angle-bottom"] .progress {
	position: absolute;
	top: 100%;
	left: 0;
	width: 100%;
	height: 20px;
	box-shadow: 0 -1px 0 #148544; /* fix the blurriness that causes a gap */
	transform: rotateX(-90deg);
	transform-origin: 50% 0%;

.progress-button[data-style="rotate-angle-bottom"].state-loading .progress-wrap {
	transform: rotateX(45deg);

The button will have one of the three states (or none): state-loading, state-success and state-error.

For browsers that don’t support necessary properties, we’ll provide the default fallback of the first style (fill horizontal).

Icons are by IcoMoon and the icon font was created with the IcoMoon app.

We hope that you find these button styles inspiring and useful!

Tagged with:

Mary Lou

ML is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

Stay up to date with the latest web design and development news and relevant updates from Codrops.

CSS Reference

Learn about all important CSS properties from the basics with our extensive and easy-to-read CSS Reference.

It doesn't matter if you are a beginner or intermediate, start learning CSS now.

Feedback 52

Comments are closed.
  1. If you’re using jQuery to generate it dynamically prepare for it not to work, it automatically removes the span tags inside the button because it doesnt meet the HTML spec.

  2. They js that is used causes an issue that doesn’t allow to submit on Chrome. Only browser, I’ve been having issues with.

  3. You make web a better place. I’m stunned every time I see something from you, and I’ve learnt so much, Thank you!

  4. Awesome work, thanks a lot!

    Same as Danilo Vaz i would like to know, how to integrate this with AJAX. Have I change transition-duration or … ?

  5. Hi !
    I try to use your submit on my form but i’ve a problem ๐Ÿ™
    I use required on my fields, i try to do not activate the effect of submit if required appears. is it possible to do it ?