Pular diretamente para a navegação
Entendendo CSS
Como vimos no artigo anterior, a primeira etapa na criação de um documento com os padrões Web é a sua correta estruturação. A segunda etapa é o uso apropriado do CSS para formatar o documento de modo que o mesmo receba o layout intencionado sem precisar mudar a sua estruturação.
Antes de estudar exemplos específicos de como isso pode ser feito, é necessário entender um pouco sobre o CSS e os problemas de implementação por trás do mesmo. O objetivo deste artigo, então, é introduzir o padrão, analisando a composição de uma folha de estilo e os diversos conceitos relacionados, e explicar alguns dos problemas mais comuns e suas soluções.
Caso você já conheça bem o padrão, você pode optar por pular este texto. De qualquer forma, este artigo serve como um revisão dos conceitos preparando para as aplicações práticas que serão dadas nos artigos futuros.
O que é CSS?
O CSS, ou Cascading Style Sheets, é uma especificação que define um mecanismo simples para a adição de estilos (ou seja, elementos de apresentação visual como fontes, cores e espaçamento) a um documento. A especificação inicial, chamada CSS1, tornou-se uma recomendação do W3C no final de 1996 e define praticamente todos estilos de apresentação utilizados hoje em dia. A segunda versão da especificação, chamada CSS2, foi aprovada em 1998 e acrescentou vários estilos novos, principalmente na área de posicionamento e tabelas. Uma nova versão está em desenvolvimento e espera-se que seja aprovada em um futuro próximo.
O suporte a CSS nos navegadores em uso atualmente está razoavelmente solidificado. A maioria dos navegadores visuais suporta a especificação CSS1 completa. A especificação CSS2, entretanto, é suportada em uma escala bem menor. Navegadores de ponta, como o Mozilla e Opera, possuem um suporte quase completo, mas navegadores que não recebem atualizações há um bom tempo, como o Internet Explorer, ainda apresentam muitos problemas e, em alguns casos, uma completa falta de suporte para determinadas partes da especificação. Apesar disso, é possível fazer uso da maior parte da especificação sem muitos inconvenientes aceitando uma certa degradação na apresentação em navegadores com menor suporte. E, como sites recentes demonstram, o que se pode usar da especificação é mais do que suficiente para criar sites que são bem estruturados e possuem uma excelente apresentação visual. O maior problema, realmente, são os defeitos de implementação que exigem um maior esforço para contornar. Esse assunto será visto em mais detalhes mais adiante.
Usando CSS
O básico
Como mencionado acima, o CSS é uma especificação simples e os conceitos básicos podem ser entendidos rapidamente.
Um grupo de estilos CSS é normalmente referido como a folha de estilos de um documento, estejam estes contidos no próprio documento ou em um arquivo à parte. Essa será a convenção utilizada neste artigo.
O bloco básico de uma folha de estilo é a regra. Uma regra define como um ou mais estilos são aplicados a um determinado elemento ou conjunto de elementos e é composta por duas partes: o seletor e um grupo de declarações. O seletor indica a quais elementos a regra se aplica e as declarações indicam quais são os estilos a serem aplicados. Uma declaração, por sua vez, também é composta por duas partes: uma propriedade e um valor. A propriedade indica qual estilo individual está sendo modificado do seu valor padrão e o valor, obviamente, indica qual novo valor será aplicado.
Um exemplo, retirado na própria folha de estilos deste documento, é:
pre
{
background-color: #eeeeee;
border: 1px solid #aaaaaa;
padding: 5px;
}
No exemplo acima, o seletor é pre. Nesse caso, ele indica que os estilos especificados na declaração da regra aplicam-se a todos elementos pre, independentemente de onde apareçam no documento. As chaves englobam o conjunto de declarações da regra e cada declaração é separada da próxima por ponto e vírgula. O CSS ignora espaços em branco, de modo que as declarações podem ser colocadas na mesma linha ou em linha separadas à vontade do seu criador. Eu costumo usar o modo acima para conferir mais legibilidade às declarações e para facilitar modificações e comentários.
Continuando no exemplo acima, vemos que a regra é composta por três declarações diferentes. A primeira regra, que é bem auto-explicativa, indica que a cor de fundo do elemento deve ser #eeeeee. A segunda declaração adiciona uma borda ao redor de elemento que terá 1 pixel de largura e cuja cor será #aaaaaa. Além disso, a última declaração indica que deve haver um preenchimento de 5 pixels entre o conteúdo e a borda.
Essas três declarações mostram que cada propriedade CSS afeta algum pequeno aspecto da apresentação de um elemento. A especificação CSS2 define mais de 100 diferentes propriedades, mas a maioria delas são de fácil entendimento e podem ser aprendidas rapidamente com a prática.
Completando o exemplo acima, deve ser notado que o mesmo apresenta duas propriedades declaradas em sua forma compacta. A propriedade padding, por exemplo, é na verdade uma atalho para a declaração de quatro propriedades diferentes (padding-top, padding-right, padding-bottom e padding-left) que indicam o preenchimento de cada lado do elemento. Usando a propriedade padding, então, é possível especificar de uma só vez todos os valores ou mesmo os valores do pares ou os valores individuais sem ter que declarar todas as quatro propriedades. Da mesma forma, a propriedade background é composta de várias outras das quais uma, background-color, é usada no exemplo acima. Por fim, a propriedade border, também usada acima, é um exemplo de propriedade composta que especifica nada menos do que 11 propriedades simultaneamente. A opção de usar uma propriedade compacta ou usar cada parte da propriedade fica a cargo desenvolvedor em vista do efeito desejado.
A cascata
Antes de entrar nos detalhes de seletores e propriedades, é preciso entender como o CSS funciona e como ele é aplicado aos elementos.
Você provavelmente deve ter se perguntado alguma vez o porquê do Cascading no nome da especificação. A resposta está na modularidade do CSS. De modo a permitir o aproveitamento máximo, o CSS permite que várias folhas de estilo sejam aplicadas a um documento e que várias regras se referiram a múltiplos elementos.
Para que isso funcione muitas vezes será preciso resolver os conflitos que porventura existam entre várias regras. Assim, é preciso embutir alguma espécie de precedência de regras -- a exemplo das operações matemáticas. A especificação, então, atribui um peso a cada uma das formas pelas quais um estilo é especificado para um elemento. Por exemplo, estilos especificados pelo usuário do documento possuem a maior importância do que estilos especificados pelo criador do documento. Da mesma forma, estilos importados por um folha de estilo possuem menor peso do que o estilos especificados na própria folha. O estilo final de um elemento, portanto, é determinado pela aplicação de cada regra em ordem inversa de peso. Ou seja, estilos menos importante são aplicados primeiro e podem ser sobrepostos por estilos de maior importância. Essa maneira de resolver conflitos causa um efeito de cascata na aplicação dos estilos, e daí vem o Cascading do nome da especificação.
Para entender melhor como os estilos são aplicados, dependendo de onde aparecem, você pode referir-se à especificação. Uma outra parte da resolução de conflitos, conhecida como especificidade, será explicada mais adiante.
O modelo de blocos
Um outro conceito importante também para entender como CSS funciona é o modelo de blocos (box model). O CSS usa um modelo simples de formatação orientado a blocos que especifica que cada elemento formatado resulta em um ou mais blocos (ou caixas) retangulares. Além disso, esse modelo também especifica que todos esses blocos possuem três outros blocos circundantes que são, respectivamente, margem (margin), borda (border) e preenchimento (padding).
A figura abaixo mostra, de uma maneira simplificada, um bloco e suas áreas circundantes. Obviamente, as áreas estão exageradas para fins de exibição.
Cada uma dessas áreas possui regras específicas que devem ser entendidas para uma boa utilização das mesmas. Um exemplo dessas diferenças é relativa à cor ou imagem de fundo de um elemento. Por exemplo, a área de preenchimento de um objeto usa a mesma propriedade do elemento. A borda, entretanto, quando existe, usa as propriedades específicas dadas para a mesma. Por fim, as margens sempre são transparentes de modo que a cor ou imagem de fundo do elemento pai aparecem na mesma. Esses e outros detalhes também podem ser obtidos na especificação.
Em relação ao modelo de blocos, existem basicamente dois tipos de elementos: elemento do tipo bloco (block-level elements) e elementos em linha (inline elements). Essa distinção é muito importante na hora de aplicar os estilos de um elemento e entender como as áreas circundantes de cada elemento funcionam.
Os elementos do tipo bloco, como o nome indica, formam um bloco completo que assume a largura máximo do elemento que os contém e a altura do seu próprio conteúdo. Esses elementos "empilham" no fluxo do documento começando um novo bloco imediatamente após o elemento anterior. Por exemplo, um elemento p, que é um elemento de bloco, localizado na raiz do documento, assume a largura total do documento que, no caso, é a largura da própria janela do navegador. Um elemento p seguinte "empilharia" novamente no fluxo, começando um outro bloco com a largura do documento.
Os elementos em linha, por outro lado, aparecem dentro do próprio conteúdo do elemento pai que os contém. Um elemento em, por exemplo, que especifica ênfase, aparece no meio do conteúdo de seu elemento pai (um elemento p, por exemplo). Ao contrário dos elementos de bloco, eles não "empilham" no fluxo do documento e acompanham qualquer conteúdo em que estejam inseridos.
Como você pode observar no parágrafo anterior, a primeira ocorrência da palavra em no mesmo é circundada por um elemento code, indicando que ela é parte do código de alguma linguagem. Como o elemento é em linha, o conteúdo do mesmo acompanha o conteúdo do elemento pai, sem quebrar o fluxo geral do mesmo. Por outro lado, os cabeçalhos da seções deste documento são elementos do tipo bloco e, consequentemente, formam blocos completos, separados tanto do conteúdo dos elementos anteriores como dos posteriores. A figura abaixo ilustra isso de uma maneira melhor.
Existem outros tipos de elementos que na verdade são variações ou casos particulares do tipos acima. Por exemplo, elementos do tipo lista (list-item elements) são elementos que acrescentam um marcador automático no início do conteúdo. Existem também elementos flutuantes (float elements) que saem do fluxo geral do fluxo geral do documento e "empurram" o conteúdo do elemento que os contém em uma determinada direção. Também existem elementos cujo conteúdo é substituído (replaced-content elements), como é o caso do elemento img, cujo conteúdo é, na verdade, a imagem para qual ele aponta. Todos esses tipos citados funcionam na prática como elementos do tipo bloco.
Qualquer elemento HTML (ou XHTML) possui um tipo, definido pela especificação, com determinadas propriedades padrão que são usadas caso não sejam sobrepostas por uma folha de estilo. O CSS, então, permite que essas propriedades padrão sejam alteradas para criar a apresentação requerida. O próprio tipo de um elemento pode ser alterado para criar determinados tipos de apresentação, embora isso deva ser usado com mais cuidado.
Seletores
Como explicado anteriormente, os seletores indicam a quais elementos uma determinada regra se aplica. Existem dezenas de formas de se combinarem elementos na especificação de um seletor e isso dá um grande poder de modularização ao CSS. Além disso, existem algumas formas de seletores especiais que fazem uso de dois atributos comuns a todos elementos -- os atributos id e class. Um outro tipo de seletores, conhecidos como pseudo-seletores, indicam classes especiais de propriedades de um objeto. Um exemplo de pseudo-seletor é :hover que indica o estilo de um elemento quando o mouse está sobre o mesmo.
Nota: Nos exemplos seguintes, as reticências indicam um conjunto qualquer de declarações. Mantenha em mente também, que algumas das combinações se referem à especificação CSS2 e não são implementadas por todos os navegadores.
Como vimos, a forma mais simples de seletor é aquela que especifica apenas um elemento. Por exemplo:
h1 { ... }
Elementos podem ser também agrupados na regra. Por exemplo:
h1, h2, h3 { ... }
A regra acima, por exemplo, indica que todos elementos h1, h2 e h3 possuem as propriedades especificadas no bloco de declarações seguinte.
Relações entre os elementos também podem ser especificadas. Por exemplo:
h1 h2 { ... }
h1 > h2 { ... }
h1 + h2 { ... }
A primeira linha do exemplo acima indica que as declarações se aplicam a todos elementos h2 que são descendentes de qualquer elemento h1. A segunda linha indica que as declarações de aplicam a todos elementos h2 que são filhos de qualquer elemento h1. Por fim, a última linha indica que as declarações se aplicam a todos elementos h2 que seguem imediatamente um elemento h1. Note a diferença entre descendente e filho: o primeiro é um elemento que aparece em qualquer posição hierárquica dentro de outro elemento; o segundo aparece na posição hierárquica imediatamente abaixo de seu pai.
Uma outra forma se criar um seletor é usar, como mencionado acima, o atributo class de um elemento. Por exemplo:
.footer { ... }
div.footer { ... }
A primeira linha do exemplo acima indica que as declarações se aplicam a qualquer elemento cujo atributo class seja footer. A segunda linha restringe as declarações a qualquer elemento div cujo atributo class seja footer. O atributo class de um elemento qualquer pode especificar múltiplas classes.
O atributo id, como também foi mencionado acima, também pode ser usado para criar um seletor. Por exemplo:
#footer { ... }
div#footer { ... }
As mesmas regras para o atributo class se aplicam aqui com as seguintes exceções: um elemento só pode ter um único atributo id e o atributo id deve ser único no documento.
Finalmente, seletores podem ser especificados por meio de pseudo-classes. Esses pseudo-classes recebem esse nome por simularem partes ou eventos específicos para um elemento. Por exemplo:
a:hover { ... }
p:first-line { ... }
Na primeira linha, as declarações serão aplicadas a qualquer elemento a quando o mouse estiver sobre o mesmo. Na segunda linha, as declarações serão aplicadas à primeira linha de qualquer elemento p. As demais linhas não receberão os estilos especificados.
Os seletores podem ser combinados de qualquer forma necessária. Por exemplo:
body > ul.links li#root:hover { ... }
No exemplo acima, as declarações seriam aplicadas a qualquer elemento li no estado hover (ou seja, com o mouse em cima do mesmo) cujo atributo id fosse root e que descendesse de um elemento ul cujo atributo class fosse links e que fosse, por sua vez, um filho de um elemento body. Como você pode ver, a regra é complexa e mostra que o CSS é realmente muito poderoso. Identificar e criar essas regras pode ser uma tarefa complexa. Uma ferramenta muito útil para qualquer desenvolvedor CSS é o Selectoracle, que explica qualquer regra CSS em uma linguagem acessível (o idioma pode ser inglês ou espanhol).
A especificação CSS possui ainda maneiras de formar outros tipos de seletores, usando, por exemplo, qualquer outro atributo de um elemento ou sua posição na cadeia hierárquica. Esses seletores ainda não são suportados por todos navegadores, mas podem ser entendidos e aprendidos na especificação.
Aplicando o CSS ao elemento
Um estilo CSS pode ser aplicado a um elemento de várias maneiras. O interpretador CSS de um renderizador qualquer irá descobrir todas as regras que se aplicam a um determinado documento e aplicá-las ao mesmo para gerar a sua apresentação final. Esse estilo é geralmente chamado de estilo computado do elemento.
Estilos que aparecem em seletores que se referem aos elementos são automaticamente associados ao elemento em questão de acordo com as regras de precedência. Por outro lado, a aplicação de estilos cujos seletores especificam atributos class ou id, dependem, obviamente do uso desses atributos. Por exemplo:
<div id="p255" class="post first">...</div>
No exemplo acima, se algum seletor para o atributo id #p255 ou algum seletor para os atributos class .post ou .first existir, os estilos correspondentes serão aplicados ao elemento na sua ordem de precedência.
Uma questão que se levanta aqui é como o CSS consegue eliminar ambigüidades entre dois seletores que se aplicam a um determinado elemento.
Considere as regras CSS abaixo:
h2 { color: blue; }
h2.date { color: green; }
Considere também o seguinte fragmento de código XHTML:
<h2 class="date">...</h2>
Dadas as regras anteriores, qual será a cor aplicada ao conteúdo do elemento? Azul ou verde? Para resolver esse tipo de problema, a especificação determina uma mecanismo de resolução de conflitos chamado de especificidade. Esse mecanismo diz que regras mais específicas tem maior prioridade, ou seja, quanto mais detalhada a regra, maior a sua prioridade. No caso acima, o conteúdo do elemento seria verde já que a segunda regra é mais específica que a primeira, por indicar um atributo class.
A especificação fornece algumas regras simples para determinar a especificidade de uma regra qualquer simulando um sistema de numeração com uma grande base. Assim, para determinar qual é o estilo final de um elemento, encontre primeiro todas as regras que o afetam. Calcule então a especificidade de cada uma delas e aplique todas da menor para a maior especificidade. Para calcular a especificidade, faça o seguinte:
- Conte o número de atributos
idno seletor e chame esse valor de a. - Conte o número de atributos
classno seletor e chame esse valor de b. - Conte o número de tags no seletor e chame esse valor de c.
- Concatene os três números na forma abc e considere esse número a especificidade.
Por exemplo (diretamente da especificação):
li {...} /* a=0 b=0 c=1 -> especificidade = 1 */
ul li {...} /* a=0 b=0 c=2 -> especificidade = 2 */
ul ol li {...} /* a=0 b=0 c=3 -> especificidade = 3 */
li.red {...} /* a=0 b=1 c=1 -> especificidade = 11 */
ul ol li.red {...} /* a=0 b=1 c=3 -> especificidade = 13 */
#x34y {...} /* a=1 b=0 c=0 -> especificidade = 100 */
Pseudo-classes são contados como classes normais.
Como mencionado anteriormente, todo elemento HTML ou XHTML possui um conjunto padrão de propriedades que serão aplicadas caso não haja nenhuma sobreposição por uma regra CSS. Por exemplo, na maioria dos navegadores, o elemento em aparece em itálico, ou seja, a sua propriedade CSS font-style é automaticamente colocada como italic. Esse é outro fator que deve ser também considerado ao determinar o estilo de um elemento qualquer e vale especialmente para margens, bordas e preenchimentos.
Todo elemento também possui um atributo style, onde propriedades podem ser aplicadas diretamente. Essas propriedades possuem precedência sobre quaisquer regras anteriormente determinadas para o elemento. Por exemplo:
<p style="color: blue">...</p>
No exemplo acima, a cor do texto no parágrafo seria azul, indecentemente de quaisquer outras regras declaradas anteriormente.
Um último fator a considerar aqui é a herança. Algumas propriedades passam os seus valores adiante por meio de herança para os elementos que estão debaixo do elemento para o qual a regra se aplica. Por exemplo, se você especifica o tipo de fonte do elemento body por meio da propriedade font-family, todos os elementos contidos no mesmo terão o mesmo tipo de fonte, barrando, é claro, problemas de implementação -- o IE, por exemplo, não respeita exatamente a herança no caso de tabelas. Se uma propriedade propaga-se ou não pode ser mais ou menos determinado por meio do que ela faz. A propagação também pode acontecer pelo valor computado, no caso de algumas propriedades. Mais detalhes sobre o assunto podem ser encontrados parte relativa da especificação.
Anexando uma folha de estilo ao documento
Existem duas maneiras de aplicar uma folha de estilo a um documento: folhas de estilo internas, onde o CSS é incluído no topo da página, e folhas de estilo externas, onde o CSS é incluído por meio de um arquivo externo. As folhas de estilo externas são geralmente melhores pois permitem que as regras sejam reutilizadas em múltiplos documentos, além de poderem usar o mecanismo de cache dos navegadores, reduzindo o tempo de download e o uso de banda de um site.
Um folha de estilo interna é declarada na seção head de um documento, como mostrado no exemplo abaixo:
...
<head>
<style type="text/css">
p { color: blue; }
p.note { font-weight: bold; }
</style>
...
O atributo type serve para indicar a linguagem usada e existe para o caso da invenção futura de uma outra linguagem de estilos que não o CSS. (De fato, o Netscape 4 usava uma estranha linguagem de estilos baseada em JavaScript e nesse caso o atributo era colocado como "text/javascript".)
Uma folha de estilo externa é declarada através de um elemento especial, link, e aponta para um arquivo cuja extensão é geralmente .css. Embora essa extensão não seja obrigatória, é de praxe usá-la e a maioria dos navegadores está configurada para reconhecê-la como CSS. De qualquer forma, o arquivo deve ser servido como o Content-Type configurado para text/css. A maioria dos servidores Web está configurada para fazer isso automaticamente. Um exemplo do elemento link pode ser vista abaixo:
<link rel="stylesheet" href="file.css" type="text/css" />
Tanto no caso do elemento style como do elemento link, um atributo media pode ser acrescentado indicando para qual tipo de mídia o CSS é válido. Exemplo são screen (monitor), print (impressão), aural (voz) e braille. Esse atributo limita a aplicação da folha de estilo à mídia específica.
Contornando problemas de implementação
Infelizmente, como já mencionamos, existem muitos problemas de implementação CSS nos navegadores. Esses problemas acabam causando muitas dificuldades em aplicar determinados estilos aos elementos e complicam a vida do desenvolvedor. Alguns exemplos desses problemas são: o Internet Explorer 5 implementa um modelo de blocos incorreto, incluindo as áreas de preenchimento e de borda na largura de um elemento; o mesmo navegador implementa uma propagação incorreta da herança de alguns propriedades impedindo um bom uso dessas propriedades; nem todos os nevegadores implementam todos os seletores CSS2 mesmo quando suportam a maioria da especificação (o Opera 6 e Internet Explorer são exemplos disso); etc.
Além desses problemas de renderização, alguns navegadores também falham em reconhecer alguns tipos corretos de declarações CSS. Esse problema, na verdade, converteu-se em uma certa benção para os desenvolvedores, permitindo a criação de certas gambiarras que forçam alguns navegadores a ser comportarem de maneira específica. Essas gambiarras, conhecidas com CSS hacks ou filters afetam praticamente qualquer navegador e costumam ser bem específicas. Um exemplo é a declaração seguinte:
p\roperty:value;
A declaração da propriedade acima é correta segundo as regras de escape do CSS. Entretanto, ela não é reconhecida pelos seguintes navegadores: Internet Explorer 4 e 5, Netscape 4 e Opera 5 no Windows; iCab 2, OmniWeb 4 e Opera 5 no Mac OS X; Internet Explorer 4, Netscape 4 e Opera 5 no Macintosh; e Konqueror 3 no Linux. Nesses navegadores, esse tipo de declaração pode ser usada para sobrepor propriedades que eles não suportem corretamente ou contornar outras falhas na implementação.
Existem dezenas desses hacks e uma boa maneira de compreendê-los e visitar uma lista dos mesmos e estudar os exemplos. Dependendo do tipo de funcionalidade que o seu site implementa em CSS, você será obrigado a usar alguns dos mesmos para contornar as dificuldade surgidas. Obviamente, à medida que os navegadores evoluem, os problemas são corrigidos e as implementações devem ser testadas novamente. Além disso, novas implementações geralmente trazem novos problemas e estes também devem ser incorporados.
Apesar do quadro pintado acima, a maioria dos sites só precisa de implementar alguns hacks simples. A maioria dos sites que eu implementei, por exemplo, fez uso de uns poucos hacks com seletores não suportados por alguns navegadores para aplicar regras extras. Isso não só conservou a compatibilidade como ajudou na manutenção e implementação dos mesmos. Este site, por exemplo, por ter uma implementação mais simples, não utiliza nenhum tipo de hack.
Conclusão
Como mostrado acima, o CSS é uma linguagem muito poderosa, mas que mantém também a sua simplicidade e cujos conceitos básicos podem ser rapidamente absorvidos e aplicados. Os conceitos mais avançados também não apresentam muitas complicações desde que as limitações dos navegadores que fazem uso do CSS sejam entendidas.
Hoje em dia, o CSS pode ser usado para criar apresentações extremamente sofisticadas, como muitos sites demonstram. Essa flexibilidade é uma coisa relativamente recente na história dos padrões Web e qualquer desenvolvedor pode aproveitá-la em muito em projetos mais modernos.
Os próximos artigos mostrarão exemplos práticos de técnicas baseadas em CSS para criar apresentações visualmente interessantes que não perdem a estrutura e as outras vantagens dos padrões Web.
Quaisquer comentários, dúvidas, correções, sugestões ou adições poder ser encaminhados para o email mtblog@reflectivesurface.com, ou, no caso desse artigo específico, comentados na entrada correspondente em meu weblog.