Monday, January 25, 2010

The New Hotness: Using CSS3 Visual Effects

Previously in this series on CSS3, we talked not only about how to create scalable and compelling buttons but about how to effectively use new CSS3 properties to speed up development and quickly create rich page elements. In this final article of the series, we'll really get into it and use CSS3 visual effects to push the envelope.

Not everything in this article is practical, or even bug-free, but it's a fun primer on what's in the pipeline for Web design. To get the most from these examples, you'll have to use Safari 4 or Chrome. (Firefox 3.5 can handle most of it, but not everything: WebKit is further along than Gecko in its tentative CSS support.) We'll show you how to create impressive image galleries, build animated music players and overlay images like a pro. All set? Let's rock.

[Offtopic: by the way, do you know the Smashing Network has its own Smashing Network RSS Feed? Only excerpts are displayed in the feed.]

Create A Polaroid Image Gallery

Hotness-1 in The New Hotness: Using CSS3 Visual Effects

We always try to stay pretty active with our Flickr feed; our chief instigator Bryan does a great job of capturing the day-to-day and special events and even some of our old work. We wanted a great way to show off these photos, so we turned to CSS3 to create a compelling, fun image gallery. The Polaroid style is pretty common, but we wanted not only to make it dead-simple to create the gallery in the markup but also to add styles that would have required Javascript just a year or two ago.

The Polaroid Gallery Markup

First off, we created very simple markup for the gallery, something that would be easy to generate automatically using the Flickr API. The markup for the entire gallery looks like this:

<ul class="polaroids"> 	<li> 	   <a href="" title="Roeland!"> 	   	<img src="image-01.jpg" width="250" height="200" alt="Roeland!" /> 	   </a> 	</li> 	<li> 	   <a href="" title="Discussion"> 	   	<img src="image-02.jpg" width="250" height="200" alt="Discussion" /> 	   </a> 	</li> </ul> 

We'll be using the title element in a minute.

The Base Style and Labels

Our next step was to create the simple Polaroid look. We placed our image inside an anchor with a white background and scaled the image container. This gave us space for the image labels, which we created using little-known CSS tricks: :after and content: attr.

ul.polaroids a:after { 	content: attr(title); } 

What we're doing here is telling the browser that after it renders the given anchor content, add another piece of content. We then generate that piece of content with the content: attr(title) element, which pulls a specific attribute from the element, in this case the title attribute. Using alt would make more sense, but neither Safari nor Firefox has implemented it for the content element.

The snippet above tells the browser to take the title attribute and render it immediately after the content. Note that the title attribute will be rendered within the anchor, which is exactly what we want. We would have liked to have used the alt attribute, but Safari and Firefox do not support the use of content with it.

Our styling of the anchor element takes care of the formatting of the title attribute as well, and we've now placed the image title attribute below it so that we don't have to replicate that content in the markup.

Scattering the Pictures

A handful of Polaroids would never be in a perfect grid; they'd be scattered over the table. We compromised by messing up the grid a little bit for each image: a little rotation here, some displacement there. However, we did not want to have to manage that scattering on a per-image basis, so we used another new pseudo-class: nth-child.

/* By default, we tilt all images by -2 degrees */ ul.polaroids a { 	-webkit-transform: rotate(-2deg); 	-moz-transform: rotate(-2deg); }  /* Rotate all even images 2 degrees */ ul.polaroids li:nth-child(even) a { 	-webkit-transform: rotate(2deg); 	-moz-transform: rotate(2deg); }  /* Don't rotate every third image, but offset its position */ ul.polaroids li:nth-child(3n) a { 	-webkit-transform: none; 	-moz-transform: none; 	position: relative; 	top: -5px; } 

These are only a few of the declarations we used; we actually added them for everything up to 11n, but you get the idea. As you can see, nth-child supports a few different arguments, including even, odd and Xn (where X can be any integer). The even and odd declarations are self-explanatory. Xn takes every Xth element and applies a particular style; in this example, every 3rd. Combining this with odd, even and some more Xn declarations means that even though the style won't really be random, it will appear random enough to the average user. You can see the entire set of styles on our demo page.

We're using a new CSS3 property here as well: the CSS transform (shown as -webkit- and -moz-transform). The transform property can take a number of arguments for different kinds of transformations; in this example, we'll be using rotate and scale. You can see the complete (tentative) list in the Safari Visual Effects Guide.

Some Final Animation

Our last touch was to give the image focus on hover; in this case, to enlarge and straighten out. We accomplish this using a -webkit-transition that is activated by the :hover pseudo-class. Check it out:

ul.polaroids a:hover { 	-webkit-transform: scale(1.25); 	-moz-transform: scale(1.25); 	-webkit-transition: -webkit-transform .15s linear; 	position: relative; 	z-index: 5; } 

What's happening here is that we're overriding the existing -webkit-transform to simply scale the image (this eliminates the rotation). The -webkit-transition tells Webkit-based browsers to animate the transform so that the move from one to another is smooth. -webkit-transition is actually extremely versatile, because it can just as easily support color, position (top, right, etc.) and most any other property.

That's how we created our Polaroid gallery. Once you know these new tricks, putting them together is actually pretty easy, and the markup is dead simple.

Polaroid-images-th in The New Hotness: Using CSS3 Visual Effects
See the Live Demo »

We've created a live demo page for this gallery in our Playground, a place for us ZURBians to create small side projects and samples of cool toys. We'll be linking to the Playground examples throughout this article.

Sliding Vinyl Albums With CSS Gradients

Hotness-2 in The New Hotness: Using CSS3 Visual Effects

This example began as a simple experiment with CSS gradients and grew into a pretty detailed investigation not just of gradients but of new background properties and animation. We'll show you how to create advanced gradients with no images and use layered backgrounds for some cool effects.

Writing the Markup

What we've created here is a simple unordered list of albums with slide-out album controls. You could use something like this to present your band's albums or to showcase a series of podcasts or any other kind of audio (or potentially video) media. Each item in the list is an album, with some fairly simple markup:

<div class="album"> 	<a href=""><img src="/playground/sliding-vinyl/muse-the-resistance.jpg" width="500" height="375" alt="Muse: The Resistance" /></a> 	<span class="vinyl"> 		<div></div> 	</span> 	<ul class="actions"> 		<li class="play-pause"><a href=""></a></li> 		<li class="info"><a href=""></a></li> 	</ul> 	<div> 		<h5>Muse</h5> 		<small>The Resistance</small> 	</div> 	<span class="gloss"></span> </div> 

It might look like a few extraneous elements are in there, but we'll be using all of them to render our slide-out record and controller buttons.

Creating the Record

Hotness-4 in The New Hotness: Using CSS3 Visual Effects The real trick here was the album. We challenged ourselves to create the album without using any images at all (we ended up cheating a bit, but we'll get to that). When it slides out, the album looks like the figure on the left: standard black vinyl with a slight shine to it and a couple of control buttons.

You'll notice that the inside edge of the album is a little jagged, and that's because the album isn't an image but rather two layered gradients generated by the browser and set as the background of the same object. This required not only a bit of messing around with the new gradient objects in CSS3 but also another CSS3 trick: multiple backgrounds. Check out the CSS for the record: li div.album span.vinyl div { 	display: block; 	border: solid 1px black; 	width: 112px; 	height: 112px; 	-webkit-border-radius: 59px; 	-moz-border-radius: 59px; 	-webkit-box-shadow: 0 0 6px rgba(0,0,0,.5); 	-webkit-transition: all .25s linear; 	background: 		-webkit-gradient( 			linear, left top, left bottom, 			from(transparent), 			color-stop(0.1, transparent), 			color-stop(0.5, rgba(255,255,255,0.25)), 			color-stop(0.9, transparent), 			to(transparent)), 		-webkit-gradient( 			radial, 56 56, 10, 56 56, 112, 			from(transparent), 			color-stop(0.01, transparent), 			color-stop(0.021, rgba(0,0,0,1)), 			color-stop(0.09, rgba(0,0,0,1)), 			color-stop(0.1, rgba(28,28,28,1)), 			to(rgba(28,28,28,1))); 	border-top: 1px solid rgba(255,255,255,.25); } 

We've omitted some of the positioning and other boring CSS pieces (check out the live demo for the complete markup). We want to focus here on the pieces that are critical to creating the album visually: border-radius and -webkit-gradient.

The simplest part was creating a round object: by setting the border radius to exactly half of the height and width of the object, the browser masks the object to a perfect circle. Watch out, though: unlike in Photoshop, if the border radius is higher than half the object's height or width, the browser might simply ignore the declaration. That said, rounding the object is the easy part; the tricky part is controlling the gradients.

Two gradients are at work on the object: one creates the album itself (complete with the hole in the middle), and the other casts a light across it. We'll start with the shine: li div.album span.vinyl div { 	... 	background: 		-webkit-gradient( 			linear, left top, left bottom, 			from(transparent), 			color-stop(0.1, transparent), 			color-stop(0.5, rgba(255,255,255,0.25)), 			color-stop(0.9, transparent), 			to(transparent)), 			... } 

The shine gradient is a linear gradient from the top-left to bottom-left. We start with transparent so that the gradient fades in, then we shift the gradient to white at the 50% mark (halfway across the album), with 25% opacity. We're using RGBa colors because they allow us to control both the color and opacity in the same declaration.

The album itself is more complicated, and it suffers a bit from early implementation of the radial gradient. li div.album span.vinyl div { 	... 	background: 		..., 		-webkit-gradient( 			radial, 56 56, 10, 56 56, 112, 			from(transparent), 			color-stop(0.01, transparent), 			color-stop(0.021, rgba(0,0,0,1)), 			color-stop(0.09, rgba(0,0,0,1)), 			color-stop(0.1, rgba(28,28,28,1)), 			to(rgba(28,28,28,1))); 	... } 

Radial gradients are just as they sound, and just what you'd expect from gradients in Photoshop. They begin at the center of the object and track across the object in concentric circles. In our case, we wanted to start with transparency, then switch to a solid black, and end up with a very dark gray.

Perhaps the most difficult part of the gradient is declaring its size and position: radial, 56 56, 10, 56 56, 112. We have five pieces of data here: type, starting center, starting diameter, ending center and ending diameter. Here's how they work:

  • Radial is, of course, where we define this as a circular gradient rather than straight (linear) gradient.
  • We begin at 56 56, which is exactly half the height and width of our 112-pixel-tall object. We want the gradient to end with the same center, so we repeat 56 56.
  • The gradient begins with a diameter of 10 pixel.
  • The ending center (56 56) ensures that this is a concentric gradient.
  • 112 is our final diameter, the same width as the object.

The radial implementation was still a bit rough around the edges, so we played around with these values and the color-stop elements to get the effect we wanted. In the future, a more polished implementation won't be quite so trial and error.

Hotness-3 in The New Hotness: Using CSS3 Visual Effects From there, similar to the linear gradient, we created a series of color-stops to go from transparent to black to dark gray. The result of these two backgrounds (separated by a comma—thanks, CSS3) is our shiny record. Again, you'll notice the center is a bit rough, but we're sure future implementations of this new element will be cleaner.

The button controls are simply rounded anchors (using border-radius), with a couple of image glyphs (we told you we cheated a bit). The final touch was to add the animation so that the album would roll out of the sleeve on hover.

Adding in the Final Animation

To achieve the rolling effect, we paired up a position shift and a rotation effect so that, as the object moves to the right, it rotates just the right amount to appear as if it's rolling. Here's what we did: li div.album span.vinyl { 	-webkit-transition: all .25s linear; } li div.album:hover span.vinyl { 	-webkit-transform: translateX(60px); } li div.album:hover span.vinyl div { 	-webkit-transform: rotate(120deg); } 

We're using two transforms, translateX and rotate, on two objects. We use the translate instead of standard positioning because transforms don't impact the DOM—from a layout perspective, the object never really moves, and so we don't have to worry about floats going awry or objects pushing each other around. Transitions also work better on translation transforms than on actual position (left: 20px, etc.) changes.

Gradients have a ways to go, but there are already some cool uses for generated gradients. You can even control them at runtime using transitions or JavaScript, which opens up yet more possibilities.

Sliding-vinyl-th in The New Hotness: Using CSS3 Visual Effects
See the Live Demo »

We've created a live demo page for this gallery in our Playground, so you can see it in action and delve deeper into the source code. Enjoy!

Sweet Overlays With Border-Image

Hotness-7 in The New Hotness: Using CSS3 Visual Effects

This last part is perhaps the most practical. We use it in our feedback tool Notable every day. The border-image property is new but has some really interesting applications. We'll explain how it works and how we're using it in our flagship application.

The Overlay Markup

Overlays in Notable have two parts: the frame and the actual glass overlay. The markup for the overlay is pretty simple, consisting of two sibling DIVs:

<div class="note" id="note1"> 	<div class="border"></div> 	<div class="overlay"></div> 	<span class="black circle note">1<span class="wrap"></span></span> </div> 

When we created these overlays, we had a few goals:

  • They shouldn't overly obscure the content beneath them.
  • They shouldn't affect the hue of the content beneath them.
  • They must look awesome.

To that end, we devised an overlay that would appear as a glass overlay, with a slight shine and a nice, fairly bold frame. For the purposes of this article, we'll focus on the frame, which we created using the new border-image property.

Using Border-Image

The new border-image property is a strange beast: very versatile, but takes a little getting used to. Here's what the border-image declaration for our frame looks like in the CSS:

div.note div.border { 	border: 5px solid #feb515; 	-webkit-border-radius: 3px; 	-moz-border-radius: 3px; 	-webkit-border-image: 		url(/playground/awesome-overlays/border-image.png) 5 5 5 5 stretch; 	-moz-border-image: 		url(/playground/awesome-overlays/border-image.png) 5 5 5 5 stretch; } 

Let's get the easy stuff out of the way. The border element is both required and a fallback: older browsers will still render a usable border for the overlay, but border-image requires a defined border width. While we've been fairly unconcerned with backwards-compatibility in our articles, in this case we needed it (Notable has to work in more than just cutting-edge browsers). This is one of many examples of progressive enhancement (or graceful degradation, if you prefer): older browsers render something usable, just less pretty. The first progressive piece in here is the border-radius, which we've already discussed at length.

The border-image is what we're interested in. Check out the figure to the right; notice the gradient on the frame that goes from top to bottom? It's a simple touch, but adding it to an object that has to scale to many different sizes required that we use this new property. And we're glad we did; learning how to use it opened up new possibilities in our coding repertoire. Let's look at just the border-image code again:

	url(/playground/awesome-overlays/border-image.png) 5 5 5 5 stretch; 

The syntax is the same for WebKit and Gecko(Mox) browsers. The actual declaration is simple. What takes some effort is understanding how to create the image file itself.

Border image takes a single image and slices it into nine pieces, which it then stretches over the object. We've sketched a diagram to explain how this works, and we've blown up the actual border image file for you to compare. Check it out:

Hotness-5 in The New Hotness: Using CSS3 Visual Effects

The browser takes the top-left corner and uses it for the top-left border, and then it stretches the top-middle to cover the entire top of the object, and so on around the image.

Hotness-6 in The New Hotness: Using CSS3 Visual Effects

We created an image with transparent center, because border-image will stretch the center quadrant across the entire object (which seems counterintuitive for a "border" image, but it does make the style a bit more versatile). You'll notice that the actual gradient is present only in quadrants 4 and 6, because those are the only pieces that will be stretched enough for us to see a gradient. The browser actually does a good job of stretching the image as long as it's not too complex, so artifacts aren't really an issue.

The last pieces of the border-image declaration are the size and style: 5 5 5 5 stretch. The repeated 5s determine the size on each side of the object; because we wanted a 5-pixel border, we created an image that was 15 x 15. If we had used a smaller image, the browser would have had to scale the corners as well, and no doubt it would have looked messier. The last property, stretch, dictates how the browser actually handles the pieces of the image. A great (and amusing) intro to the different styles can be found at lrbabe.

Putting It Together

Combining the frame with the glass overlay center (which is a semi-transparent PNG) gives us our frame. Using different border images, we actually created classes for our different colors (red, blue, etc.), while older browsers still get a usable frame without the gradient-edged niceties. This isn't an incredibly complex example, but you can see how useful border-image can be, especially using an alpha-mapped image format such as PNG.

Awesome-overlays-th in The New Hotness: Using CSS3 Visual Effects
See the Live Demo »

We've created a live demo page for this gallery in our Playground so that you can see it in action and delve deeper into the source code. You can also read up on why we created this overlay in our two-part Notable Behind the Scenes blog post: part 1 and part 2.

CSS 3 Is Totally Bad Ass

Right? We hope you've enjoyed this primer on what we can look forward to in the final CSS3 specification. Familiarize yourself with the properties and start using them—just be sure to account for browsers that, sadly, will never support all of these fun new tricks. You can see how we use CSS3 in our work for clients as well as in our own product, Notable. Found a super-awesome way to use these new properties? We'd love to hear about it in the comments!

References and Resources

No comments: