WTF, HTML e CSS?

Razões pelas quais HTML e CSS podem fazer você dizer "Mas que *orra!". Uma lista com frequentemente frustrantes equívocos, situações difíceis e dilemas de HTML e CSS.

Criado por @mdo. Versão brasileira por webfatorial.

Conteúdos

Declare um doctype

Sempre inclua um doctype. É recomendado o doctype simples de HTML5:

<!DOCTYPE html>

Esquecer o doctype pode causar problemas com tabelas malformatadas, inputs e mais.

Matemática do Box Model

Elementos que têm width especificada se tornam maiores quando eles têm padding e/ou border-width. Para evitar esses problemas, faça o uso do agora comum box-sizing: border-box; reset.

Unidades rem e Mobile Safari

Apesar de Mobile Safari suportar o uso de rems em todos os valores de propriedades, ele não lida muito bem quando rems são usados com media queries e fica piscando infinitamente o texto da página em tamanhos diferentes.

Por enquanto, use ems no lugar de rems.

html {
  font-size: 16px;
}

/* Causa o "flashing bug" no Mobile Safari */
@media (min-width: 40rem) {
  html {
    font-size: 20px;
  }
}

/* Funciona lindamente no Mobile Safari */
@media (min-width: 40em) {
  html {
    font-size: 20px;
  }
}

Ajuda! Se você tiver um link para um relatório de bug Webkit ou Apple sobre isso, envie para que seja incluído aqui. Não temos certeza onde reportar isso já que só se aplica a Mobile, não Desktop, Safari.

Floats primeiro

Elementos com float devem sempre vir primeiro na ordem do documento. Elementos com float precisam de alguma coisa para envolvê-los ("wrap around"), do contrário podem causar um efeito "step down", aparecendo abaixo do conteúdo.

<div class="parent">
  <div class="float">Float</div>
  <div class="content">
    <!-- ... -->
  </div>
</div>

Floats e clearing

Se você usa float em algo, você provavelmente precisa usar clear nisso. Qualquer conteúdo que segue um elemento com float vai envolver ("wrap around") aquele elemento até que ele fique "cleared". Para dar clear em floats, use alguma das técnicas a seguir.

Use a micro clearfix para fazer o clear de floats com uma classe separada.

.clearfix:before,
.clearfix:after {
  content: " ";
  display: table;
}
.clearfix:after {
  clear: both;
}

Alternativamente, especifique overflow com auto ou hidden no elemento-pai.

.parent {
  overflow: auto; /* clearfix */
}
.other-parent {
  overflow: hidden; /* clearfix */
}

Tenha cuidado, pois overflow pode causar alguns efeitos colaterais, tipicamente em relação a elementos posicionados dentro do elemento-pai.

Pro-Tip! Mantenha seu futuro você e seus colegas de trabalho felizes incluindo um comentário do tipo /* clearfix */ quando der o clear em floats, já que a propriedade pode ser usada por outras razões.

Floats e altura computada

Um elemento-pai que tem somente conteúdo float irá ter uma altura computada height: 0;. Adicione um clearfix a esse elemento-pai para forçar os navegadores a computar a altura.

"float" é nível de bloco

Elementos com float automaticamente se tornam display: block;. Não especifique ambos.

.element {
  float: left;
  display: block; /* Não necessário */
}

Fato engraçado: Anos atrás, nós tínhamos que especificar display: inline; para a maioria dos floats funcionarem apropriadamente em IE6 e evitar o double margin bug. Mas são águas passadas.

Colapso de margens verticais adjacentes

Margens superiores e inferiores podem e irão colapsar em muitas situações, mas nunca por elementos posicionados com float ou absolutamente. Leia esse artigo na MDN para saber mais.

Margens horizontais adjacentes nunca colapsam.

Estilizando linhas de tabela

Linhas de tabela, <tr>s, não recebem borders a menos que você especifique border-collapse: collapse; no elemento-pai <table>. Além disso, se o <tr> e filhos <td> ou <th> tiverem a mesma border-width, as linhas não terão suas bordas aplicadas. Veja este JS Bin para um exemplo.

Firefox e botões <input>

Por razões desconhecidas, Firefox continua aplicando estilos para <input>s dos tipos submit e button que não podem ser sobrescritos via CSS personalizado. Prefira elementos <button>.

<!-- Não tão bom -->
<input type="submit" value="Save changes">
<input type="button" value="Cancel">

<!-- Superbom em qualquer lugar -->
<button type="submit">Save changes</button>
<button type="button">Cancel</button>

Alguns dos estilos do Firefox podem ser sobrescritos com este snippet de CSS:

input::-moz-focus-inner {
  border: 0;
  padding: 0;
}

Entretanto, como David Walsh apontou, isso não resolve tudo. Apenas use o elemento <button>.

Boas novas! Parece que uma correção pra isso deve vir com o Firefox 30. São boas nova para nossos futuros eus, mas isso não vai consertar nada em versões antigas.

Sempre especifique um type para <button>s

O valor padrão é submit, significando que qualquer botão em um formulário pode submeter este formulário. Use type="button" para qualquer coisa que não seja submeter o formulário e type="submit" para o que o submete.

<button type="submit">Salvar altera&ccedil;&otilde;es</button>
<button type="button">Cancelar</button>

Para ações que demandam um <button> e não estão em um formulário, use type="button".

<button class="dismiss" type="button">x</button>

Fato engraçado: Aparentemente o IE7 não suporta apropriadamente o atributo value em <button>s. Ao invés de ler o conteúdo do atributo, ele "puxa" a partir do innerHTML (conteúdo entre a tag de abertura e fechamento de <button>). Entretanto, isso não precisa ser visto como grande coisa por duas razões: o uso de IE7 está caindo cada vez mais e parece bastante incomum especificar ambos, value e innerHTML, em <button>s.

Limite de seletor do Internet Explorer

Internet Explorer 9 e abaixo podem ter o máximo de 4.096 seletores por folha de estilo. Eles também tem um limite de 31 folhas de estilo combinadas e inclusões de <style></style>. Qualquer coisa acima desse limite é ignorado pelo navegador. Escolha entre dividir o CSS ou começar a refatorar. Sugerimos a última.

Como uma valiosa nota, aqui está como os navegadores contam seletores:

/* Um seletor */
.element { }

/* Dois seletores */
.element,
.other-element { }

/* Três seletores */
input[type="text"],
.form-control,
.form-group > input { }

"position" explicada

Elementos com position: fixed; são colocados relativamente à viewport do navegador. Elementos com position: absolute; são colocados relativamente ao elemento-pai mais próximo que tenha posicionamento diferente de static (e.g., relative, absolute, ou fixed).

"position" e "width"

Não especifique width: 100%; em um elemento que tenha position: [absolute|fixed];, left e right. O uso de width: 100%; é o mesmo que o uso combinado de left: 0; e right: 0;. Use um ou outro; não ambos.

Posição "fixed" e transforms

Navegadores quebram position: fixed; quando um elemento-pai de um elemento tem transform especificado. Usar transforms cria um novo "bloco que contém", efetivamente forçando o elemento-pai a ter position: relative; e o elemento fixo a se comportar como position: absolute;.

Veja a demo e leia o post de Eric Meyer sobre o assunto.