How do I offset where my fixed nav bar takes me?

Problem

I have a fixed navigation bar on my website that stays at the top with links that take me to different sections further down the page. However, because my fixed nav bar has a height of 40px, the beginning 40px of every section is covered up. How would I offset where my links take me by 40px using either HTML or CSS? Thanks.

Problem courtesy of: Brian

Solution

You might try absolutely positioning "dummy" anchors 40 pixels above the top of each section. You can give them zero width/height and hidden visibility to ensure that these anchors don't affect how your page is displayed. When the user clicks one of the links in your fixed navigation bar, the window will scroll to the top of the dummy anchor, 40 pixels above the beginning of its actual section.

Example HTML:

<div class="navbar">
  <a href="#anchor1">Anchor 1</a>
  <a href="#anchor2">Anchor 2</a>
  <a href="#anchor3">Anchor 3</a>
</div>
<div class="section">
  <span id="anchor1" class="anchor"></span>
  Section Content
</div>
<div class="section">
  <span id="anchor2" class="anchor"></span>
  Section Content
</div>
<div class="section">
  <span id="anchor3" class="anchor"></span>
  Section Content
</div>​

Example CSS:

body {
    padding-top: 40px;
}
.navbar {
    position: fixed;
    width: 100%;
    height: 40px;
    top: 0;
    left: 0;
    z-index: 10;
    border-bottom: 1px solid #ccc;
    background: #eee;
}
.section {
    position: relative;
}
.anchor {
    display: block;
    position: absolute;
    width: 0;
    height: 0;
    z-index: -1;
    top: -40px;
    left: 0;
    visibility: hidden;
}

For a working example, see http://jsfiddle.net/HV7QL/

Edit: CSS3 also includes the :target pseudo-class, which applies to an element whose id has been referenced by the href of a link in the document, or the hash value of the URL. You can apply a 40-pixel padding to the top of the :target that will be applied only to the section the user selects from the fixed navbar.

Example CSS:

.section:target {
    padding-top: 40px;
}

This is semantically cleaner than the method described above, but won't work on older browsers.

Working example: http://jsfiddle.net/5Ngft/

Solution courtesy of: Aaron

Discussion

I just happened to stumble across this problem myself today so I had been thinking about it for a bit already, but I think I just found a solution:

Add a padding-top: 40px; margin-top: -40px to the element that you want to jump to. The negative margin cancels the padding, but the browser still thinks that the top of the element is 40px higher than it actually is (because in fact it is, only the content of it starts lower).

Unfortunately, this might collide with already set margins and paddings, and if you're using a background on the targeted element it's going to mess it all up.

I'll see if I can work around that and post a jsfiddle, but in the meantime here's a basic answer at least :)

edited: I thought I had a solution for the background, but it didn't work. Removed again.

final edit: It does kind of work if you know the background color of the wrapping element. In my example I know the text is on a white background, but the titles have a silver background. To prevent the title from having a background on the extra padding we set, instead I put it on a pseudo-element before it:

#three:before { 
    content: " ";
    background: white;
    display: block;
    margin-top: -40px;
    padding-top: 40px;
}

This way the extra padding has a white background again, but this only works if you already know what background it needs. Setting it to transparent for example will show the underlying background of the title itself, not of the article.

jsFiddle: http://jsfiddle.net/Lzve6/ Heading one is the default one you're having problems with. Heading two is my first solution, guaranteed to work on almost all browsers Heading three is using the :before pseudo-element, might not work on older browsers.

Discussion courtesy of: Stephan Muller

This recipe can be found in it's original form on Stack Over Flow.