Blog da Improve It

Margens na tag HR: um caso resolvido

Publicado por Leandro Mello há aproximadamente 1 ano.

Basicamente, o tapa na cabeça que faz o IE se comportar direitinho é o display: block. Depois dessa, você põe a margem que quiser no <hr />.

Sou dos que usa a tag <hr /> para dividir seções. É semanticamente mais coerente do que separar usando as próprias bordas dos divs, fica melhor quando se vê o HTML sem CSS, essas coisas que já se discute à beça por aí. Aprendi a apreciar sua utilidade.

Mas o fato é que é enjoado dar estilo a esta tag. Para cada browser devem-se editar atributos diferentes para se alcançar um mesmo efeito. Enfim, com algum jeitinho, deixa-se o <hr /> com a mesma cara em todos os browsers.

O que mata mesmo, o que deixa fóruns de cabelo em pé é a tal margem que fica em volta do <hr /> no IE. A margem no IE é teimosa, indestrutível, incontrolável, inestilizável. Por todo lugar que eu procurasse, as pessoas decidiam seguir com as margens mesmo, e os mais persistentes chegavam ao ponto de inserir o <hr /> num <div> para conseguir dar o estilo que quiserem. Ou seja, uns desistiam muito fácil, outros recorriam a medidas desesperadas para fazer a coisa de qualquer jeito.

Um dia encontrei dois posts que se aproximavam da solução. Sozinho, nenhum dos dois resolvia o caso. Mas juntando os dois funcionava! Fiz os testes e, de fato, misturando os dois raciocínios eu conseguia a medida que eu quisesse em qualquer browser que fosse — até zero. Mas, numa jogada um tanto infeliz, não salvei os posts nos favoritos, nem salvei o arquivo de testes. Uma resposta bastante procurada, e eu a perdi de bobeira...

Este artigo é sobre o meu caso: eu queria usar o <hr /> livre de divs sem sentido, e com margem zero; até no IE. E, sobretudo, o artigo é para deixar a resposta num lugar seguro e ao alcance de todos. Agora que eu lembrei a solução, não perco mais.

“Pescotapa”

Depois que perdi de vista os posts e as semanas se passaram, tudo o que eu lembrava é que o Internet Explorer precisava de um tapa na cabeça: era um atributo nada intuitivo e totalmente inesperado, que surpreendentemente o fazia acordar para a realidade. Basicamente, o tapa na cabeça que faz o IE se comportar direitinho é o display: block. Depois dessa, você põe a margem que quiser no <hr />.

Vai entender. O <hr /> já é um elemento em bloco. Tem toda a cara de elemento em bloco. Pula linha em cima, pula linha embaixo, não tem nada de inline. E é por isso que os fóruns tanto rodam atrás de uma solução: poucos suspeitariam que é necessário lembrar a ele que de fato é um bloco. Dizer display: block seria redundante e desnecessário. Mas o danado do IEca faz questão disso para se dar conta de que “ah, é, é verdade...”.

Como aconteceu comigo:

Vamos ao meu caso. Eu pretendia inserir umas tags <hr /> no site da fotógrafa Patricia Figueira, como experimento para implementação futura no próprio site e em nosso produto em desenvolvimento, o beonthe.net.

O primeiro passo foi inserir tags <hr /> nos espaços entre cabeçalho, conteúdo principal e rodapé. Vamos ao HTML:

<div id="header">
  (conteúdo do cabeçalho)
</div>
<hr />
<div id="main_content">
  (conteúdo principal)
</div>
<hr />
<div id="footer">
  (conteúdo do rodapé)
</div>

E logo surgiu a linha cinza entre os divs. Ficou assim:

Safari: O hr foi inserido corretamente.

Opera: O hr também foi inserido corretamente.

Firefox: O hr foi inserido corretamente como os outros.

Internet Explorer 7: O hr foi inserido corretamente, mas deixou margens que afastaram os conteúdos dos divs.

Internet Explorer 6: O hr foi inserido corretamente, mas também deixou as mesmas margens que afastaram os conteúdos dos divs.

(Ah, o IE...)

Linhas inseridas, eu queria que cada divisória tivesse as seguintes características:

A primeira tarefa é simples. Na folha de estilos application.css, usamos:

hr {
  clear:      both;
}

Nota: Depois vi que, para limpar de fato os floats do div#main_content, foi necessário pôr os hr’s dentro do #main_content, em suas extremidades, e não adjacentes a ele. Mas isto é papo para toda uma outra discussão e, para efeito de ilustração, vou continuar exibindo os hr’s como se estivessem entre o #main_content e o #header.

Agora vem a segunda tarefa: limpar os espaços que a linha gera no IE. Mas, como dito antes e tantas vezes blogs afora, a margem do hr no IE é turrona, implacável, indelével, inoxidável. Tem gente tentando de tudo: margin: 0, padding: 0, line-height: 0, font-size: 0, até overflow: hidden. Zerar as margens até ajuda com os browsers que seguem os padrões, mas nada acaba com o espaçamento no IE.

Então, em nosso application.css, cuidemos dos browsers inteligentes para garantir que não haja margens:

hr {
  clear:      both;
  margin:     0;
}

A explicação para as margens insistentes do IE é a seguinte: o IE renderiza o <hr /> com uma margem vertical de 7px a mais do que os outros browsers. Ou seja, se você escreve...

hr {
  clear:      both;
  margin:     7px 0;
}

... Você tem num browser decente o esperado: Exatamente 7px de espaço para cima e 7px para baixo.

E no IE, uma soma do que você inseriu mais a margem nativa: 14px para cima e 14px para baixo (7 + 7).

“Ora”, posso pensar, “Então é só escrever margens negativas para o IE”. Seria o intuitivo, mas o IE não trata de coisas como “o intuitivo”. Não funciona: o IE tira a margem superior, mas jamais elimina a margem inferior! Veja: O hr fica zerado em cima, mas o espaço de baixo dobra: ele assumiu os 7px de cima!

O pulo do gato

Entra na nossa folha de estilos application.css o atributo display: block:

hr {
  clear:      both;
  margin:     0;
  display:    block;
}

Este é o último atributo em que eu pensaria para eliminar margens. Não sei explicar por que dá certo. É contra-intuitivo. É redundante. É idiota. E mesmo assim (ou talvez por isso mesmo) faz o IE acordar.

Agora sim, podemos repensar a questão das margens negativas. Então corrigimos o application.css:

hr {
  clear:      both;
  margin:     -7px 0;
  display:    block;
}

E voilà! Finalmente, um <hr /> sem margens! Não é ilusão! É possível eliminar totalmente as margens de um hr no IE!

Comentário condicional:

O problema de usar margens negativas é que os browsers inteligentes as interpretam como elas são — negativas: Margens negativas no Safari.

Temos que definir um comportamento para cada tipo de browser.

O que eu costumo usar são comentários condicionais. Há quem diga que eles são maus, mas até agora só têm me ajudado.

Vamos ao <head> no HTML. Lá inserimos uma condição para o IE:

<!--[if IE]>
  <link href="/stylesheets/application_ie.css" media="all" rel="stylesheet" type="text/css" />
<![endif]-->

Ela serve para chamar uma folha de estilo com regras específicas para o Internet Explorer: application_ie.css.

Nesta nova folha application_ie.css, aplicamos as margens negativas que tínhamos até então:

hr {
  margin:     -7px 0;
}

E pronto. Esta é a única regra que diferencia o <hr /> nos browsers inteligentes e no Internet Explorer. Agora podemos zerar as margens no nosso application.css:

hr {
  clear:      both;
  margin:     0;
  display:    block;
}

E o resultado é: consenso! Nos browsers inteligentes: (Aparência do hr no Safari. É a mesma do IE!)

... e no IE: (Aparência do hr no IE6. Agora o hr de todos os browsers tem a mesma aparência.)

Acabamento

Resta a terceira tarefa, que é deixar invisíveis as linhas divisórias. Isso é fácil com visibility: hidden:

hr {
  clear:      both;
  margin:     0;
  display:    block;
  visibility: hidden;
}

O que vai fazer com que o <hr /> fique invisível, mas ainda ocupando o espaço que ocupava antes, de modo que fica uma lacuna de 2 pixels entre o cabeçalho e o conteúdo principal: O hr agora sumiu, mas ainda ocupa o espaço que ocupava antes. Temos que nos livrar da lacuna.

Nota: Usar display: none não é útil neste caso porque cancela o atributo clear: both. E nós queremos que ele quebre os floats. Neste caso, é melhor ficar com visibility: hidden.

Nos browsers decentes, esta lacuna é devida à espessura da borda. Bordas de 1px em volta de um elemento com altura zero resultam em 2px de altura total. Um aributo de borda deve resolver:

hr {
  clear:      both;
  margin:     0;
  display:    block;
  visibility: hidden;
  border:     none;
}

A lacuna some no Safari e no Opera. Mas Firefox e IE precisam de mais uma forcinha. Lembre-os que a altura do elemento deve ser nula:

hr {
  clear:      both;
  margin:     0;
  display:    block;
  visibility: hidden;
  border:     none;
  height:     0;
}

No Firefox resolve. Mas no IE a lacuna, em vez de sumir, fica reduzida a 1px (Ah, o IE...). Isso porque nele as bordas não são definidas pelo atributo border, mas pelo atributo color (já mencionei que o IE não trata de coisas como “o intuitivo”?). Como não dá para “zerar” a cor para sumir com a lacuna, o jeito é retocar as margens que deixamos no application_ie.css, contribuindo para as margens negativas com 1px em cima ou em baixo:

hr {
  margin:     -8px 0 -7px 0;
}

E eis o resultado final: Em todos os browsers, uma linha divisória sem margens e invisível. Missão cumprida.

Conclusão

Este foi o meu caso bem específico com a tag <hr />. Se você for uma das pessoas que se descabelavam por causa das margens teimosas no IE, saiba que há solução. Não é preciso desistir e adaptar seu precioso layout para se conformar com as margens, nem é preciso inserir a tag num <div> só para ela. Boas práticas já!

Espero ter contribuído para diminuir sua dor de cabeça. A resposta está agora aqui, para todo o mundo. E breve você a verá implementada no site da Patricia Figueira e no beonthe.net. Mas se este post não resolver o seu caso específico, escreva. Vamos juntos pensar em soluções para dominar o <hr /> — e, quem sabe, só quem sabe, o Internet Explorer.

Tags , , , ,  | 7 comentários