CSS position: fixed;

Uma das práticas mais comuns atualmente é ter uma barra de navegação fixa em um website/webapp. Seja no topo, laterais ou rodapé. Na minha opinião, essa prática se tornou ainda mais popular quando navegadores mobile passaram a suportar o valor fixed e também com o advento dos single-page websites — que na realidade não me agradam muito por inúmeros motivos (SEO, performance etc).

Um método que se tornou bem popular para implementar o comportamento fixed era como o jQuery Mobile fazia: o elemento supostamente fixo tinha position: absolute; top: 0; e todo o resto era feito com JavaScript, no onScroll o elemento era escondido (display: none;) e no onScrollEnd a posição era recalculada e o elemento era mostrado novamente. Ou seja, bem hacky!

Há um artigo sobre position: fixed; escrito pelo Brad Frost que, apesar de bem antigo (2011), ainda tem informações relevantes.

Então, como fazer para que uma barra de navegação seja fixa?

.navbar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
}

Simples, não? O código acima irá funcionar na maioria dos casos, mas a depender do que existe no restante da sua página, problemas com scrolling serão frequentes.

Segue um exemplo de como a sidebar da MDN é renderizada no Google Chrome:

CSS position: fixed bug

Esse problema é muito comum e já afetou, inclusive, o site do Globo Esporte há uns meses atrás.

Solução

Mas como resolvê-lo? A forma mais simples e provavelmente mais utilizada é:

.navbar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  transform: translateZ(0);
}

Dessa forma, nós colocamos .navbar em sua própria compositing layer. Sendo assim, transform: translate3d(0,0,0); ou backface-visibility: hidden; também poderiam substituir o transform: translateZ(0);. Infelizmente, isso não resolve todos os problemas, porque se tivermos:

.navbar-child {
  position: fixed;
}

Esse elemento será fixo com relação à mesma compositing layer (provavelmente .navbar) e não com relação ao documento/página, como era de se esperar, vide bug. Na maioria dos casos isso realmente não apresenta um problema crítico, já que não é todo dia que temos fixedception :)

Vale lembrar que esse hack — sim, eu considero isso tão feio quanto o position: relative; na época do IE6 — é válido para Blink/Webkit. Em um artigo chamado Scrolling Performance, o Paul Lewis explica muito bem o motivo desse comportamento estranho.

Futuro

Há uma especificação CSS (working draft, no momento) que visa corrigir essas e outras discrepâncias: will-change. Essa propriedade vai permitir que nós - desenvolvedores - possamos dizer/indicar ao navegador quais tipos de mudanças são esperadas em determinado elemento, assim os browsers podem executar otimizações antes mesmo dos elementos alterarem. Atualmente apenas Chrome 36+ e Firefox 36+ suportam will-change.