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.

About these ads

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.

Speeding Up CSS3 Transforms on Mobile

In my previous post about making a standalone single file Web app, there was a gotcha. The demo worked fine on the desktop, but the performance was abysmal on mobile. The nice, animated transitions were chopping, stuttering and sometimes simply jump from start to finish. To a certain extent I could understand why. A mobile browser is pretty limited by the hardware it’s running on. But in general webkit mobile browsers seems very capable.

I did some research on transform and learned something. Whereas 2D transforms are render by the browser, 3D transforms are hardware excellerated. I went back to my CSS and updated it to use 3D transforms, leaving the z value at 0. When I uploaded this to the server and hit it from my phone, voilá, I got the smooth sliding transforms on mobile that I was expecting. So, if you’re going to do a traslate, rotate or skew, use the 3D version. Just leave the z value at 0. For example, previous I was using CSS3 like this to move something:

article {
    -webkit-transform: translate(100%, 0%);
}

This would work fine on the desktop, but was terrible on mobile. In the above example the first value is for the x or horizontal position and the second is the y or vertical. Since the x value is 100% it would move the element 100% to the right.

By using 3D transforms I could get decent performance on mobile handsets:

article {
    -webkit-transform: translate3d(100% ,0%, 0%);
}

If you’re only doing 2D transforms, leave the last, third value at 0%. This will be off-loaded to hardware for rendering, giving you the performance you would expect on phone and tablets. I’ve updated the example code online to use the 3D transforms, which you can download.

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.

Follow

Get every new post delivered to your Inbox.

Join 46 other followers