Intermediate Articles

Responsive Web Above The Fold

The following post is by Arley McBlain. Arley has written a few other great articles for CSS-Tricks in the past so I'm pleased to have him back for another!

This post's title isn't just a weak attempt at SEO stuffing, it's also a blind-folded scissor kick into a beehive convention! Few topics in web production can bring a nerd's blood to a rolling boil as quickly as "The Fold" and "Responsive Web Design", so it's high time we combine the two and bring this server to its knees under the sheer weight of trolls sharing how they really feel about me as a person.

Before the Webbys have to invent the "Best Use of Naivety" award I'd like to point out that for years I've been in the "The Fold is Dead" camp. Nothing used to please me more than comparing a website to the source of the scrolling metaphor; papyrus scrolls (bonus points when I could work a font joke in). "Sure, and when scribes wanted to avoid scrolling they probably wrote letters on fortune cookie paper, amiright?!" (clients probably love sarcasm). However, recently I've been forced to admit that maybe I should be a bit more accommodating of the Fold's proponents, rather than simply beating them over the head with my sack of used mouse scroll wheels.

Recently Google (incidentally a company whose clean "above the fold" website is merely a gateway to lots of scrolling search results) added a new feature to their Google Analytics suite that allows you to see what size of browsers users are visiting your site with. This allowed me to stop pretending that the data from the user's monitor resolution actually meant something relevant, and to begin to learn more about the way users are surfing my site.

View Browser Size analtyics in Standard Reporting > Content > In-Page Analytics, and click the "Browser Size" button. (view full size)

Google Analytics is a useful tool, and can do more than just shatter your faith in humanity's ability to upgrade their web browser. This tool can literally tell you how your site is being used. You can easily see who is scrolling and clicking those big shiny buttons you painstakingly designed, even after they've been pushed well out of sight by multiple instances of the "Ask Jeeves" toolbar. It will literally show you a percentage of who is scrolling and clicking individual links!

This new analytics tool made me start thinking about the fold afresh. I used to think of the fold as a weakness or handicap of Luddite users, but for the first time I saw that the fold has a huge bearing on the way that I (a savvy webmaster) surf the web as well. I had to admit to myself that I bounce a lot (Google Analytics probably has me pegged with the attention span of a goldfish). I'll often making the decision to leave a website within a second of arriving if I don't immediately see what I came for, or if I think the site looks like it was built by rodents. Judging books and websites by the cover doesn't usually hurt until a lack of results has me return to that same page to look harder, only to find the content in question sitting smug just below the fold.

Getting Started

The new browser size analytics feature has been around just long enough now that you can see some real data on how your current site design fares with impatient people like me! You'll see that many users aren't using their browsers full screen. While this has always been true of Mac users (most bitterly say they prefer their windows smaller after not being able to figure out what that UI "+" button does), it turns out that many PC users probably don't either (probably trying to look cool to the aforementioned Mac people).

I'm guessing the results of the reports will motivate you to do one of two things: personally confront each user on their questionable computer habits in dimly lit remote areas, or make you want to fine tune your design. The latter is where some handy vertical responsive web design comes in (for the former I recommend a sack full of used mouse scroll wheels).

Vertical Responsive Web Design

For the last couple years we've all been resizing our monitor widths like giddy accordion players to better understand RWD. The magic behind those is the now-common Media Query:

@media screen and (min-width: 768px) {  
  marquee { font-size: 43em; }
}

To get started with vertical RWD is a simple matter of addressing heights. Cinchy.

@media screen and (min-height: 768px) {  
  blink { color: pink; }
}

I wanted to make a couple practical vertically responsive demos to tantalize and delight!

Example #1: A Cuddlier, Squishier Fold

The most obvious use of vertical RWD would be to keep your all-important calls to action above the fold. To make this example extra fun I've chosen to do this on an existing horizontally responsive layout courtesy of Twitter Bootstrap. Being responsive on two axes is good fun, and is a great opportunity to make some messy code, if you're into that kind of thing.

When you vertically resize this demo site on desktop I've decided that I want to keep all four of the buttons in view as much as possible (hopefully the average site will just have one key CTA). For mobile sizes I only will require the blue CTA button to stay visible. For this demo I've decided not to worry about screens shorter than 320px. Realistically that should cover the extremely small desktop users and mobile users alike. We should be comfortable to talk about the users with smaller viewports in the tone that we usually reserve for IE6 users.

With this Twitter Bootstrap layout there are four horizontal break points - all but the last one (when the lower 3 columns get stacked) I am able to keep my buttons in view. This is all done by a few media queries which you can find in a <style> block at the bottom of the document's source code.

All in all, it was pretty cinchy to retro fit this layout to work in this way (he said with confidence despite deliberately picking a layout lacking media above the fold), especially by piggy-backing on existing horizontal break points.

Of course we also need to address the ugly elephant that basically lives in this room like an ever-unwelcome squatter: what shall we do about the old versions of Internet Explorer? There's a lovely little JavaScript library called CSS3MediaQueries.js that does the trick fairly well. I've included it in this demo in conditional IE tags. While it doesn't resize as fluidly as a modern browser, on the page load the user will see the responded version of the layout. Beauty.

Twitter Bootstrap demo:

View Demo

Example #2: WordPress Dashboard Navigation Tweak

This example is much simpler: one tiny media query with a simple bit of CSS that completely changes the ordinarily relative positioned left navigation that all WordPress admins are familiar with:

@media screen and (min-height: 500px) {  
  #adminmenuwrap { position: fixed; } 
}

Now, regardless of the navigation being in the collapsed or n00b mode (I jest), it will stay visible when you're scrolling through taller pages - provided your vertical resolution is taller than 500px. If your browser is shorter than that it goes back to being a relative positioned element that will scroll with the rest of the site. I've chosen 500px which is enough for the standard menu, as well as a few extra menu items from plugins or themes.

This vertical oriented RWD positioning treatment can work very well for navigation, widgets, pictures of cats, page tools, and even the lovely ads, like the ones from the charming sponsors of this website!

For this proof of concept I've made a localized HTML version of the WordPress dashboard (all of the links are broken it's a demonstration of the scrolling effect only):

WordPress Dashboard Demo (media query only):

View Demo

The 500px used in my example only works when you know the maximum height needed for the element. However, if the height of that object is unknowable or changes (an accordion navigation with multiple levels for example) you may want to consider a bit of jQuery that will compare the height of the object to the height of your window.

I have done this in the following variation of our demo by calling jQuery and using this script:

var setResponsive = function () {

  // Is the window taller than the #adminmenuwrap by 50px or more?
  if ($(window).height() > $("#adminmenuwrap").height() + 50) {

     // ...if so, make the #adminmenuwrap fixed
     $('#adminmenuwrap').css('position', 'fixed'); 
    
  } else {
       
     //...otherwise, leave it relative        
     $('#adminmenuwrap').css('position', 'relative'); 

  }

}

$(window).resize(setResponsive);
setResponsive();

WordPress Dashboard Demo (with jQuery):

View Demo

A Tall Order

So there you go.

A slight disclaimer: throughout this post I've been using the phrase "vertical responsive web design" - please understand that I'm just specifying the orientation of the responsivity, not coining a phrase. Whether the response is vertical or horizontal the term "responsive web design" captures it all.

I think there's a lot more we can be doing with vertical responsive web design in relation to our best practices, our content, and our users. The advent of this snappy new Google Analytics tool makes this a great time to start. In the past these wily short-browsered users may have been unpredictable and frustrating enough to drive Berners-Lee to kick a kitten, but now we can learn from their behavior, and predict the common ways they'll view important content.

Good news my friends, the fold is now undead.

Don’t Overthink It Grids

The vast majority of websites out there use a grid. They may not explicitly have a grid system in place, but if they have a "main content area" floated to the left a "sidebar" floated to the right, it's a simple grid.

If a more complex layout presents itself, people often reach for a grid framework. They assume grids are these super difficult things best left to super CSS nerds. That idea is perpetuated by the fact that a lot of the grid systems they reach for are very complicated.

Here's how I build grids. It's not hard or complicated. Even making them flexible is no big deal.

(more…)

Crazy Town Selectors

I recently used this selector as an example in a blog post:

.article #comments ul > li > a.button {
  /* Crazy town */
}

There was a few comments to the effect that this seemed perfectly reasonable. I think if we step through it backwards the craziness comes out more clearly.

(more…)

Crafting Minimal Circular 3D Buttons with CSS

The following post is written by Brandon Pierce. Brandon saw some nicely designed buttons by Wouter on Dribbble and set about building them with CSS. They came out nicely and he agreed to post about the process here.

Here's what we are going to make in this tutorial:

View Demo   Download Files

The Base HTML

We will be using a simple unordered list1 within a <nav> tag to create our buttons.

<nav>
      
  <ul class="nav">
       
    <li><a href="#" class="icon-home"></a></li>

    <li><a href="#" class="icon-cog"></a></li>

    <li><a href="#" class="icon-cw"></a></li>

    <li><a href="#" class="icon-location"></a></li>

  </ul>

</nav>

Some Resources

  • These icons in the buttons come from the Fontello icon font and web app.
  • The textured background is from Subtle Patterns.

CSS for the Icon Font

There are a few ways to go about this, but we'll go with this:

@font-face {
  font-family: 'fontello';
  src: url('../fonts/fontello.eot');
  src: url('../fonts/fontello.eot?#iefix') format('embedded-opentype'),
       url('../fonts/fontello.woff') format('woff'),
       url('../fonts/fontello.ttf') format('truetype'),
       url('../fonts/fontello.svg#fontello') format('svg');
  font-weight: normal; font-style: normal;
}

[class*=" icon-"] {
  font-family: 'fontello';
  font-style: normal;
  font-size: 3em;
  speak: none;
}

.icon-home:after     { content: "\2302"; } 
.icon-cog:after      { content: "\2699"; } 
.icon-cw:after       { content: "\27f3"; } 
.icon-location:after { content: "\e724"; }

CSS for the Buttons

The buttons will be arranged horizontally by making the list items inline-block. We'll make them butt up against each other with a little negative margin (or see these other techniques).

.nav {
  list-style: none;
  text-align: center;
}

.nav li {
  position: relative;
  display: inline-block;
  margin-right: -4px;
}

The lovely three dimensionality of the buttons comes from a subtle gradient and bevel and embossing courtesy of a pair of box-shadows, one normal and one inset. The width and height of the buttons are identical creating a square, which turns to a circle with border-radius. The line-height matches the height and text-align is set to center, so the icons will be both horizontally and vertically centered.

.nav a {
  display: block;
  background-color: #f7f7f7;
  background-image: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e7e7e7));
  background-image: -webkit-linear-gradient(top, #f7f7f7, #e7e7e7); 
  background-image: -moz-linear-gradient(top, #f7f7f7, #e7e7e7); 
  background-image: -ms-linear-gradient(top, #f7f7f7, #e7e7e7); 
  background-image: -o-linear-gradient(top, #f7f7f7, #e7e7e7); 
  color: #a7a7a7;
  margin: 36px;
  width: 144px;
  height: 144px;
  position: relative;
  text-align: center;
  line-height: 144px;
  border-radius: 50%;
  box-shadow: 0px 3px 8px #aaa, inset 0px 2px 3px #fff;
}

We can flatten out the buttons a bit on :hover to create a pressed look. You may want to save that for :active and do something else for :hover, but we'll leave that up to you.

.nav a:hover{
  text-decoration: none;
  color: #555;
  background: #f5f5f5;
}

At this point our buttons should look like this:

Not bad, but we have a ways to go.

Now on to the fun part, were going to be using pseudo elements to give us some additional elements to work with to create the rest of the effects. To create the horizontal "groove" you see between each button were going to add the pseudo selector :before to our list items. Using negative z-index we can keep it beneath everything. The double border give it the inset look. The negative margins we used on the list items earlier are what allow this groove to look continuous. We take advantage of absolute positioning to center the line.

.nav li:before {
  content: "";
  display: block;
  border-top: 1px solid #ddd;
  border-bottom: 1px solid #fff;
  width: 100%;
  height: 1px;
  position: absolute;
  top: 50%;
  z-index: -1;
}

For that extra three-dimensionality, we'll create a "well" around each anchor that the button sit inside. We can create this again with no additional HTML by using a pseudo element.

.nav a:before {
  content: "";
  display: block;
  background: #fff;
  border-top: 2px solid #ddd;
  position: absolute;
  top: -18px;
  left: -18px;
  bottom: -18px;
  right: -18px;
  z-index: -1;
  border-radius: 50%;
  box-shadow: inset 0px 8px 48px #ddd;
}

The size of the well (a bit larger than the button itself) comes from absolutely positioning itself behind the button and stretching itself out by using negative positioning values (top/bottom/left/right). The 3D look this time comes from a top border and inset box-shadow creating an internal shadow.

And we're done!

That wasn't so bad was it?

View Demo   Download Files

Browser Support

These buttons use a good bit of CSS3 and some pseudo elements which are CSS 2.1. Nothing too overly progressive though. Any version in the last few years of Safari, Chrome, Opera, or Firefox will have no trouble. IE 8 doesn't do CSS3, so it falls back to this:

Not a big deal.

IE 7 is similar only without the "groove" line as it doesn't do pseudo elements. Also not a big deal.

 


1 Unordered lists may not actually be ideal for navigation. We still used them here because we needed the extra element.

On :target

The :target pseudo selector in CSS matches when the hash in the URL and the id of an element are the same.

The current hash of the URL is "voters"
<section id="voters"> 
   Content
</section>
:target {
   background: yellow;
}

While that URL is as it is, that section element will have a yellow background, as per our CSS.

When would you use this?

One possibility is when you want style with "states." When the page has a certain hash, it's in that state. It's not quite as versatile as manipulating class names (since there can only be one and it can only be related to one element) but it's similar. Anything you could do changing a class to change state you could do when the element is in :target. For instance: change colors, change position, change images, hide/show things, whatever.

I'd use these rules-of-thumb for when :target is a good choice:

  1. When a "state" is needed
  2. When the jump-down behavior is acceptable
  3. When it's acceptable to affect the browser history

We'll touch on all these things in this article.

How do you get hashes in URLs?

The most common way is by a user clicking a link which includes a hash. Could be an internal (same-page) link or a fully qualified URL that happens to end with a hash and value. Examples:

<a href="#voters">Go To There</a>

<a href="http://example.com/#specific-part">Go To There</a>

Jumping Behavior

Regardless if it's a same-page link or not, the browser behavior is the scroll the page until that element is at the top of the page. Or, as far as it can if it can't scroll that far. This is rather important to know, because it means exploiting this "stated" behavior is a bit tricky/limited.

For instance, I once tried a variety of techniques to replicate functional CSS tabs, but ultimately decided using the checkbox hack was a better idea because it avoids the page-jumping issues. Ian Hansson at CSS Science has some examples of tabs as well. His third example uses :target, and absolutely positioned elements hidden above the top of the page to prevent page jumping behavior. It's clever, but not really a solution, because that would mean the page would jump upwards should the tabs be down further on a page. The anchors are actually fixed position, meaning they scroll with the page and don't exhibit top-jumping behavior. Extra clever!

A perfect use: highlighting sections

Here's a problem: When a hash-link sends you flying down the page to the relevant section, it will try and make that section snug against the top of the browser window.

But what if there isn't enough room to scroll beneath that section? That section will be visible, but it won't be snug against the top, which can be weird and confusing.

It can be disorienting.

I'm not just making that up. From personal experience, page-jumping links that don't take me to somewhere where was I was linking to is exactly on the top, I get all out of sorts. I find it happens all to often on things like FAQ pages where the linked-to sections often aren't very tall.

So let's solve that!

One historical method was called the Yellow Fade Technique. It was employed by 37 signals in situations where new content is added to the page, and they were trying to draw the user's attention to it. Jonathan Snook ported that idea to CSS and combined it with :target.

Instead of yellow fade, we'll indicate which section the link we just clicked was referring to by nudging it over to the right and flashing a red border. Instead of making you think, here you go:

The structure is a bit of navigation that links to sections by ID:

<nav>
  <a href="#one">1</a>
  <a href="#two">2</a>
  <a href="#three">3</a>
</nav>

<section>
  <div id="one"><h2>One</h2>Pellentesque habitant morbi ...</div>
  <div id="two"><h2>Two</h2>Pellentesque habitant morbi ...</div>
  <div id="three"><h2>Three</h2>Pellentesque habitant morbi ...</div>
</section>​

When the sections become in :target, the scoot over to the right a bit via translateX transform (prevents any weird text wrapping or anything we might get with padding) and a red border flashes on via keyframe animation.

:target {
  animation: highlight 1s ease;  
  transform: translateX(20px);     
}
@keyframes highlight {
  0% { border-left-color: red; }
  100% { border-left-color: white; }
}
section > div {
  border-left: 40px solid white;
  padding: 10px;
  transition: all 0.5s ease;     
  padding-right: 50px;
  margin-left: -20px;    
}

That's all there is too it really. I'd chalk this up under progressive enhancement, if you're worried about browser support. As in, it's just a nice touch, not vital.

View Demo

Fighting the Jump!

Let's say you like the idea of using :target for states but dislike the page jumping behavior, you can change the hash link in a URL without a page jump.

Using jQuery, you could target all hash-links, prevent their default behavior, and use pushState (or replaceState, I suppose) to change the URL (which won't move the page).

$("a[href^=#]").on("click", function(e) {
  e.preventDefault();
  history.pushState({}, "", this.href);
});

You could interchangeably use replaceState there too, which would change the URL without adding an entry to the browser history. Sometimes you might want that, sometimes you might not. At least you have a choice here, which you don't with the default behavior of clicking a hash link, which always adds.

But there is bad news

When the URL changes to a new hash, you'd think the current target would change and new CSS would take effect. It doesn't (tested current WebKit and Firefox at time of this writing). It's a bug.

Theoretically, you could measure and save the current scroll position of the page, let the link move it naturally, then set it back to where it was. But that just sounds so awful I couldn't even bring myself to make a test page for it.

More

Minimum Paragraph Widths in Fluid Layouts

The following is a guest post by Gustav Andersson who has come up with a clever little technique for a text flow problem. I've struggled with this myself in the past, so I'm happy to add this technique to the ol' toolbox. Not mention, yet another one of these.
An example where a floating image leaves only enough space for a very narrow column of text which looks ugly and is broken up be a long word.
An example where a floating image leaves a few orphaned words.

A floating image takes away horizontal space from the text that flows around it. In fixed width layouts, you can check that the space left for the text is enough to create a decent looking column, safe in the knowledge that everyone else will see the same thing.

In a fluid layout, however, you have no such guarantee. If a user views your site on a smaller device, the horizontal space left for the text may only fit a word or two per line. Such a narrow column of text doesn't just look ugly; it is also brittle. A sentence containing a long word will split when the long word moves down below the floating image, leaving behind it dead empty space mid-sentence.

The elusive minimum paragraph width

To solve the problem of too narrow paragraphs, we need a way to implement a minimum paragraph width. If the space left by the floating image is below this width, then the whole paragraph moves down underneath the image.

Same example as above, but with a red border around the paragraph, showing that it extends behind the  image.

The red border is the boundary of the paragraph element.

Intuitively, the following CSS seems to fit the bill:

p {
  min-width: 10em;
  /* For demonstration */
  border: 1px solid red;
}

However, this has no effect. The image floats above the paragraph, and thus doesn't reduce the paragraph's 'official' width. Meanwhile, the text within the paragraph dutifully moves aside to make room for the floating image, and so the problem remains.

The media query solution for known image widths

An example showing how the media query has disabled the floating of the image, and the entire paragraph is underneath.

The Media Query solution works, but requires fixed-width images.

If your images (or other floating content) share a fixed and predefined width, then you can use CSS3 media queries to disable the floating at screen sizes that are too narrow to fit both an image and a text column side by side.

This solution will of course only work on browsers that support CSS media queries. For other browsers, the solution degrades to the original problem.

@media screen and (max-width: 400px) {
  img {
    float: none;
  }
}

General case solution using pseudo element

The media query solution doesn't work when the floating elements have arbitrary widths, nor is it very elegant.

An example of the pseudo-element rule in place, showing a thin green border surrounding the pseudo element. Both the element and the paragraph are below the image.

This example shows the pseudo-element rule in place, showing a thin green border surrounding the pseudo element. Both the element and the paragraph are below the image.

A better solution is to give every paragraph an invisible CSS pseudo-element with the desired minimum paragraph width. If there isn't enough space to fit this pseudo-element, then it will be pushed down underneath the image, taking the paragraph with it.

This solution is supported by most browsers. On older browsers, the solution degrades gracefully to the original problem.

p:before {
  content: "";
  width: 10em;
  display: block;
  overflow: hidden;
  /* For Demonstration */
  border: 1px solid green;
}

The pseudo element's green border is there for demonstration purpose only. It is not required for the solution and you should remove it in your code. The pseudo element will then take no vertical space at all.

Demo & Download

View Demo   Download Files

 

About the Author

Gustav Andersson is the author behind The Modern Nomad, a site exploring nomadic lifestyles which frees people to live and work anywhere, anytime. He is a tango-dancing, steer-wrestling, grammar-loving burner who wants to inspire you to consider a life without an address.


Places It’s Tempting To Use Display: None; But Don’t

You want to hide something on a page, so:

.hide {
   display: none;
}

But wait! By applying that class to an element you've immediately made that content "inaccessible" by screen readers. You've probably known this forever, but still the poison apple sneaks into our code once in a while.

I don't want to re-hash all the specifics. Your best bet is to read "Now You See Me" by Aaron Gustafson on A List Apart to get an understanding of this if you don't already.

One way to encourage yourself to do the right thing is by creating more appropriate class names. Your regular hide class should position the content off screen, which still leaves it screen reader accessible:

.hide {
   position: absolute !important;
   top: -9999px !important;
   left: -9999px !important;
}

I use !important here because if you've gone to the trouble to add a "hide" class to something, you probably mean it and don't want to think too hard about if the specificity value is strong enough. And if you know that you need to display: none something, the class should help you understand it:

.remember-this-will-NOT-be-read {
   display: none !important;
}

Another option for accessible hiding comes from some Snook research and the HTML5 boilerplate:

.visuallyhidden { 
  position: absolute; 
  overflow: hidden; 
  clip: rect(0 0 0 0); 
  height: 1px; width: 1px; 
  margin: -1px; padding: 0; border: 0; 
}

OK you got it. Easy peasy when you're totally in control of class names and all you do is apply and remove them. But things get a little tricker with JS libraries that apply their own CSS. For instance in jQuery, after you .slideUp(), you'll have a display: none in the inline CSS to deal with. Yes, screen readers run JavaScript and yes, that's still a problem.

Again Aaron Gustafson has us covered there, who suggests applying the accessible class name after the sliding is done and then removing the display: none by sliding it the other direction.

var $button = $('#myButton'),
    $text   = $('#myText'),
    visible = true;

$button.click(function() {
  if (visible) {
    $text.slideUp('fast',function() {
      $text.addClass('hide')
           .slideDown(0);
    });
  } else {
    $text.slideUp(0,function() {
      $text.removeClass('hide')
           .slideDown('fast');
    });
  }
  visible = !visible;
});​

Here's a demo of that:

View Demo

Now we have the tools we need to stop using display: none and start using more accessible "hiding" methods.


FAQ pages

If you're hiding the answer until the question is clicked, hide with an accessible class name. Careful you don't .hide() and then slideToggle(), that's not good enough!



Labels

It's tempting to use placeholder text as a label replacement (especially now with some browsers improved UX of leaving the text until you actually type), but don't display: none or remove the labels. I recently heard a heartbreaking story about a blind girl trying to apply for college and the form had missing labels so she had no idea what to put in what fields. So if you're going to use placeholder text as a label replacement, use an accessible hiding technique for the labels.



Tabs

Just because a panel of content isn't the "currently active" one doesn't mean it should be inaccessible. Hide it with an accessible hiding technique instead. Or, you may not even need to. If all the panels are the same height, you can just flip-flop which ones is visible by adjusting z-index.



@media queries

Turning on Voice Over in OS X and using Safari is a screen reader. Now imagine that Safari window was open to a very narrow width and the page had some @media queries for handling smaller viewports. And say that @media query hides some things with display: none in order to better visually accomodate the space. This could be good or bad for accessibility. Are you hiding a bunch of crap that isn't important to the page? Or are you hiding useful things that a person using a screen reader should have access to like they normally would.


No Expert Here

This entire post is based on the premise that display: none is bad for accessibility. It's not based on my deep and thorough understanding of screen readers and general accessibility. If you have more to add, things to correct, or personal experience to share, please do.

(Better) Tabs with Round Out Borders

The following is a guest post by Menno van Slooten. You might notice we've been down this road before, but I quite like Menno's approach here. The end result proves you can get a little fancier with the design than I originally did, with borders, gradients, and shadows and while actually using less elements.

A good-looking tab control usually has one feature that I've always found impossible to reproduce without images: borders that bend to the outside at the bottom of each tab. In this article I would like to show how you can use the CSS :before and :after pseudo elements to create this effect without using images. First, let's start with some basic markup.

The markup

<ul class="tabrow">
   <li>Lorem</li>
   <li>Ipsum</li>
   <li class="selected">Sit amet</li>
   <li>Consectetur adipisicing</li>
</ul>

This would be about as basic as you can get. Not a lot of elements to work with here, but it will do.

Getting started

To get started, let's get rid of the default <ul> and <li> styling and get these babies in line.

  • Lorem
  • Ipsum
  • Sit amet
  • Consectetur adipisicing
.tabrow {
  text-align: center;
  list-style: none;
  margin: 0;
  padding: 0;
  line-height: 24px;
}
.tabrow li {
  margin: 0 10px;
  padding: 0 10px;
  border: 1px solid #AAA;
  background: #ECECEC;
  display: inline-block;
}

Selecting a tab

The selected tab should of course be clearly visible.

  • Lorem
  • Ipsum
  • Sit amet
  • Consectetur adipisicing
.tabrow li.selected {
  background: #FFF;
  color: #000;
}

Getting to the bottom of things

Every tab control needs a well-defined bottom edge. It won't do much now, but later it will help emphasize the effect of the selected tab being on top.

  • Lorem
  • Ipsum
  • Sit amet
  • Consectetur adipisicing
.tabrow {
  position: relative;
}
.tabrow:after {
  position: absolute;
  content: "";
  width: 100%;
  bottom: 0;
  left: 0;
  border-bottom: 1px solid #AAA;
  z-index: 1;
}

Here we introduced our first :after pseudo-element. Basically, :after appends another child to an element. It's not in the DOM (which is why it's called a pseudo element), so it is not a real child but it is completely stylable, as long as you add some content, in this case a single space character.

In my opinion, the terms :before and :after are slightly confusing since the pseudo's aren't actually added before or after the element they apply to, but are inserted as children. This is also why you can't apply :before and :after to elements that can't contain children ("no content" elements), like <input>.

In this case, we use the pseudo element to create a bottom border that doesn't influence the tabs' positioning. We could have just put a bottom border on the <ul> but that would've made the next step a little trickier.

Above and beyond

Now, an essential part of a convincing looking tab control, is that the selected tab sits in front of the edge while the rest fall behind the edge. To do this, we change its bottom border and do some z-index magic.

  • Lorem
  • Ipsum
  • Sit amet
  • Consectetur adipisicing
.tabrow:before {
  z-index: 1;
}
.tabrow li {
    position: relative;
  z-index: 0;
}
.tabrow li.selected {
  z-index: 2;
  border-bottom-color: #FFF;
}

Around the bends

It is now time to add the elusive border that bends to the outside and we'll use :before and :after for this. Let's take this step by step and first just put everything in position.

  • Lorem
  • Ipsum
  • Sit amet
  • Consectetur adipisicing
.tabrow li:before,
.tabrow li:after {
  position: absolute;
  bottom: -1px;
  width: 6px;
  height: 6px;
  content: " ";
}
.tabrow li:before {
  left: -6px;
}
.tabrow li:after {
  right: -6px;
}
.tabrow li:after, .tabrow li:before {
  border: 1px solid #AAA;
}

Don't be such a square

You can probably see where this is going. Let's remove the borders we don't want and add some rounded corners.

  • Lorem
  • Ipsum
  • Sit amet
  • Consectetur adipisicing
.tabrow li {
  border-top-left-radius: 6px;
  border-top-right-radius: 6px;
}
.tabrow li:before {
  border-bottom-right-radius: 6px;
  border-width: 0 1px 1px 0;
}
.tabrow li:after {
  border-bottom-left-radius: 6px;
  border-width: 0 0 1px 1px;
}

Cutting corners

There's something not quite right about this result. Let's look at it up close. As you can see both the original straight corner as well as the rounded corner are visible. We need to somehow get rid of the straight corner. To do that, we will cover it up with a shadow. To illustrate what's going on, let's make the shadow stand out a little bit.

  • Lorem
  • Ipsum
  • Sit amet
  • Consectetur adipisicing
.tabrow li:before {
  box-shadow: 2px 2px 0 red;
}
.tabrow li:after {
  box-shadow: -2px 2px 0 red;
}

Almost there

As you can see, the red shadows completely cover up the square corners we would like to hide. If we give the shadow the correct colors the illusion is complete.

  • Lorem
  • Ipsum
  • Sit amet
  • Consectetur adipisicing
.tabrow li:before {
  box-shadow: 2px 2px 0 #ECECEC;
}
.tabrow li:after {
  box-shadow: -2px 2px 0 #ECECEC;
}
.tabrow li.selected:before {
  box-shadow: 2px 2px 0 #FFF;
}
.tabrow li.selected:after {
  box-shadow: -2px 2px 0 #FFF;
}

Pieces of flair

All that's left to do now is adding a sprinkling of gradients and shadows to spice it up just a little bit.

  • Lorem
  • Ipsum
  • Sit amet
  • Consectetur adipisicing
.tabrow li {
  background: linear-gradient(to bottom, #ECECEC 50%, #D1D1D1 100%);
  box-shadow: 0 3px 3px rgba(0, 0, 0, 0.4), inset 0 1px 0 #FFF;
  text-shadow: 0 1px #FFF;
  margin: 0 -5px;
  padding: 0 20px;
}

If you're wondering about browser compatibility, it's exactly as you'd expect: everything but IE. It's very possible that it'll work in IE10, but I haven't had the chance to test with a preview release. Since IE8 and IE9 do support :before and :after but not border-radius you'll have to create a separate stylesheet for them if you want to give their users a nice visual experience.

View Demo   Download Files

Editor's note: I added anchor links inside the tabs in the demo since I think it's the most likely case that tabbed navigation like this have them. Most likely, they would have an href attribute that would link to the content they go with by id, and that behavior would be controlled by JavaScript. The fact that this tutorial doesn't need the anchor links for the extra pseudo elements is further testament to it being better than my original.

Update: Allan Watkins sent me a variation of these that point downwards:

See the Pen Round Out Tabs that Point Down by Chris Coyier (@chriscoyier) on CodePen.

Columns and the Greatest Common Divisor

I was recently putting together some CSS for columns. There was a few pre-determined widths that the columns needed to accommodate. For whatever reason (maybe because every grid framework in the world is this way) my mind went right to trying to find a common building block size. All columns are either the size of one building block or a multiple of a building block with gutter widths added. (more…)

We have a pretty good* newsletter.