Thursday, March 14, 2013

Animated Anchor and Button with CSS3

Agenda

HTML5 and CSS3 features give us great opportunity to create rich animated user interfaces comparable with desktop versions. Here I will explain how to create rich UI button with custom HTML content and CSS3 tools only, without using javascript and images.

I am not going to explain details of an appropriate CSS3 style, but a live demo will be attached to every step, so you will be able to see how it affects different browsers.

Layout

Let's specify a general .button class, with some default styles. The class will be suitable for different kinds of tags to make them look like rich UI button.

.button {
    border: none;
    outline: none;
    text-decoration: none;
    display: inline-block;
    padding: 10px;
    margin: 10px;
    font-size: 2em;
    color: black;
    background-color: lightgray;
    cursor: pointer;
}

And apply this class to an anchor:

<a class="button" href="javascript:void(0)">Button</a>

So our new button will look and behave like: Button

Looks ugly, but let's go on:)

Transform effect on :active selector

Let's set a transform effect on button's :active selector to decrease button size on mouse down:

.button:active {
    transform: scale(0.9, 0.9);
    -o-transform: scale(0.9, 0.9);
    -ms-transform: scale(0.9, 0.9);
    -moz-transform: scale(0.9, 0.9);
    -webkit-transform: scale(0.9, 0.9);    
}

So here it is: Button

But there is an issue with css transform for content elements. It's described in details here: StackOverflow - Non-clickable area on transforming anchor. Shortly, there is an area that doesn't raise a click on the border of content of the button.

If we select the content, we can see the border, specified on the picture: click on the border of text node doesn't fire click event

The area is very small, but it can be very annoying to your user when once in a while he clicks the button and nothing happens.

To fix the issue, we should override clickable area by some streched stub which will intercept the :active state from the whole button area. To avoid inserting additional markup it can be reached by :before selector:

.button {
    position: relative;
}

.button:before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
 }

New button without non-clickable area: Button

Disallow button's content selection

In some cases it's not even needed, but considering that you cannot select content on a normal button, we should do it:

.button {
    user-select: none;
    -o-user-select: none;
    -ms-user-select: none;
    -moz-user-select: none;
    -webkit-user-select: none;
}

You cannot select the content by dragging the mouse: Button

Background color transition effects

To make the button smoothly change its background on mouse hover:

.button {
    transition: background 0.3s ease;
    -o-transition: background 0.3s ease;
    -ms-transition: background 0.3s ease;
    -moz-transition: background 0.3s ease;
    -webkit-transition: background 0.3s ease;
}

.button:hover {
    background-color: darkgray;
}

.button:active {
    background-color: gray;
}

Looks more sexy:) Button

Actually you can add even more transition effects for other styles, or customize transitions for different states.

Reset predefined appearance

Browsers can have predefined appearance while rendering html elements. Since we are going to apply the .button class to different kinds of tags, and to control the element states ourselves, we should do it to prevent unpredictable behavior:

.button {
    appearance: none;
    -o-appearance: none;
    -ms-appearance: none;
    -moz-appearance: none;
    -webkit-appearance: none;
}

There are 2 plain <button> elements, which may look different in different browsers, the second one resets its appearance:

Shadow effects

There is a simple outer shadow effect, which makes it 3D feel:

.button {
    box-shadow: 3px 3px 3px #888888;
    -o-box-shadow: 3px 3px 3px #888888;
    -moz-box-shadow: 3px 3px 3px #888888;
    -webkit-box-shadow: 3px 3px 3px #888888;
}

.button:active {
    box-shadow: none;
    -o-box-shadow: none;
    -moz-box-shadow: none;
    -webkit-box-shadow: none;
}

And the button: Button

I also like inset shadow, which looks pretty attractive:

.button {
    box-shadow: 0px 0px 6px 3px #888888 inset;
    -o-box-shadow: 0px 0px 6px 3px #888888 inset;
    -moz-box-shadow: 0px 0px 6px 3px #888888 inset;
    -webkit-box-shadow: 0px 0px 6px 3px #888888 inset;
}

.button:active {
    box-shadow: 0px 0px 2px 2px #888888 inset;
    -o-box-shadow: 0px 0px 2px 2px #888888 inset;
    -moz-box-shadow: 0px 0px 2px 2px #888888 inset;
    -webkit-box-shadow: 0px 0px 2px 2px #888888 inset;
}

Button

Border radius

Just very simple rounded border:

.button {
    border-radius: 5px;
    -o-border-radius: 5px;
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
}

The buttons with border radius:
Button
Button

Gradients

Linear gradient to make it a salient feel. The issue is that gradient transitions are not supported, so we make a trick with background-position style:

.button {
    background-image: linear-gradient(lightgray 0%, gray 50%, lightgray 100%);
    background-image: -o-linear-gradient(lightgray 0%, gray 50%, lightgray 100%);
    background-image: -moz-linear-gradient(lightgray 0%, gray 50%, lightgray 100%);
    background-image: -webkit-linear-gradient(lightgray 0%, gray 50%, lightgray 100%);
}

.button:hover {
    background-position: 0 28px;
}

.button:active {
    background-image: linear-gradient(gray 0%, lightgray 50%, gray 100%);
    background-image: -o-linear-gradient(gray 0%, lightgray 50%, gray 100%);
    background-image: -moz-linear-gradient(gray 0%, lightgray 50%, gray 100%);
    background-image: -webkit-linear-gradient(gray 0%, lightgray 50%, gray 100%);
}

The buttons with gradients:
Button
Button

Summarized styles

.button {
    border: none;
    outline: none;
    text-decoration: none;
    display: inline-block;
    padding: 10px;
    margin: 10px;
    font-size: 2em;
    color: black;
    background-color: lightgray;
    cursor: pointer;

    position: relative;

    user-select: none;
    -o-user-select: none;
    -ms-user-select: none;
    -moz-user-select: none;
    -webkit-user-select: none;

    transition: background 0.3s ease;
    -o-transition: background 0.3s ease;
    -ms-transition: background 0.3s ease;
    -moz-transition: background 0.3s ease;
    -webkit-transition: background 0.3s ease;

    appearance: none;
    -o-appearance: none;
    -ms-appearance: none;
    -moz-appearance: none;
    -webkit-appearance: none;

    box-shadow: 3px 3px 3px #888888;
    -o-box-shadow: 3px 3px 3px #888888;
    -moz-box-shadow: 3px 3px 3px #888888;
    -webkit-box-shadow: 3px 3px 6px #888888;

    border-radius: 5px;
    -o-border-radius: 5px;
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;

    background-image: linear-gradient(lightgray 0%, gray 50%, lightgray 100%);
    background-image: -o-linear-gradient(lightgray 0%, gray 50%, lightgray 100%);
    background-image: -moz-linear-gradient(lightgray 0%, gray 50%, lightgray 100%);
    background-image: -webkit-linear-gradient(lightgray 0%, gray 50%, lightgray 100%);
}

.button:before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
}

.button:hover {
    background-color: darkgray;

    background-position: 0 28px;
}

.button:active {
    transform: scale(0.9, 0.9);
    -o-transform: scale(0.9, 0.9);
    -ms-transform: scale(0.9, 0.9);
    -moz-transform: scale(0.9, 0.9);
    -webkit-transform: scale(0.9, 0.9);

    background-color: gray;

    box-shadow: none;
    -o-box-shadow: none;
    -moz-box-shadow: none;
    -webkit-box-shadow: none;

    background-image: linear-gradient(gray 0%, lightgray 50%, gray 100%);
    background-image: -o-linear-gradient(gray 0%, lightgray 50%, gray 100%);
    background-image: -moz-linear-gradient(gray 0%, lightgray 50%, gray 100%);
    background-image: -webkit-linear-gradient(gray 0%, lightgray 50%, gray 100%);
}

And how different kind of elements look:

<a>: Anchor

<button>:

<input type="button">:

<button> with custom content:

Browsers compatibility

As the article is about HTML5 and CSS3, it basically works in all desktop browsers which support these technologies. I can enumerate what I have tested:

IE8 and earlier - no support. I believe people who use IE8 should not expect the best browsing experience. Therefore I didn't write -ms-filter styles as it's not about CSS3.

IE9 - user-select doesn't work. It also doesn't support appearance, transitions and gradients. But gradients can be reached by IE filter property.

IE10 - it doesn't support appearance and user-select. Even though it supports -ms-user-select property, but it doesn't seem to be working. We should probably wait for SP1;)

Opera (12.10) - user-select and appearance doesn't work. But it's possible to prevent content selection by using unselectable="on" inline attribute.

Mozilla Firefox (19.0.2) - everything seems to be working.

Google Chrome (25.0.1364.172 m) and Safari (5.1.7) - everything is supported.

Mobile browsers - still need to be investigated.

Post Scriptum

Fell free to use this code in your projects. I'm sure designers can create more attractive styles, and this can be a start point for them. I wish I had some designer skills, but I don't;)

I also appreciate any constructive criticism, so please comment if you see any mistake or inaccuracy here.

No comments:

Post a Comment