Advertisements

CSS3 Page Flips

Works with Safari, iPhone, iPad.

In previous examples of page navigation we used various types of sliding effects to indicate to the use the transition from one section of a Web app to another. Now we’re going to look at how to indicate section transitions with various types of page flips. In order for these types of flips to work properly, we will need to enclose the items being rotated in a parent element that has the CSS3 property for transform style with a value of preserve-3d. We also need to define the perspective value. The higher the value, the further back from the element the viewer will be, reducing the perspective distortion. Similarly, the smaller the perspective value, the more pronounced the 3D effect will be. For something a little less pronounced I use a value of 1000, and for more pronounced: 500.

First we’re going to look at implementing a typical “card flip” effect. We’re going to implement this with the same basic layout that I created in earlier posts with backward and forwards navigation with a slide effect, except that the links will lead to pages with the various flips and rotations. So, the target of the menu items will contain a parent element, a div tag, which we’ll give a class of “card.” This will have the perspective values that will determine how the rotation looks with perspective distortion. Here’s the markup for the first section. The other section have the same markup, but a different identifier to allow for unique transforms on each section. One thing to bear in mind when implenting these types of transforms, the background of the body tag will show as the elements transition. So, you should make transforms elements’ backgrounds visually different from the background of your body tag so that you can see the elements as they are rotated easier.

<article id="Flip">
	<section class="card">
		<div class="face front">
			 <header>
				 <span class="button back"><span></span>Back</span>
				 <h1>Flip Page Over</h1>
				 <span  id="flipOverButton" class="button">Flip Over</span>
			 </header>
			 <div class="paddingWrapper">
				 <h2>This is the front</h2>
				 <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
		 </div>
		</div>
		<div class="face back">
			<header>
				<h1>Flip Page Over</h1>
				<span  id="flipBackButton" class="button">Flip Back</span>
			</header>
			<div class="paddingWrapper">
				<h2>This is the back</h2>
				<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
			</div>
		</div>
	</section>
</article>

Here’s the CSS we need to get the rotation flip to work:

#Flip {
	-webkit-perspective: 1000;
	position: absolute;
}
article .card {
	min-height: 100%;
	width: 100%;
	position: absolute;
}
.face {
	position: absolute;
	width: 100%;
	height: 100%;
	text-align: center;
	-webkit-backface-visibility: hidden;
	-webkit-transform-style: preserve-3d;
	-webkit-transition: all .5s ease-in-out;
}
#Flip .face.front {
	-webkit-transform: rotateY(0deg);
	background-color: #fffccf;
}
#Flip .face.back {
	-webkit-transform: rotateY(180deg);
	-webkit-box-sizing: border-box;
	background-color: rgba(0,0,0,0.125);
}
#Flip .face.front.flip  {
	-webkit-transform: rotateY(-180deg);	
}
#Flip .face.back.flip {
	-webkit-transform: rotateY(0deg);	
}

We define the perspective property -webkit-perspective: 1000 on the article tag with an id of #Flip. Then we set the initial rotation status of the two divs. For the frontmost div we give it a rotation of 0 degrees, and for the back one, a rotation of 180. We also give the div with the “face” class a CSS3 property to hide its rear side so that it doesn’t show when we rotate it: -webkit-backface-visibility: hidden;. Now, to initiate the flip effect all we have to do is use a little JavaScript to add and remove the “flip” class to the two elements.

Now let’s look at how to implement another flip effect that resembles a left page turn. This is basically the same as the card flip described above, except that we’re going to define the start position of the transform. By default the start position is at the center of the element’s y axis. We’re going to use -webkit-transform-origin: 0% 0%; If we used a value of -webkit-transform-origin: 100% 0%; the result would resemble doors swinging open and closed. Here’s the complete list of styles for the left page turn effect. Notice that we’ve decreased the perspective on the “card” class to 500:

#TurnLeft .card {
	height: 100%;
	-webkit-perspective: 500;
}
#TurnLeft {
	height: 100%;
	background: #cbd2d8;background-image: -webkit-gradient(linear, left top, right top, from(#c5ccd4), color-stop(0.75, #c5ccd4), color-stop(0.75, transparent), to(transparent)); 
	-webkit-background-size: 6px 100%;
}
#TurnLeft .face {
	position: absolute;
	width: 100%;
	height: 100%;
	-webkit-backface-visibility: hidden;
	-webkit-transform-style: preserve-3d;
	-webkit-transition: all .5s ease-in;
	-webkit-box-sizing: border-box
	text-align: center;
}
#TurnLeft .face.front {
	background-color: #fffccf;
	background-image: -webkit-gradient(linear, left top, right top, from(rgba(197, 204, 212, 0.5)), color-stop(0.75, rgba(197, 204, 212, 0.5)), color-stop(0.75, transparent), to(transparent)); 
	-webkit-background-size: 5px 100%;
	-moz-background-size: 7px 100%;
	-webkit-transform-origin: 0% 0%;
	-webkit-transform: rotateY(0deg);
}
#TurnLeft .face.back {
	-webkit-transform: rotateY(180deg);
	-webkit-box-sizing: border-box;
	background-color: rgba(122,137,212,0.25);
	-webkit-transform-origin: 0% 0%;
}
#TurnLeft .face.front.turnLeft  {
	-webkit-transform: rotateY(-180deg);	
}
#TurnLeft .face.back.turnLeft {
	-webkit-transform: rotateY(0deg);	
}

So, once again we can trigger the turn page left effect by adding and removing the “turnLeft” class to the necessary elements.

Lastly, we’ll look at how to do a swing back and down effect. Notice that we’re not rotating on the y-axis but on the x-axis. This causes the elements to rotate on their x-axis:


#SwingBack .card {
	min-height: 100%;
	-webkit-perspective: 500;
}
#SwingBack {
	height: 100%;
	background: #cbd2d8;background-image: -webkit-gradient(linear, left top, right top, from(#c5ccd4), color-stop(0.75, #c5ccd4), color-stop(0.75, transparent), to(transparent)); 
	-webkit-background-size: 6px 100%;
}
#SwingBack .face {
	position: absolute;
	width: 100%;
	height: 100%;
	-webkit-backface-visibility: hidden;
	text-align: center;
	-webkit-transform-style: preserve-3d;
	-webkit-transition: all .5s ease-in-out;
	-webkit-box-sizing: border-box
}
#SwingBack .face.front {
	background-color: #fffccf;
	background-image: -webkit-gradient(linear, left top, right top, from(rgba(197, 204, 212, 0.5)), color-stop(0.75, rgba(197, 204, 212, 0.5)), color-stop(0.75, transparent), to(transparent)); 
	-webkit-background-size: 5px 100%;
	-moz-background-size: 7px 100%;
	-webkit-transform-origin: 0% 0%;
	-webkit-transform: rotateX(0deg);
}

#SwingBack .face.back {
	background-color: #fffccf;
	background-image: -webkit-gradient(linear, left top, right top, from(rgba(197, 204, 212, 0.5)), color-stop(0.75, rgba(197, 204, 212, 0.5)), color-stop(0.75, transparent), to(transparent)); 
	-webkit-background-size: 5px 100%;
	-moz-background-size: 7px 100%;
	-webkit-transform: rotateX(180deg);
	-webkit-box-sizing: border-box;
	-webkit-transform-origin: 0% 100%;
}
#SwingBack .face.front.swingBack  {
	-webkit-transform: rotateX(-180deg);	
}
#SwingBack .face.back.swingBack {
	-webkit-transform: rotateX(0deg);	
}

And here’s a working example online, or you can download the source. Enjoy.

Advertisements

iPad Style Split Layout with Flexible Box Model

Works in Safari, Firefox, Chrome, iPhone, iPad, Android

In a previous post we looked at how CSS3’s flexible box model allowed us to create horizontal alignment of elements. There are several other features in the flexible box model that allow us to create layouts that would require complicated CSS hacks or JavaScript: equal height columns. You know the kind of layout I’m talking about. A header with several columns of material and a footer. You want the columns to always be the same height. You can use a table, but that can introduce a certain nasty kudginess to what you’re trying to achieve. Or you could use background images with nested divs, which is another type of kludginess. Or you could use some JavaScript, which introduces another type of kludginess.

With the flexible box model you can accomplish the equal height columns with a few CSS3 properties. What we’re going to do is show how to put together a typical split-level iPad layout with equal height sections. Here’s what we’re shooting for:
Example Layout with Equal Height Columns using CSS3 Flexible Box Model

To create the layout in this image, we’ll use the following markup:

<!DOCTYPE HTML>
<html lang="en">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width; height=device-height; initial-scale=1.0, maximum-scale=1.0, user-scalable=0;">
	<meta name="apple-mobile-web-app-capable" content="yes">
	<title>Flexible Box Model Layouts</title>
	<style type="text/css">
	</style>
</head>
<body>
<article>
	<header>
		<h1>Home Base</h1>
	</header>
	<section>
		<aside>
			<nav>
				<ul>
					<li><a href="#">Item 1</a></li>
					<li><a href="#">Item 2</a></li>
					<li><a href="#">Item 3</a></li>
					<li><a href="#">Item 4</a></li>
					<li><a href="#">Item 5</a></li>
					<li><a href="#">Item 6</a></li>
					<li><a href="#">Item 7</a></li>
					<li><a href="#">Item 8</a></li>
					<li><a href="#">Item 9</a></li>
				</ul>
			</nav>
		</aside>
		<section>
			<h2>Subtitle Here</h2>
			<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero. </p>

			<p><b>Lorem ipsum dolor sit amet, consectetur adipiscing elit</b>. Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. <i>Lorem ipsum dolor sit amet, consectetur adipiscing elit</i>. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh. Quisque volutpat condimentum velit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. </p>
			
			<p>Nam nec ante. Sed lacinia, urna non tincidunt mattis, tortor neque adipiscing diam, a cursus ipsum ante quis turpis. Nulla facilisi. Ut fringilla. Suspendisse potenti. <b>Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh</b>. Nunc feugiat mi a tellus consequat imperdiet. Vestibulum sapien. Proin quam. Etiam ultrices. Suspendisse in justo eu magna luctus suscipit. Sed lectus. Integer euismod lacus luctus magna. Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. </p>
		</<span class="hiddenGrammarError" pre=""><span class="hiddenGrammarError" pre="">section>
	</section</span></span>>
	<footer>
		<p>Footer stuff here</p>
	</footer>
</article>
</body>
</html>

If you load this markup in a browser, it’ll be pretty plain. Let’s add some basic styling for the header, footer, list menu, etc. First we’ll get rid of the defaults for the body and list elements.

html, body {
	margin: 0px;
	padding: 0px;
	font: normal 14px/16px Helvetica, Sans-serif;
}
ul, li {
	list-style: none;
	padding: 0px;
	margin: 0px;
}

Next, for Firefox, we need to indicate that the HTML5 tags are block elements. The latest versions of Webkit in Safari, iPhone and iPad already recognize these tags and display them properly.

/* for Firefox */
article, header, section, nav, aside, footer {
	display: block;
}
/* end Firefox */

Now we need to add the styles to give are markup a more polished look. We’re going to add CSS3 background gradients, rgba color and border radius:

section > section {
	padding: 10px 20px;
	background-color: #cbd2d8;
	background-image: -webkit-gradient(linear, left top, right top, from(#c5ccd4), color-stop(0.75, #c5ccd4), color-stop(0.75, transparent), to(transparent)); 
	background-image: -moz-linear-gradient(left, #c5ccd4, #c5ccd4 75%, transparent 75%, transparent);
	-webkit-background-size: 5px 100%;
	-moz-background-size: 5px 100%;
}
header {
	background-color: #b0bccd;
	background-image: -webkit-gradient(linear, left top, left bottom, 
		from(#b0bccd), 
		color-stop(0.5, #889bb3), 
		color-stop(0.5, #8195af), 
		to(#6d84a2));
	background-image: -moz-linear-gradient(top, #b0bccd, #889bb3 50%, #8195af 50%, #6d84a2); 
	padding: 10px 10px;
}
header h1 {
	text-align: center;
	font: bold 21px/21px Helvetica, Arial, Sans-serif;
	letter-spacing: -1px;
	text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.5);
	color: #fff;
	margin: 0px;
}
footer {
	background-color: #b0bccd;
	background-image: -webkit-gradient(linear, left top, left bottom, 
		from(#b0bccd), 
		color-stop(0.5, #889bb3), 
		color-stop(0.5, #8195af), 
		to(#6d84a2));
	background-image: -moz-linear-gradient(top, #b0bccd, #889bb3 50%, #8195af 50%, #6d84a2); 
	padding: 1px 0px;
	text-align: center;
	color: #fff;
}
nav li > a {
	text-decoration: none;
	display: block;
	padding: 8px 10px;
}
nav li > a {
	cursor: pointer;
	padding: 8px;
	border-bottom: 1px solid #acacac;
	background-color: #fff;
	font-weight: bold;
	color: rgba(0,0,0,.75);
}
nav li > a:after {
	content: "›";
	font: normal 28px/28px Verdana;
	color: rgba(0,0,0,.5);
	display: block;
	float: right;
	margin-top: -6px;
}
nav li > a:hover {
	background-image: -moz-linear-gradient(top, #4286f5, #194fdb);
	background-image: -webkit-gradient(linear, left top, left bottom, from(#4286f5), to(#194fdb));
	color: #fff;
}
nav li > a:hover:after {
	color: #fff;
}
h2 {
	color: #666;
	text-shadow: 0px -1px 1px #fff;
}
section > p {
	-webkit-border-radius: 10px;
	-moz-border-radius: 10px;
	background-color: rgba(255,255,255,0.85);
	border: solid 1px rgba(0,0,0,0.5);
	padding: 10px;
}

This gives us a layout that looks like this:
Style Layout Without Flexible Box Model Applied

This doesn’t look too bad, except that we want to the list to be on the left, and the content to be on the right. Normally, to achieve that we would need to put the content section before the list and float it right and then float the list left. Then we’d need to clear that float on the footer. That would work visually, except that neither the list nor the content would know about the other’s height. Or we could use relative and absolute position, and basically have the same problem. Or we could use CSS3’s flexible box model and have no problems at all, well, unless you really, really need to support IE, any version. You could use this for the god browsers and use conditional comments to give IE some crappy float arrangement. Honestly, I would to that to IE without thinking about it.

So, if you look at the document markup, we have an article tag with a header tag, section tag and footer tag as its children. The section tag has and aside tag and another section tag as its children. To make those work they way we want we need to give the parent section tag some flex power:

article > section {
	display: -webkit-box;
	-webikit-box-orient: horizontal;
	-webkit-box-align: stretch;
	display: -moz-box;
	-moz-box-orient: horizontal;
	-moz-box-align: stretch;
	-moz-box-lines: multiple;
}

This will give us cause the aside and section tags to align horizontally in their parent and each will have the same height. However in both Webkit and Firefox they both have some other issues with understanding how to deal with the long, wrapping content in the section tag.

Equal Height Columns in Firefox with Width Adjustment ProblemsEqual Height Columns in Safari with Width Adjustment Problems

To fix the problem with the browsers not knowing how to handle the wrapping content we just need to add a flexible box value to the section tag with the content:

section > section {
	-webkit-box-flex: 1;
	-moz-box-flex: 1;
}

What this does is tell the browser that the child section tag’s width should be whatever space is left over from that occupied by the aside tag, it’s sibling in the same container. Since the aside has a defined width of 200 pixels, the browser will give the rest to the section tag. Suddenly our layout is looking and acting the way you’d expect. It will even expanding dynamically as you resize the browser window.

You can try it out live or download the source.

CSS3 Layouts with Horizontal Alignment

Works with Safari, Chrome, Firefox, iPhone, iPad, Android

One of the most common features in modern Web layouts is to have some with a fixed width on one side or on both sides, with the rest of the space taken up dynamically. This is usually accomplished by floating the first item to the right and the second item to the left, or by using relative and absolute position. With CSS3 there is another, better way. No floats, no clearing floats, no positioning. Instead we use the properties of the flexible box model. The magic all starts with a new display property: box. At the moment, you’ll need to use the -moz and -webkit prefixes to implement these features. They have been supported since version 3 of both Firefox and Safari. If the majority of your users are on Firefox, Chrome and Safari, or if you’re targeting the mobile Web, you can go ahead and use these properties today and achieve the kind of layouts that previously required complex CSS frameworks or JavaScript libraries like YUI or Ext.js. The flexible box model allows us to create the equivalent of stack panels and wrap panels with HTML.

Let’s get started. We’ll create and HTML5 document with a header tag. We’ll style the header and style two links inside it to look like buttons:

<!DOCTYPE HTML>
<html lang="en">
<head>
	<meta charset="utf-8">
	<title>Flexible Box Model Example One</title>
	<style type="text/css">
		html, body {
			margin: 0px;
			padding: 0px;
			font: normal 14px/16px Helvetica, Sans-serif;
		}
		header {
			/* The following line is for Firefox */
			width: 100%;
			padding: 5px 0px;
			background-image: -moz-linear-gradient(top, #b0bccd, #889bb3 50%, #8195af 50%, #6d84a2); 
			background-image: -webkit-gradient(linear, left top, left bottom, from(#b0bccd), color-stop(0.5, #889bb3), color-stop(0.5, #8195af), to(#6d84a2));
		}
		.button
		{
			color: #fff;
			text-decoration: none;
			display: block;
			padding: 4px 10px;
			-webkit-border-radius: 5px;
			-moz-border-radius: 5px;
			font: normal 14px/16px Helvetica, Arial, sans-serif;
			cursor: pointer;
		}
		
		.button.black {
			background-image: -webkit-gradient(linear, left top, left bottom, 	
				from(#7d828c),
				color-stop(0.5, #303749), 
				color-stop(0.5, #121a2e), 
				to(#121a2e));
			background-image: -moz-linear-gradient(top, #7d828c, #303749 50%, #121a2e 50%, #121a2e);
			border: solid 1px rgba(79, 79, 79, 0.75);
		}
		.button.black:hover {
			background-image: -webkit-gradient(linear, left top, left bottom, 
				from(#4286f5), 
				color-stop(0.5, #4286f5),
				color-stop(0.5, #194fdb),
				to(#194fdb));
			
			background-image: -moz-linear-gradient(top, #4286f5, #4286f5 50%, #194fdb 50%, #194fdb);
		}
		.button.back {
			margin-left: 20px;
		}
		.button.next {
			margin-right: 20px;
		}
	</style>
</head>
<body>
	<article>
		<header>
			<a class="button black back">Back</a>
			<a class="button black next">Next</a>
		</header>
	</article>
</body>
</html>

This will give us a layout like this:
Initial setup for a header tag that will use the flexible box model

Now we’ll add the magic. All we have to do is give the header tag a definition of display: box, then tell it which orientation to arrange its child elements and how to pack them in. We just need to add these properties to the header tag’s CSS:

display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack:justify;
display: -moz-box;
-moz-box-orient: horizontal;
-moz-box-pack:justify;

By telling the browser to pack the child elements with justification, the browser takes all the space not occupied by the buttons and spreads it out between them. This works as expected in all Webkit based browsers. Unfortunately, although Firefox has implemented every other feature of the flexible box model, they have not as yet implemented the box-pack: justify property. It’s listed on their developer site with a question mark: https://developer.mozilla.org/en/CSS/-moz-box-pack

In the next section of this post I’ll introduce a technique that requires some extra markup that will make this work in Firefox too. For now, here is how this looks in Webkit:
Header tag with buttons aligned at both end using the flexible box model

You can see it in action, or download the document.

Adding Centered Content With Dynamic Width

Now we’ll look at how to add a title into our heading. Since we already have two buttons at either end, we’ll want our title to be centered. We’ll add an h1 tag to the header directly between the two buttons:

<body>
	<article>
		<header>
			<a class="button black back">Back</a>
			<h1>Home Base</h1>
			<a class="button black next">Next</a>
		</header>
	</article>
</body>

We also need to give the h1 some suitable styling. We’re going to make it white with an indented look using a text shadow:

h1 {
	font: bold 21px/21px Helvetica, Arial, Sans-serif;
	text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.5);
	color: #fff;
	margin: 0px;
	-moz-box-flex: 1;
	text-align: center;
}

This gives us the following in Webkit:
Buttons on either end and title centered

Unfortunately, Firefox still needs a couple more lines of code to get it right. Here’s what we have at the moment:
Firefox having problems implementing the flexible box model

There is one more flexible box model property that we can apply to the h1 so that Firefox will display it as we want, that is box-flex. If we give this property a value of one, it tells the browser to have that elements width the equivalent of all left over space from whatever other sibling elements are in the same parent tag. We’ll also need to apply text-alignment to center the text:

h1 {
	font: bold 21px/21px Helvetica, Arial, Sans-serif;
	text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.5);
	color: #fff;
	margin: 0px;
	-moz-box-flex: 1;
	text-align: center;
}

Now Firefox display the header as we want:
h1 { 	font: bold 21px/21px Helvetica, Arial, Sans-serif; 	text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.5); 	color: #fff; 	margin: 0px; 	-moz-box-flex: 1; 	text-align: center; }

You can check out the finished page, or download it.

Update: October 5, 2010

I’ve update these buttons to use subpixel rendering. You can read about the technique here. You can try this out online or download the source code.

Create Back & Next Buttons with CSS3

Works with Safari, iPhone, iPad

Update: March 21, 2011

I’ve update the method of create “Back” and “Next” button without the need for any extra markup, such as the span tag used in post below. Instead I use a CSS technique to create the pointer through only CSS. Read the new post and checkout the new example.

In an earlier post about the iPad’s distinctive dropdown menus I devised a scheme for creating an angular pointer. Now I’m going to use the same technique to create back and next buttons using only CSS3. Unfortunately, Android and Google Chrome do not presently support image masks, so this only works with iPhone, iPad and the Safari desktop browser. If you wish to reproduce this look for Android or Chrome, you’ll need to use image pieces, or be satisfied with simpler button styles. Eventually the CSS3 image mask will be supported on Android and Chrome.

To make the backward/forward pointer on the buttons possible we will need to add some extra markup to the basic button. The markup will look like this:

<a href="#" class="button back black"><span></span>Back</a>
<a href="#" class="button next black"><span></span>Next</a>

You’ll notice the inclusion of the span in the two link buttons above. We’ll be styling these spans to be the left and right pointing part of our buttons. First we’ll start with the basic button styles. We’ll start with the styles for our button class:

.button {
	color: #fff;
	text-decoration: none;
	display: inline-block;
	padding: 4px 10px;
	-webkit-border-radius: 5px;
}

This class gives us some basic properties for all our button styles. I’m using a black class to enhance this with border and background styles. But I could just as easily create some other color scheme using a different class, like blue or gold. So, here’s the style definitions for the black class:

 .button.black {
	 background-image: -webkit-gradient(linear, left top, left bottom, 
		 from(#7d828c),
		 color-stop(0.5, #303749), 
		 color-stop(0.5, #121a2e), 
		 to(#121a2e));
	 border: solid 1px rgba(79, 79, 79, 0.75);
 }

We’ve given the button a border color that is slightly transparent using RGBA values. We also styled the background of the button using a CSS3 gradient. It starts at the top, stop half way and changes to another color, creating a chiseled or beveled look. We’ll also add in a bright blue hover effect for mouse-based systems:

.button.black:hover {
	background-image: -webkit-gradient(linear, left top, left bottom, 
		from(#4286f5), 
		color-stop(0.5, #4286f5),
		color-stop(0.5, #194fdb),
		to(#194fdb));
}

This will give us a button with a hover state as indicated in the image below:
How the basic button class look with the addition of the black class.

As you can see, this creates a fairly slick looking button with a minimum of markup and can easily be adjust for other color schemes. We could also get fancy with the text by beveling it with an offset text shadow or give the button some depth with a box shadow.

Now let’s see how to create the pointer part that we need for back and next buttons. Because we’re using a basic, empty span, we can use the same span for either a back or next button, we just need to flip its styles to achieve the two effects.

Let’s do the back button first. First we need to do a little prep to the parent button. We’ll give it relative positioning because we’ll want to use absolute position on the span to move it into place. We’ll also want to change the default padding from 10px on the left to just 8. This is to balance out the space created by the back pointing part of the button the finished button.

.button.back {
	position: relative;
	padding-left: 5px;
	margin-left: 8px;
}

Now we’ll give the span some formatting styles and a matching background gradient. And, since we plan on rotating the span to the right and clipping off the right half, we only need a border on the left and bottom part of the span. Here’s the styles:

.back.black > span {
       display: block;
       height: 20px;
       width: 20px;
       background-image: -webkit-gradient(linear, left top, left bottom, 
              from(#7d828c),
              color-stop(0.5, #303749), 
              color-stop(0.5, #121a2e), 
              to(#121a2e));
       border-left: solid 1px rgba(79, 79, 79, 0.75);
       border-bottom: solid 1px rgba(79, 79, 79, 0.75);
}

Our styled span now looks like this:
Pointer span with gradient and borders

Since we’re going to rotate the span, we might as well realign the direction of the gradient so that it appears to be vertical. To do that we need to make it flow from left top to right bottom, creating a diagonal dragient:

.back.black > span {
       display: block;
       height: 20px;
       width: 20px;
       background-image: -webkit-gradient(linear, left top, right bottom, 
              from(#7d828c),
              color-stop(0.5, #303749), 
              color-stop(0.5, #121a2e), 
              to(#121a2e));
       border-left: solid 1px rgba(79, 79, 79, 0.75);
       border-bottom: solid 1px rgba(79, 79, 79, 0.75);
}

Now the span looks like this:

Pointer span with angled gradient

Next we’re going to rotate the span using -webkit-transform: rotate.

.back.black > span {
       display: block;
       height: 20px;
       width: 20px;
       background-image: -webkit-gradient(linear, left top, right bottom, 
              from(#7d828c),
              color-stop(0.5, #303749), 
              color-stop(0.5, #121a2e), 
              to(#121a2e));
       border-left: solid 1px rgba(79, 79, 79, 0.75);
       border-bottom: solid 1px rgba(79, 79, 79, 0.75);
      -webkit-transform: rotate(45deg);
}

This give us a rotated span:
Pointer span rotated 45 degrees.

.back.black > span {
       display: block;
       height: 20px;
       width: 20px;
       background-image: -webkit-gradient(linear, left top, right bottom, 
              from(#7d828c),
              color-stop(0.5, #303749), 
              color-stop(0.5, #121a2e), 
              to(#121a2e));
       border-left: solid 1px rgba(79, 79, 79, 0.75);
       border-bottom: solid 1px rgba(79, 79, 79, 0.75);
      -webkit-transform: rotate(45deg);
}

To get rid of the right half we’ll use a CSS3 image mask. But instead of using an actual image, we’ll use a CSS3 gradient. The gradient needs to go from black to transparent. To create the cut off effect we go from solid black halfway, stop, then go the rest of the way with full transparency. The syntax is fairly straightforward: -webkit-mask-image.

.back.black > span {
       display: block;
       height: 20px;
       width: 20px;
       background-image: -webkit-gradient(linear, left top, right bottom, 
              from(#7d828c),
              color-stop(0.5, #303749), 
              color-stop(0.5, #121a2e), 
              to(#121a2e));
       border-left: solid 1px rgba(79, 79, 79, 0.75);
       border-bottom: solid 1px rgba(79, 79, 79, 0.75);
      -webkit-transform: rotate(45deg);
      -webkit-mask-image: -webkit-gradient(linear, left bottom, right top, 
              from(#000000), 
              color-stop(0.5,#000000), 
              color-stop(0.5, transparent), 
              to(transparent));
}

The final, masked span now looks like this:
Masked pointer span

However, if we look at it inside of our regular button, it looks awful because of its rotation:

Since the parent button already has relative positioning, we just need to give the span absolute position with a left and top value to move it into place:

Back button with unpositioned pointer

.back.black > span {
       display: block;
       height: 20px;
       width: 20px;
       background-image: -webkit-gradient(linear, left top, right bottom, 
              from(#7d828c),
              color-stop(0.5, #303749), 
              color-stop(0.5, #121a2e), 
              to(#121a2e));
       border-left: solid 1px rgba(79, 79, 79, 0.75);
       border-bottom: solid 1px rgba(79, 79, 79, 0.75);
      -webkit-transform: rotate(45deg);
      -webkit-mask-image: -webkit-gradient(linear, left bottom, right top, 
              from(#000000), 
              color-stop(0.5,#000000), 
              color-stop(0.5, transparent), 
              to(transparent));
       position: absolute;
       left: -8px;
       top: 2px;
}

And here’s the finished button:
Completed back button

To create a next button, we can use the same button, but switch out a “next” class with all the values flipped to produce a button pointing the opposite way:

.back.black > span {
       display: block;
       height: 20px;
       width: 20px;
       background-image: -webkit-gradient(linear, left top, right bottom, 
              from(#7d828c),
              color-stop(0.5, #303749), 
              color-stop(0.5, #121a2e), 
              to(#121a2e));
       border-left: solid 1px rgba(79, 79, 79, 0.75);
       border-bottom: solid 1px rgba(79, 79, 79, 0.75);
      -webkit-transform: rotate(45deg);
      -webkit-mask-image: -webkit-gradient(linear, left bottom, right top, 
              from(#000000), 
              color-stop(0.5,#000000), 
              color-stop(0.5, transparent), 
              to(transparent));
       position: absolute;
       left: -8px;
       top: 2px;
}

You can view the file or download the complete file here to see the “back” and “next” buttons with their hover states.

NOTE
Although this technique does not work properly in Google Chrome because it presently lacks the CSS3 image mask property, you could make it work by enclosing the text of the link in a second span tag, give that span tag relative positioning with a z-index of 2 or greater. This would effectively position the text over the rotated span tag used a the pointer.

Update for Chrome and Android – August 29/2010
OK. Some good news, image masks are coming to Google Chrome 6. It has support for image masks the same as desktop Safari, iPhone, iPod Touch and iPad. The bad news is that this will most like not trickle down to the many Android devices out there. If you want to have the back and next buttons work on iOS and be backward compatible with existing Android devices, you can makes some slight changes to how the buttons are put together. Enclose the button’s text in a span and rename the empty span that gets used as a pointer to something sensible. I’m giving it a class of “pointer.” What we’ll do is give the text span relative positioning. Then we’ll give the pointer span a z-index of 1 and the text span a z-index of 2. That way the text will sit on top of the pointer and for Android devices that don’t support image masks you’ll still get the same look. Here’s the new markup:

<a href="#" class="button back black"><span class="pointer"></span><span>Back</span></a>
<a href="#" class="button next black"><span class="pointer"></span><span>Next</span></a>

With the CSS we’ll give all spans in the button relative positioning with a z-index of 2, then we’ll give the pointer span absolute positioning with a z-index of 1:

.back.black > span {
    position: relative;
    z-index: 2;
}
.back.black > span.pointer {
       display: block;
       height: 20px;
       width: 20px;
       background-image: -webkit-gradient(linear, left top, right bottom, 
              from(#7d828c),
              color-stop(0.5, #303749), 
              color-stop(0.5, #121a2e), 
              to(#121a2e));
       border-left: solid 1px rgba(79, 79, 79, 0.75);
       border-bottom: solid 1px rgba(79, 79, 79, 0.75);
      -webkit-transform: rotate(45deg);
      -webkit-mask-image: -webkit-gradient(linear, left bottom, right top, 
              from(#000000), 
              color-stop(0.5,#000000), 
              color-stop(0.5, transparent), 
              to(transparent));
       position: absolute;
       left: -8px;
       top: 2px;
       z-index: 1
}

Update: October 5, 2010

I’ve update these buttons to use subpixel rendering. You can read about the technique here. You can try this out online or download the source code.

Apple’s HTML5 Demo

OK, there’s been so much controversy about Apple’s HTML5 demo page (http://www.apple.com/html5/). Of course, you can go to the Mozilla developer site and find links to their own CSS3 examples like this: https://developer.mozilla.org/En/CSS/Using_CSS_transforms. It only works in Firefox. Hmmm… You can find similar pages on the Opera developer site. Hmmm… You can find similar ones from Google as well, oh and from Microsoft too. Do you see a pattern? Every browser vendor is putting up examples of how their browser renders whatever it is they’re trying to show off. So is it disingenuous for Apple to post their HTML5 demo? No.

All of this started with a blog post by a Mozilla evangelist, Christopher Blizzard: http://www.0xdeadbeef.com/weblog/2010/06/intellectual-honesty-and-html5/

Since Netscape Navigator 6, the Mozilla team for years lead the charge in establishing new standards for the Web. However, in June of 2005 Apple release the source code of the rendering engine for their Safari browser as and open source project known as Webkit. Even before the creation of the Webkit project, the Safari team had managed to create code to pass the Acid 2 test, the first browser to do so. Since then the Webkit team has introduced many new feats for the Web: the canvas tag, multiple backgrounds, background sizing, background clipping, rounded corners, text shadow, box shadow, text stroke, CSS gradients, CSS image masks, border images, CSS transforms, CSS transitions, CSS keyframe animation, as well as support for multi-touch events, etc. David Hyatt from the Webkit team has submitted all of these to the W3C for standardization. Many of these have already been adopted by the Mozilla team into Firefox. That’s why it’s interesting to see Mozilla’s Christopher Blizzard reacting Apple HTML5 demos as follows:

“The demos that they put up are just filled with stuff that Apple made up, aren’t part of HTML5 and are only now getting to the standards process. Part of CSS3? Kind of, sort of, but under heavy development and still in a feedback process.”

Then he goes on to say:

“The most important aspect of HTML5 isn’t the new stuff like video and canvas (which Safari and Firefox have both been shipping for years) it’s actually the honest-to-god promise of interoperability. Even stodgy old Microsoft, who has been doing their best to hold back the web for nearly a decade, understands this and you’ll see it throughout their marketing for IE9. (Their marketing phrase is “same markup” – watch for it and you’ll see it everywhere in their messaging.) The idea that the same markup, even with mistakes, will be rendered exactly the same. HTML5 represents the chance for browsers to work together and find common ground.”

OK, then why are there so many sites that use only CSS3 with -moz prefixes, including stuff on mozilla.com? If he wants one code to work everywhere always, then why have the Mozilla guys tried to persuade the W3C to use different syntax from the CSS3 features that the Webkit team introduced? We’re talking border radius, gradients, etc. If we wanted one code that worked everywhere we would still be using HTML 1.0. Progress is always painful, like a snake outgrowing its skin and shedding it. To me it seems like his biggest gripe is that most innovation on the Web today that is impacting standards seems to be coming from the Webkit team. This has meant that the Mozilla team is now in react mode trying to play catch up or minimalize the perception of Webkit’s advances over the last five years.

If i download the alpha of Firefox 3.7 right now, it’s got built-in support for the stuff that he’s complaining about in the above paragraph, stuff that is still in early recommendation status. Then there’s Mozilla’s decision to dump the HTML5 client-side database API for their own JavaScript based API for database calls. They apparently thinks developers don’t know how or don’t feel comfortable writing SQL. Hmmmm… What kind of developers are those? Sounds like sounds to me like a lot of their developers only know JavaScript so they want a JavaScript API. Check out the reaction of real developers in the comments section of the above link.

So, it seems like the Mozilla team is heading down a path to somewhere. It’s just that I don’t see myself wanting to head down that path. I want innovation that adds new capabilities and makes things simpler. But I don’t want simpler to mean I can do the hard things because simple can only do simple.

iPad Dropdown Menu with CSS3

Works with Safari, iPhone, iPad

One thing noticeable about the iPad’s interface, in contrast to that of the iPhone, is the gorgeous dropdown menus. They have a highly polished look with their gradients and drop shadows. For a while now I was wondering if it would be possible to re-create their look using only CSS3. If you haven’t seen them yet, here is a sample:

Typical iPad dropdown menu

With CSS3 we can easily create the rounded corners, gradients and drop shadows. But what about the menu pointer? That part had me scratching my head for a while. I debated whether to use a canvas element. But I had already decided that I would pull this off using only CSS3. So, what I decided to do was use a rotated div. First I filled it with the last color at the top of the gradient that I used on the top of the menu. Since I was going to rotate the div, I didn’t need border on all four sides, just one two. I was rotating the div 90 degrees, so I gave it a border matching the menu on the left and bottom. Of course after rotating the div I still had the problem of the other half of the div. To remove it I used a CSS3 image mask. For the image I used a CSS3 gradient that went from solid black halfway and then turned transparent. Since the div was rotated 45 degrees, I needed to create a gradient mask from the bottom left to the top right:

-webkit-transform: rotate(45deg);
-webkit-mask-image: -webkit-gradient(linear, left top, right bottom, from(#000000), color-stop(0.5,#000000), color-stop(0.5, transparent), to(transparent));

This effective cut the div in half diagonally. I then used position to move the div to where it needed to be.

I originally designed this menu to work on desktop Safari/Chrome so that I could mimic the iPad menus. However, it also works on the iPad. The desktop version has some subtle differences because I used inset box shadows to create depth. That won’t be on the iPad until the iOS 4 update.

You can see the finished example or download the source to see how I put it together.

And here’s the finished menu using only CSS3:
An iPad style dropdown menu create with CSS3

Deep Navigation with Push and Pop

As I showed in a previous post, it’s easy to create a simple back and forth navigation. That works for the simplest of situations, but what if you need deeper navigation? This is complicated since with the single document Web app approach, you’re always on the same page, moving from section to section. There is a simple way to make keeping track of where you’ve come from so you can make your way back. All you need is a dedicated JavaScript array. A JavaScript array has two very useful functions: push and pop. Consider an array like a tube into which you are going to stuff ping pong balls. One end of the tube is sealed, so you can only insert the ping pong balls through the open end. Let’s assume you’ve put all your ping pong balls into the tube and now you want to take one out. The last one in will be the first one out. That’s how arrays work. Push shoves data into an array, staking it on top of what is already there. Pop removes the last thing inserted in the array.

So, by using pushing data into an array as we navigate forward and popping items out when we navigate backwards, we can always tell what are navigation path is. Based on the example that I put together previously, we can create an array pre-populated with the home section’s id:

var navigation = ["#Home"];

Now what we’ll do is whenever the user clicks on a forward navigation element, we’ll push that into the navigation array:

$(".menuList li").click(function() {
    navigation.push($(this).attr("rel"));
});

Then, when we want to navigate back, we just need to use array.pop() to get the previous location. Since our array is navigation, we pop it first:

navigation.pop();
$(navigation[navigation.length-1]).addClass("current");

That will set the previous section as the current frame, firing off all the appropriate CSS3 transitions to bring it into view. You can see it in action here. And you can download it here.