Grow your CSS skills. Land your dream job.

BERG Cloud Buttons

Published by Chris Coyier

The buttons at BERG Cloud are pretty sweet looking. It's not a hugely big deal, but the way they have created them uses two separate images (not sprited) and two internal spans. In this tutorial we will recreate them with some modern tricks. We'll use no images (more efficient), less markup (more semantic), and function smoother (no javascript controlling states).

Here are the originals we're trying to recreate:

orig-buttons

And our final result on CodePen:

Simple Markup

A link is a link.

<a href="#" class="button blue">
  Find Out<br>More
</a>

In our case we have "blue" class in addition to "button". This kind of simple thing is worth discussing. For one, in the "real world" I'd have simply the class of button be fully functional on it's own. It would be whatever the most common button style on the site is. Then to handle variations, in the past I've added extra classes, like shown above. The second class would handle overrides. I'd probably write them like:

.button.blue {
   /* overrides */
}

But I'm starting to think that's not so great. That button has twice the specificity of the original button and uses a class that has no stand-alone value. Lately I've seen several code bases that use single class names for variations, like "button-blue" being the sole class name and having that do all the work. Perfectly do-able in regular CSS, but extra easy in Sass with @extend. For instance:

.button {
   /* Styles for basic button */
}

.button-blue {
   @extend .button;
   /* overrides */
}

Super clean authoring, non-bloated output, and a single class name of equal specificity as any other button. I'm on board with that.

Holy box-shadow!

The meat of what is going on here is layer upon layer of box-shadow on an inline-block link. If you create a box shadow with 1px of offset in two directions and zero blur, you can create essentially a 1px border off two edges of a box. If you do that again only with 2px of offset in the same directions, you start to form a little diagonal line and give the the box some faux depth. If you do it six times, you have a pretty hefty looking button.

Now if you double it up and alternate colors (left offset gets one shade, bottom offset gets another shade) you can simulate a bit of lighting.

.button {
    box-shadow:
      /* Left Side */      /* Right Side */
      -1px 0px 1px #6fadcb, 0px 1px 1px #54809d,
      -2px 1px 1px #6fadcb, -1px 2px 1px #54809d,
      -3px 2px 1px #6fadcb, -2px 3px 1px #54809d;
      /* etc */
}

Using the same technique, but reversing the direction and starting where you stopped the edges, you can do it with a really soft transparent black to make a literal-looking shadow. Toss an inset shadow on there too with a soft transparent white, and it drives home the illusion that light is hitting the button.

.button {
  box-shadow:
    
    /* Sides */
    -1px 0px 1px #6fadcb, 0px 1px 1px #54809d,
    -2px 1px 1px #6fadcb, -1px 2px 1px #54809d,
    -3px 2px 1px #6fadcb, -2px 3px 1px #54809d,
    /* etc */

    /* Shadow */
    -6px 7px  0 rgba(0, 0, 0, 0.05),
    -5px 8px  0 rgba(0, 0, 0, 0.05),
    -3px 9px  0 rgba(0, 0, 0, 0.04),
    /* etc */
  
    /* Light & Top Edge */
    inset 0 4px 5px -2px rgba(255, 255, 255, 0.5),
    inset 0 1px 0 0 rgba(0, 0, 0, 0.3);

}

I'm sure ya'll can imagine Sass helping out with this in clever ways. Calculating shades, loops, variables for colors, tighter syntax, etc.

More CSS!

The face of the button is a very subtle gradient (again helping with the lighting). No problem there. Probably best to apply the gradient to the base class of button and then override it for variations.

.button {
  /* ...shadows ... */

 background: linear-gradient(#a2d3e9, #7abedf);
}

The text inside the buttons has a bit of shadow to it. Easy cheesy with text-shadow. Just make sure the shadow makes sense for the background. We made two variations of the button and we essentially need to override every bit of coloring on the yellow version since it's dark-on-light instead of light-on-dark.

.button {
  /* ...shadows ... */
  /* ...background ... */

 text-shadow: -2px 2px 0 rgba(0, 0, 0, 0.2);
}

There is of course a bunch of other CSS properties on these buttons that you'd except. Like turning off underlines, text coloring, setting up spacing, and other obvious stuff.

Interaction

In the original, when you click or tap the button JavaScript applies an "active" class which triggers the "pressing" animation. It's a little wanky since the buttons can get stuck in that state. Try clicking the button and mousing away and letting go. The button stays pressed. Solvable if they did things like removing the active class on mouseleave, but alas. We don't have to worry about that, since we can trigger the pressed state on :active, in which we get all the interactivity specifics automatically.

Our interaction will be the same pressing animation in which:

  1. All box-shadow is removed, making button look flat to the surface
  2. Button is moved to position where it looks like the surface is

Pretty easy.

.button {

  /* ... styling stuff ... */

  /* interaction */
  transition: all 0.1s linear;
  transform: translateZ(0);
}
.button:active {
  box-shadow: none;
  transform: translate3d(-6px, 6px, 0);
}

The transition does the bulk of the work. With that applied, the box-shadow will slowly slink away rather than instantly disappear. While that is happening, we'll move the button. We could relatively position the button and nudge it around with top/left values. The problem with that is that:

  1. It confuses the issue of positioning and interactivity. If the button were to be absolutely positioned, it would get weird quick. There is no top: where-its-at-plus-10;
  2. It's not as performant as moving via translate.

The second thing to note is the use of 3D transforms. We're using 3D transform properties but we're not actually doing any 3D transforms. We're doing that because:

  1. It'll kick on the ol' GPU and get smoother performance.
  2. It will crap-up (thin out) the text in the default state of the button, so it's not a suprise when the transition starts.

WebKit has that nasty (I'd call it a bug) where text being transformed/transitioned looks all thin and crappy. I've attempted to deal with this before unsatisfactorily. My friend Koop once summed it up pretty well in saying "You just decide which is crappier." Option A) Having the text crapped-up all the time so that there is no awkward screen flashes and shifts in text weight. Option B) Let it be. The text will look good by default but you'll get flashing and thinning when the button is in transition or gets transformed.

It's rather dramatic on these buttons.

thick
Without transform (thick, normal)
thin
With transform (thin)

In this rare case, I like the thinned out version anyway, so best of both worlds.

The Arrow

In my first attempt at creating these buttons, I created the arrow with two spans. It worked great, but not much of a semantic improvement from the original. Neil Kinnish forked my Pen and make the arrows with pseudo elements, meaning we're back at just using a happy anchor link only. I since updated my Pen with his code. You'd think my mind would go right to pseudo elements since I used to do a talk exclusively on them and I've long touted their amazingness. I know that in WebKit you still can't transition a pseudo element, but I was under the impression you couldn't transform them either. Either I've always been wrong or that's a new-ish ability.

The trick is to use the two freebie elements to create a little black rectangle absolutely positioned on the right of the button (there will always be space over there thanks to padding). One little black rectangle is rotated 45 degrees (via transform) and the other -45 degrees. When they were nested spans, setting a filter: drop-shadow() made the most sense (apply just to top span), but now with pseudo elements box-shadow on both works just as well.

.button:after, .button:before
    position: absolute;
    content: "";
    right: 15px;
    top: 14px;
    width: 6px;
    height: 18px;
    background: white;
    transform: rotate(-45deg);
    display: block;
    z-index: 2;
}
.button:before {
    height: 14px;
    top: 26px;
    right: 16px;
    z-index: 3; /* on top */
    transform: rotate(-137deg); /* hey, it works */
    filter: drop-shadow(0 -2px 0 rgba(0, 0, 0, 0.15)); 
}
.button:After {
   filter: drop-shadow(-2px 0 0 rgba((0, 0, 0, 0.2)); 
} 

Again you'd have to adjust all the coloration for different button variations. But the positioning would be the same.

Done

Here's the final again on CodePen:

Also: OMG Tiny Printer so cute #want.

Comments

  1. Joe
    Permalink to comment#

    As expected the click effect doesn’t happen on mobile but the button is still totally usable – looks like a nice technique.

  2. Very cool, I love the style of these buttons! I may have to try this on my next project.

  3. Permalink to comment#

    These are nice. A fresh style moving away from the Apple look.

  4. MR.OMAR HADIDY
    Permalink to comment#

    WRITE LESS DO MORE WITHOUT jQuery >>nice code

  5. OH MY GOD!!! The animation upon click gave me goosebumps :) I was so impressed that I immediately placed it on my web-site. Really cool. Thanks

  6. Dave^
    Permalink to comment#

    Hi Chris – great run through.

    I was wondering about your specific(!) reasons for not liking the fact that .button.blue has twice the specificity. What’s the downside you’re envisaging to that? I mean, you wanted .button.blue to be your variants on .button, so the fact it’s more specific is good, isn’t it?

    Also, I really want a little printer.

  7. Benjamin Knight
    Permalink to comment#

    Cool effect but this is UI decoration not UI design in my opinion. Moving through a UI with these buttons would feel slow and heavy.

  8. LJ
    Permalink to comment#

    Pretty oogy on IE9 for now/doesn’t degrade very well as is. Maybe 10 will like it more? =)

    *PS not trolling, I appreciate the demo for the direction we’re moving in with CSS3, just don’t see a practical app just yet. Getting there, for sure!

  9. I managed to do pretty much the same arrow with one single pseudo-element using a rotated square and 2 borders. Demo here: http://jsfiddle.net/vVeqr/

    By the way Chris, what’s this -137deg? It looks like a magic number to me! :P

    • Exactly my thought when I saw it, although I would have made a smaller square with an outer shadow (less semantic).

    • David Leitch
      Permalink to comment#

      The only problem I see with this is that it wouldn’t be as easy to do any extra presentation changes on the arrow. For instance, I can’t see an easy way to to create an inset shadow on the arrow, as it has in Chris’ example.

    • David Leitch
      Permalink to comment#

      Wait, his example doesn’t have an inset shadow. Eyes playing tricks on me, whoops!

    • You could actually add an inset shadow, by offsetting the a darker shadow that would be behind the arrow, thus making a shadow.

  10. Nice use of box-shadows.

    Seeing the border that they had, I added this line:

    border: 1px solid rgba(black, 0.3);

    to the button class, which adds some nice definition to the main edge.

    http://codepen.io/Druid-of-Luhn/pen/fiDlE

    • Permalink to comment#

      Nice touch, lowered the opacity a tad bit more.
      Try adding a nice subtle border radius of 3px:

      border-radius: 3px;

      ;)

      Ah well, possibilities are endless though

  11. John
    Permalink to comment#

    I could sit here and click those buttons all day!

  12. Permalink to comment#

    Very nice feel and look to these, muchas gracias for the code!

  13. reda
    Permalink to comment#

    Hi,

    Great effet,

    The effect is not the same between opera and chrome. Chrome is OK but opera 12.10, when clicked, the button is not pressed but rather the shadow that move up to the surface.

    Sure I will use it in the next project :)

  14. Adrián Salgado
    Permalink to comment#

    Does anyone else see the letters blur out when moving or is that just me?, that doesn’t seem to happen on the BERG one.

    Is there a way to make the movement of the letters a lot more smoother?

  15. alwan
    Permalink to comment#

    very cool .. and works perfectly in google chrome

  16. Neato, I just did this on this site, but not near as refined.
    The box shadow makes a ton of difference for sure.

  17. triune
    Permalink to comment#

    Any chance you could just put up the normal css for these buttons? I’ve just about to give up with converting the SCSS into CSS. Its killing me! I just want to drop some styles onto a site! (O_o)

  18. Such a decent post and definitely served to clear my head a bit

  19. Great post! Think I’ll try the BERG style concept on a project I’m working on. Seeing SCSS in action was useful too. I need to learn it.

  20. Nate
    Permalink to comment#

    Chris, thank you so much for this. You never cease to amaze me. Heads up: in the code example in the interaction section

    translate3d(-6px, 6px, 0));

    should be

    translate3d(-6px, 6px, 0);

  21. gussoner
    Permalink to comment#

    Have you see them in IE 8 or 9 ?

  22. Michael C.
    Permalink to comment#

    I gotta confess… every few months I find myself coming back to this page just to click those buttons some more. Sooo buttery smooth… ;)

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".