By Craig Buckler

How to Build a Better Button in CSS3

By Craig Buckler

I tend to opt for <button> tags rather than <input type="submit" /> but, whichever you prefer, the standard style follows your OS conventions. It can be a little uninspiring…

standard HTML button

Let’s build a better button using CSS3 styles, animations and transformations:

better HTML button with CSS3

View the button demonstration…


No surprises here — we only require a single button tag:

<button>Click me!</button>

We’re going to apply our styles to every button but, if you’d prefer not to do that, add a class and target it in the CSS accordingly.


Our button should work well in the latest browsers but also degrade gracefully in older applications. That said, I’m only going to add CSS prefixes when they’re absolutely necessary. Firefox, Opera and IE10 support transitions, transformations and animations without prefixes — but we still require -webkit- for Chrome and Safari.

Let’s start with some basic styles to format the button:

	display: block;
	font-size: 1.1em;
	font-weight: bold;
	text-transform: uppercase;
	padding: 10px 15px;
	margin: 20px auto;
	color: #ccc;
	background-color: #555;
	background: -webkit-linear-gradient(#888, #555);
	background: linear-gradient(#888, #555);
	border: 0 none;
	border-radius: 3px;
	text-shadow: 0 -1px 0 #000;
	box-shadow: 0 1px 0 #666, 0 5px 0 #444, 0 6px 6px rgba(0,0,0,0.6);
	cursor: pointer;

Nothing too complicated. I’ve used a fairly generic gray color throughout, but you can apply whatever colors you need. Note also that a background-color has been defined for browsers which do not support linear-gradients. I’ve also set the cursor to pointer — I’ve never understood why browsers don’t do that by default?

The box-shadow is the most interesting property: box-shadow: 0 1px 0 #666, 0 5px 0 #444, 0 6px 6px rgba(0,0,0,0.6). This defines the edge of the button, a 5px “depth” and a light shadow around it.

We complete the block by stating we want transition effects applied to every style when a hover or focus event occurs. Prefixless and -webkit alternatives are required:

-webkit-transition: all 150ms ease;
	transition: all 150ms ease;

In the next block, we’ll define the hover and focus styles. This defines a pulsate animation which makes the text glow:

button:hover, button:focus
	-webkit-animation: pulsate 1.2s linear infinite;
	animation: pulsate 1.2s linear infinite;

@-webkit-keyframes pulsate
	0%   { color: #ddd; text-shadow: 0 -1px 0 #000; }
	50%  { color: #fff; text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; }
	100% { color: #ddd; text-shadow: 0 -1px 0 #000; }
@keyframes pulsate
	0%   { color: #ddd; text-shadow: 0 -1px 0 #000; }
	50%  { color: #fff; text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; }
	100% { color: #ddd; text-shadow: 0 -1px 0 #000; }

Firefox, Chrome and Safari do not require the 0% and 100% definitions but IE10 fails to animate text-shadows if we don’t use them. Sounds like a browser bug to me — make a mental note of that one.

Finally, we set the button active state:

	color: #fff;
	text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff;
	box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9);
	-webkit-transform: translateY(3px);
	transform: translateY(3px);
	-webkit-animation: none;
	animation: none;

This performs a number of actions:

  • the animation is switched off and the text is set to it’s ‘fully glowed’ state
  • the button is moved down 3 pixels using translateY
  • the box-shadow which defines the button depth is changed to 0 2px 0 #444. It’s therefore been reduced from 5px to 2px, but the 3px translation makes it appear that the button has sunk into the page.
  • the outer shadow is also reduced to give the impression the button is lower.

Our button is now complete:

better HTML button with CSS3

View the button demonstration…

The code works as expected in all modern browsers. The only issue I discovered was in Opera — it plays the pulsate animation just once? But they’re switching to WebKit soon, so let’s not worry too much…

Please use the code however you like!

  • Dzre

    Bug report: if the user happens to click too high on the button, the button will be pressed down but the action is not triggered. This is a lot more common than it would seem and very annoying and confusing when it happens.

    • The demonstration does not trigger an action? Have you used it somewhere which demonstrates the issue? Is it occurring in all browsers?

      • Dzre

        I attached a click event to the button on your cssdeck example and it didn’t fire if I clicked too high. Same thing if you use it as a submit button. I only tested it on chrome with this button, but I’m quite confident it fails on all browsers, because I’ve come across this problem before on pretty much all similar buttons, and tested it on all browsers back then.

        I have made some not so pretty workarounds for it before but I’m interested in alternative solutions, that’s why I commented here :)

        • Stephen Ford

          Any progress on this issue?

    • Jeroen

      I couldn’t reproduce: on Win7/FF19: If you mouse-down on the very top of the button, it moves down (and your mouse is not over the button anymore) So if you release, the browser noticed and for a split second the cursor is normal mouse instead of pointer. After the button moves back, mouse becomes pointer again and animation resumes.
      On Win7/Chrome: Samething happens on mousedown. uppon release, the browser does not detect your hovering the button again (after it moved back) and the mouse is default arrow, no button-text annimation. This is restored after you move your mouse.
      So I added an action in this jsFiddle. It’s not triggered if the button is moved away (so you actually misclick…) I’ve tested this only on FF

  • Les

    Well, tranisitions, animations, etc should be the task of Javascript (JQuery), not CSS, and all those advocates who think otherwise (typically, designers who have no formal programming experience in other words) can just go and kiss my hairy butt.

    • Admittedly, I used to be uncomfortable with the concept of animation in CSS rather than JavaScript but I’ve come around to the idea. After all, CSS controls appearance — what is animation? Besides, you couldn’t do this in JavaScript without a load of code and graphic animation frames.

      Are you bent over?

    • kartofelek007

      Les. You are wrong.

      .box {visiblity:hidden; opacity:0;} {visibility:visible; opacity:1;}

      then in js i only add class to .box. This is max comfortable, becauseI i do not have to worry about whether .acive must animate slide, or fade or something else.

      And second. Animation in CSS is much better and faster that in JS
      Paul Irish has no formal programming experience? :)

  • Tim

    There is a visible flicker when clicking the button in Safari, Firefox and Chrome. Latest versions of all browsers.

    • Really? It was fine on all Windows browsers? Are you using a Mac?

      • Tim

        Yes, iMac.

      • Strange. I’m surprised it affects all Mac browsers — especially when they’re using different rendering engines. Is it a particularly old machine? Are you running any developer consoles at the same time, such as Firebug or the webkit inspector? Do other Mac users have the same issue?

  • very informative! keep up the good work. thanks for sharing

  • tried it for the text in a hyperlink and it doesn’t work at all in IE8, link is not active

  • Siva

    See this link for preparing Bootstrap Button:
    its easy work in all browser

Get the latest in Front-end, once a week, for free.