Intermediate Articles

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…)

Full Browser Width Bars

Updated on December 17, 2014 by Parker Bennett.

As the line length of text content gets longer, it can be harder to read. So it's common practice to limit the width of content as the viewport widens. Often, this is accomplished by giving the content a max-width and centering it, either within a wrapping element, or section by section.

But what if we want an element to extend visually to cover the full width of the viewport, say, a background color for a header or individual headings?

One approach is to use an inner wrapper for the content we want width-constrained, but leave the outer element at 100% width. However, this adds extra markup and it's (arguably, GASP) non-semantic!

<header>
  <div class="wrap">
    <h2>Header</h2>
  </div>
</header>

<h3 class="full-width">
  <div class="wrap">Heading</div>
</h3>
<div class="wrap">
  <p>... text ... </p>
</div>
header, .full-width {
  width: 100%;
  background: #ccc;
}
.wrap {
  width: 80%;
  max-width: 24em;
  margin: 0 auto;
  padding: 0.25em 0.625em;
}

It would be better to wrap the content semantically (e.g., main or section) and have the headings break out visually as needed. Here are a few ways we can:

Using negative margin

A simple approach (from Timothy Mackey) uses negative margins and padding to extend the background in both directions. To avoid triggering horizontal scrolling, we need to set overflow-x:hidden on both the html and body elements.

html, body {
  overflow-x: hidden;
}
.full-width-bar {
  margin: 0 -9999rem;
  /* add back negative margin value */
  padding: 0.25rem 9999rem;
  background: rgba(0, 0, 0, 0.5);
}

See the Pen Full Width Bars Using Negative Margins by Chris Coyier (@chriscoyier) on CodePen.

Want to make the full-width bar a different color than the heading background? One way is to use left and right borders. But it turns out super-wide borders have some issues. For borders over 960px, you need to make them exactly 9600px or 10240px (or equivalent ems) to avoid a bug in Chrome. Also, you need to use solid colors, since RGBa values display a tiny gap in Safari. This is because of rounding errors when the containing element is centered.

html, body {
  overflow-x: hidden;
}

.full-width-borders {
  /* also subtract section padding (1.5rem) */
  margin: 0 -601.5rem;
  /* add back section padding (1.5rem) */
  padding: 0.25rem 1.5rem;
  background: red;
  /* border has to be solid, not RGBa */
  /* 9600px or equiv (600rem = 9600/16) */
  border-left: 600rem solid maroon;
  border-right: 600rem solid maroon;
}

See the Pen Full Width Bars Using Borders by Parker Bennett (@parkerbennett) on CodePen.

Given the fussiness of using borders, let's look at an alternative:

Using pseudo elements

By adding a :before pseudo-element to the heading, you can make the background behind it distinct from the full-width bar, using absolute positioning and a negative z-index. This also lets us use RGBa for the full-width bar.

.full-width-tinted {
  position: relative; /* for child pseudo-element */
  z-index: 0;
  margin: 0 -600rem;
  /* add back negative margin value */
  padding: 0.25rem 600rem;
  background: #666;
  background: rgba(0, 0, 0, 0.25);
}

.full-width-tinted:before {
  content: "";
  position: absolute;
  z-index: -1; /* behind parent */
  top: 0;
  bottom: 0;
  /* subtract h2 padding (1.5rem) */
  left: 598.5rem;
  right: 598.5rem;
  background: green;
}

See the Pen Full Width Bars Using Negative Margins by Chris Coyier (@chriscoyier) on CodePen.

You need to be a little careful with negative z-index, since it tends to work only in fairly simple situations without many nested elements with their own backgrounds.

If you want even more flexibility, you can use two pseudo-elements, :before and :after, to extend the header background both left and right. This way the background could include different color, widths, or heights, extend in only one direction, fade with a gradient, etc.

.full-width {
  position: relative; /* for the child pseudo-elements */
  /* negative offset = section padding */
  margin: 0 -30px;
  /* add back section padding value */
  padding: 0.25rem 30px;
  background: #333;
}
.full-width:before, .full-width:after {
  content: "";
  position: absolute;
  /* fill vertically */
  top: 0;
  bottom: 0;
  width: 9600px;
  right: 100%;
  background: green;
}
.full-width:after { 
  width: 320px;
  left: 100%;
  /* you're free to do something totally different here */
  background: green;
}

See the Pen Full Width Bars Using Pseudo-Elements by Chris Coyier (@chriscoyier) on CodePen.

Using box-shadow

A clever way to not trigger horizontal scrolling is to use box-shadow to generate the background extensions (at least on the right). Andreas created a demo of this:

See the Pen Full Width Browser Bars with box-shadow by Andreas (@receter) on CodePen.

Unfortunately, there are issues with this. Charles Stuart pointed out a bug in Internet Explorer 11 (v11.0.9600.17501) that makes using box-shadow unreliable. It appears IE is using sub-pixel antialiasing on the box-shadow even when blur is set to 0.

Using viewport units

Lisa suggested a way to not trigger horizontal scrolling by using a combination of vw and calc(), which now have pretty solid browser support. It could be useful in the right circumstance (but doesn't yet factor in things like padding or min-width).

See the Pen Full width bars by Chris Coyier (@chriscoyier) on CodePen.

There's a whole bunch of content on CSS-Tricks.

Search for Stuff   •   Browse the Archives

Get the Newsletter ... or get the RSS feed