Creating Awesome Buttons in CSS

Click the buttons

If you count just the lines of code, CSS is probably the language I type the most in. Yet, it is also the language I share the least about on this website. So this is an attempt to fix that. I created a set of three state buttons that you can customize to your liking.

I rarely use bootstrap, material design or any of popular CSS solutions that are currently available. The reason is simply that I would rather write the CSS myself instead of including an entire library just to make use of one or two features. If you have the same mindset, well without further due here they are.

Find the Buttons on Github.

The buttons themselves are very simple, they use 3 states to create this effect. They have a small one pixel wide bottom border to give the impression of nudging up (state 1). When you hover it grows by another pixel and lighten up (state 2). When you click it the bottom border goes down, it darkens, giving the impression of being pressed down (state 3).

If you also noticed, when you click there is a small effect similar to what you would see on Google's material design. This effect is possible without JavaScript, however I had to use Javascript because it appears right under the mouse button no matter where you click. If you don't have javascript enable the buttons still work fine but this is a nice little animation to have. Let me show you how it is done.

I created the animation using a circle that grows from 30 pixels to 150 pixels. Note that a circle is just a square with border radius set to 50%. And the circle is not a separate element but just the ::after css pseudo selector:

.btn.animate::after {
    position: absolute;
    content:" ";
    top:var(--coord-y);
    left:var(--coord-x);
    transform: translateX(-50%) translateY(-50%);
    background-color:rgba(0,0,0,.1);
    display:block;
    border-radius: 50%;
    animation: btn-click ease-out .5s normal;
}

Two things to note here, one is the animation btn-click which I defined using the @keyframes directive. (I'll post the full code below) And the second is the function var() and it's parameter --coord-x and --coord-y These are CSS3 features. Because I cannot determine the location of the mouse click using CSS, I used Javascript to find the location and then set it as a custom css style property --coord-y (note that you can use any name). The top property will read whatever the value of those custom properties I set on the element.

btn.setAttribute("style","--coord-x: "+x+"px;--coord-y: "+y+"px;");

Once I have set the position of the mouse I can trigger the animation by adding the class animate to the button. I have set the animation to last 500 milliseconds so right away I set a timer to remove the class name after 500ms.

setTimeout(function(){
    self.classList.remove("animate");
},400);

And just like that we have ourselves realistic and interactive CSS buttons with minimal amount of code.

The code:

CSS

@keyframes btn-click {
    from {
        width:30px;
        height:30px;
        opacity:1;
    }
    to {
        width:150px;
        height:150px;
        opacity: 0;
    }
}
.btn {
    position: relative;
    top: 0;
    border: none;
    padding: 12px 16px;
    background-color: #d0d0d0;
    color: #111;
    font-weight: bold;
    border-radius: 4px;
    box-shadow: 0 2px 2px rgba(0,0,0,.16);
    border-bottom: 1px solid #aaa;
    outline: none;
    transition: background-color .2s, box-shadow .2s;
    overflow:hidden;
    cursor:pointer;
    display:inline-block;
}
.btn.animate::after {
    position: absolute;
    content:" ";
    top:var(--coord-y);
    left:var(--coord-x);
    transform: translateX(-50%) translateY(-50%);
    background-color:rgba(0,0,0,.1);
    display:block;
    border-radius: 50%;
    animation: btn-click ease-out .5s normal;
}
.btn:hover {
    top:-1px;
    background-color:#dadada;
    border-bottom-width: 2px;
    box-shadow: 0px 2px 4px rgba(0,0,0,.16);
}
.btn:active {
    top:1px;
    background-color: #cdcdcd;
    box-shadow: 0px 1px 1px rgba(0,0,0,.16);
    border-bottom-width: 0;
}
.btn.primary {
    background-color:#2980b9;
    color:#fff;
    border-bottom-color: #1e608c;
}
.btn.primary:hover {
    background-color:#3498db;
}
.btn.primary:active {
    background-color:#2672a5;
}
.btn.danger {
    background-color:#c0392b;
    color:#fff;
    border-bottom-color: #9e2f23;
}
.btn.danger:hover {
    background-color:#de4434;
}
.btn.danger:active {
    background-color:#a93226;
}
.btn.warning {
    background-color:#f1c40f;
    color:#fff;
    border-bottom-color: #caa40d;
}
.btn.warning:hover {
    background-color:#ffd323;
}
.btn.warning:active {
    background-color:#deb40d;
}
.btn[disabled]{
    color:#999;
    background-color: #d0d0d0;
    border-bottom-color: #aaa;
}
.btn[disabled]:hover {
    top:0;
    background-color: #d0d0d0;
    border-bottom-width: 1px;
    box-shadow: 0 2px 2px rgba(0,0,0,.16);
}

JavaScript

var Buttons = {
    init:function(){
        var btns = document.getElementsByTagName("button"),
        l = btns.length,i = 0;
        function onMouseDown(event){
            var scrollTop = document.body.scrollTop,
                position = Buttons.getPosition(this),
                x = event.pageX - position.left,
                y =  event.pageY - position.top - scrollTop;
            Buttons.onclick(this,x,y);
        }
        for(;i<l;i++){
            btns[i].onmousedown = onMouseDown;
        }
    },
    onclick: function(self,x,y){
        self.setAttribute("style","--coord-x: "+x+"px;--coord-y: "+y+"px;");
        self.classList.add("animate");
        setTimeout(function(){
            self.classList.remove("animate");
        },400);
    },
    getPosition:function ( el ) {
        var x = 0;
        var y = 0;
        while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
            x += el.offsetLeft - el.scrollLeft;
            y += el.offsetTop - el.scrollTop;
            el = el.offsetParent;
        }
        return { top: y, left: x };
    }
};
Buttons.init();

Comments

There are no comments added yet.

Let's hear your thoughts

For my eyes only