
A diferença entre o uso bem-sucedido de CSS e tentativas dolorosas de lidar com isso geralmente depende de pequenos detalhes. De fato, o CSS tem muitas nuances. Uma das áreas mais comuns em que frequentemente percebo essa luta é o estilo dos layouts. Pessoalmente, eu gosto de aprender padrões CSS. Percebi que costumo usar um pequeno número deles para resolver a maioria dos problemas com o layout. Este artigo é sobre os padrões CSS que eu uso para superar problemas de layout. As situações serão consideradas independentemente da metodologia CSS usada: seja SMACSS, BEM ou até mesmo um tópico CSS-in-JS quente, porque todos eles se concentram nas propriedades CSS, e não na arquitetura, organização ou estratégia.
Por diversão, vamos começar com o teste.
Usaremos a plataforma que desenvolvi, chamada
Questionable.io, e usei-a para criar um teste, que seguiremos adiante. Não se preocupe, nenhum dado pessoal é coletado, os resultados são anônimos e são absolutamente gratuitos.
O objetivo do teste é verificar se você consegue reconhecer o comportamento específico do CSS e alguns problemas sem ler o artigo. Eu não dificultaria o teste, mas as nuances do estilo CSS podem ser bastante complicadas. Lembre-se, este teste é apenas por diversão. Os resultados do teste não demonstram sua frieza, mas espero que ainda sejam úteis.
O teste consiste em 10 perguntas e não deve demorar mais que 10 minutos
Do tradutor:
No final do artigo sobre Habr, uma pesquisa sobre o número de pontos marcados no teste é adicionada.
Nota: se você não quer perder tempo,
aqui está um link para perguntas com as respostas corretas.
Você passou no teste? Ótimo! Vamos examinar as perguntas uma a uma para ter uma idéia melhor dos padrões de estilo afetados no teste.
Pergunta 1: Modelo de Bloco
Estudar o modelo de bloco deve ser uma prioridade na lista de qualquer desenvolvedor FrontEnd. Talvez o artigo “
The CSS Box Model ” seja um pouco antigo, não subestime seu valor e relevância para o CSS moderno. O modelo de bloco é a base para quase todos os problemas relacionados aos layouts de CSS.
Esta questão em particular verifica como descobrir a largura com base nos princípios do modelo de bloco. Um bloco possui uma largura explícita através da
width: 100px
, mas as
regras padrão do modelo de bloco aplicam as propriedades de
width
à camada "conteúdo" desse bloco. A largura resultante (a largura do bloco na página) é a soma das camadas de conteúdo, preenchimento e borda. Por esse motivo, a resposta é 112px.
.box { width: 100px; height: 50px; padding: 5px; border: 1px solid red; background-color: red; }
Se você se deparar com uma situação em que a última coluna ou guia na interface é movida para a próxima linha, embora você tenha especificado para toda a
width: 20%;
e tinha certeza de que caberiam em 100% do elemento pai, provavelmente isso estava se tornando um problema. Cinco colunas com uma largura de 20% são colocadas em 100%, mas se receberem preenchimento e / ou borda adicionais, isso aumentará a largura de cada uma, deixando espaço insuficiente na linha atual para a última coluna.
Quando o CSS3 foi introduzido, uma nova ferramenta chamada
box-sizing
. Ele nos permite controlar em qual camada do modelo de bloco queremos aplicar a propriedade
width
. Por exemplo, podemos especificar o
box-sizing: border-box
. Isso significa que queremos que qualquer regra de largura seja aplicada à camada de borda externa em vez da camada de conteúdo. Na pergunta do teste, se a propriedade
box-sizing: border-box
da
box-sizing: border-box
fosse aplicada, a largura do bloco resultante seria 100px.
Para alguns de vocês, esse é um fato bem conhecido, mas ainda será um bom lembrete para profissionais e iniciantes.
Há vários artigos sobre o modelo de bloco e como usar a propriedade de dimensionamento de caixa como redefinição para aplicá-la imediatamente a todo o projeto.
Dimensionamento de caixa e
herança de dimensionamento de caixa Provavelmente, as práticas recomendadas um pouco melhores são dois bons artigos de CSS-Tricks para ler.
Pergunta 2: limites dos elementos
A segunda questão de teste pode ser parcialmente vista como uma continuação da primeira. Lembre-se, uma coisa é ler: "O modelo de bloco possui camadas (conteúdo, preenchimento, borda) e todas elas afetam a largura e a altura total do elemento", outra coisa é ser capaz de reconhecer os problemas do modelo de bloco em uma situação real. Esse problema em particular é um pouco clássico entre aqueles que trabalham com CSS há algum tempo. Resulta do fato de que os quadros ocuparão um certo espaço adicional e afastarão os elementos circundantes, uma vez que fazem parte do modelo de bloco. Adicionar quadros ao alterar o estado de um elemento, como
:hover
, significa que os blocos ficarão maiores e os blocos a seguir serão pressionados. Isso
pode ser irritante :
De todas as soluções possíveis mencionadas na pergunta de teste, adicionar
border: 2px solid transparent
ao estado original (sem passar o mouse) é a única solução possível para o problema.
box-sizing
não resolve esse problema, porque não definimos explicitamente a altura. Se fizéssemos isso, o quadro se tornaria parte da altura geral do elemento e a mudança não ocorreria, mas esse não é o nosso caso.
Existem outras soluções que não mencionamos nas opções de resposta. Uma delas é adicionar uma pseudo-borda usando a propriedade
box-shadow
ou usando
outline
vez de
border
. Cada um deles não gera viés, pois não é uma camada do modelo de bloco. Aqui está outro
artigo de CSS-Tricks, onde você pode ler mais sobre essas soluções.
Nota:
Lembre-se de que o
outline
não suporta o
border-radius
Pergunta 3: Posicionamento - absoluto versus fixo
Além de entender quando usar cada um dos tipos e
como eles diferem na representação visual , também é muito importante conhecer as regras de como cada um dos métodos de posicionamento está vinculado ao elemento pai usando as propriedades superior, direita, inferior ou esquerda.
Primeiro de tudo, vamos olhar para o
bloco que contém . Uma breve definição é que o bloco que contém é frequentemente o pai de qualquer elemento em questão. No entanto, as regras para o bloco de contenção são diferentes para elementos posicionados de forma absoluta e fixa.
1) Para elementos absolutos . O bloco que contém é o ancestral mais próximo cujo posicionamento não é
static
. Por exemplo, quando um elemento está absolutamente posicionado e contém as propriedades
top
,
right
,
bottom
ou
left
, ele será posicionado em relação a qualquer pai que possua posicionamento
absolute
,
relative
,
fixed
ou
fixed
.
2) Para elementos fixos . O bloco que contém é a viewport, apesar da presença de qualquer elemento pai com posicionamento diferente de
static
. Além disso, o comportamento de rolagem é diferente dos elementos absolutamente posicionados. Os elementos fixos permanecem "fixos" na área de visualização, daí o nome.
Muitos desenvolvedores pensam que elementos absolutamente posicionados procuram apenas o elemento pai mais próximo com posição de
position: relative
. Esse conceito errôneo tornou-se difundido simplesmente porque
position: relative
mais frequentemente especificado emparelhado com
position: absolute
para criar um bloco contendo. A razão pela qual isso é frequentemente usado é que o posicionamento relativo do bloco pai o deixa
no fluxo , o que geralmente é preferido. Há situações em que o pai de um elemento absolutamente posicionado é ele próprio absolutamente posicionado. Isso é completamente normal. Se todos os pais forem estáticos, um elemento absolutamente posicionado será anexado à janela de visualização - mas para que ela role junto com a janela de visualização.
Há uma adição pouco conhecida às regras mencionadas acima: em situações em que o elemento pai possui a propriedade
transform
(
entre outros ) com um valor diferente de
none
, ele se torna um bloco contendo elementos posicionados de maneira absoluta e fixa. A confirmação disso pode ser vista no CodePen, onde o elemento com o texto "Aviso!" É um elemento fixo e o pai tem a propriedade transform, mas apenas quando o cursor do mouse está sobre ele (no estado
:hover
)
Pergunta 4: Recolher margem dos elementos pai e filho
Essa é uma daquelas pequenas coisas de CSS que podem causar muitos problemas se você não souber como funciona. Existe um conceito chamado margens de colapso e muitas pessoas estão familiarizadas com a manifestação disso, chamadas margens de colapso de irmãos adjacentes. No entanto, existe outro formulário, chamado de margens de recolhimento do pai e do primeiro / último filho, que é menos conhecido. A seguir, uma demonstração dos dois casos:
Cada tag de parágrafo possui uma margem superior e inferior igual a 1em, denominada pelo navegador. Até agora, esta é a parte mais fácil da situação. Mas por que o espaçamento entre parágrafos não é 2em (a soma da parte superior e inferior)? Isso é chamado de margens em colapso dos irmãos adjacentes. As margens se sobrepõem de forma que a maior das duas margens seja do tamanho do intervalo, portanto, nesse caso, o intervalo será 1em.
No entanto, algo mais estranho está acontecendo. Você notou que a margem superior do primeiro parágrafo não cria um espaço entre ela e a div azul do contêiner? Em vez de quebrar, parece incorporar uma margem no div principal, como se o div tivesse uma margem superior. Isso se chama Recolher margens do pai e do primeiro / último filho. Esse tipo de margens de recolhimento não ocorrerá em determinadas circunstâncias se o seguinte for característico do pai:
- Há um preenchimento superior / inferior maior que zero
- Há uma borda superior / inferior maior que zero
- É especificado um contexto de formatação de bloco que pode ser criado usando
overflow: hidden
ou overflow: auto
- Exibição: conjunto de propriedades de raiz de fluxo (suporte ruim ao navegador)
Quando fico feliz em explicar às pessoas esse pequeno detalhe do CSS e resolvê-lo com preenchimento ou borda, a resposta é quase sempre: “E o preenchimento ou borda igual a 0?”. Bem, isso não funciona, porque o valor deve ser um número inteiro positivo.
No exemplo anterior, o preenchimento de 1px nos permite alternar entre o recolhimento e a prevenção de margens reduzidas do pai e do filho. O espaço que aparece entre o primeiro / último parágrafo e o pai é de 1px preenchimento, mas agora a margem é considerada o interior do contêiner, porque a camada de preenchimento cria uma barreira para impedir o colapso das margens.
Quanto à pergunta, tenho certeza de que você pode ver qual é o problema nessa interface:
O primeiro elemento da classe
.comment
(sem a classe
.moderator
)
.moderator
as margens. Mesmo sem olhar para o código, podemos ver que o elemento com a classe
.moderator
tem um quadro e o elemento sem essa classe não. Na verdade, havia três respostas sobre essa pergunta que foram consideradas corretas: cada uma delas, de fato, já está aplicada na fonte CodePen, apenas comentada.
Um dos motivos pelos quais esse tipo de margem de colapso não é tão conhecido como os outros é porque existem muitas situações nas quais podemos evitá-lo acidentalmente. Os elementos Flexbox e Grid criam um contexto de formatação de bloco; portanto, ao usá-los, não encontramos esse tipo de margens de recolhimento. Se a interface de nossos comentários era um projeto real, é provável que houvesse acolchoamentos nos quatro lados para deixar espaço ao redor, e isso, por sua vez, corrigiu o colapso das margens para nós.
Pergunta 5: Porcentagem de quê?
Quando unidades de porcentagem são usadas, as porcentagens são baseadas na largura ou altura do bloco que contém (geralmente o pai). Como dissemos anteriormente, um elemento com a propriedade
transform
se tornará um bloco de contenção. Portanto, quando o elemento usa transformação, as unidades em porcentagem (apenas para
transform
) são
no tamanho do próprio elemento e não no tamanho pai.
No exemplo abaixo, podemos ver que 50% assumem dois valores diferentes, dependendo do contexto. O primeiro bloco vermelho tem a propriedade
margin-left: 50%
, e o segundo bloco vermelho usa
transform: translateX(50%)
.
Pergunta 6: O modelo de bloco ataca novamente ...
Só você pensou que tínhamos terminado de falar sobre o modelo de blocos ...
O problema se deve ao fato de usarmos
width: 100%
para o rodapé e, ao mesmo tempo, adicionar preenchimento. A largura do contêiner é de 500px, o que significa que a camada de conteúdo do rodapé (sendo 100%) também é de 500px antes da adição de preenchimentos nesse tamanho.
O problema pode ser corrigido por uma das duas técnicas comuns:
- Use a propriedade de tamanho da caixa para o rodapé diretamente ou através da redefinição mencionada anteriormente
- Remova a propriedade
width
do elemento e use as propriedades left: 0
e right: 0
. Este é um bom exemplo de uso das propriedades left
e right
ao mesmo tempo. Essa abordagem evita os problemas do modelo de bloco, porque a propriedade width
usará seu valor padrão auto
para preencher qualquer espaço disponível entre preenchimento e bordas quando left: 0
e right:0
.
Nota: Uma opção era remover o preenchimento do rodapé. Tecnicamente, isso resolveria o problema, porque a camada de conteúdo seria 100% e não teria preenchimento ou borda para expandir além da largura do contêiner. Mas acho que esta solução é a abordagem errada, porque não precisamos alterar nossa interface para nos adaptar aos problemas do modelo de bloco, que são facilmente corrigidos sem ela.
A realidade para mim é que eu sempre tenho a propriedade size
box-sizing: border-box
como parte da minha redefinição geral de estilo. Se você fizer o mesmo, provavelmente não encontrará esse problema. Mas ainda gosto da técnica de definir as propriedades
left:0
e
right:0
ao mesmo tempo, porque, como mostra o tempo, é mais confiável (de qualquer forma, a julgar pela minha experiência) do que resolver os problemas do modelo de bloco que surgem devido à largura 100%
Pergunta 7: Centralizando elementos absolutos e fixos
Agora estamos realmente começando a combinar todo o material descrito acima, com a centralização dos elementos absolutos e fixos:
Como já abordamos a maior parte do material nesta questão de teste, eu simplesmente indico que a centralização horizontal e vertical pode ser feita usando o método old-school através de margens negativas, ou mais recente usando a propriedade transform. Também trago à sua atenção um excelente
guia de centralização de elementos CSS-Tricks .
Nota : há algum tempo, argumentou-se que, se soubermos a largura e a altura do bloco, devemos usar margens negativas, porque elas funcionam de maneira mais estável que a nova propriedade de transformação. No momento, a transformação funciona de forma estável e eu quase sempre uso essa propriedade, a menos que eu precise evitar converter o bloco no que contém.
Pergunta 8: Centralizando elementos em um fluxo normal
O Flexbox nos trouxe muitas ferramentas incríveis para resolver problemas complexos de layout. Antes de seu lançamento, foi relatado que a centralização vertical era um dos recursos mais complexos implementados no CSS. Com o advento do Flexbox, a centralização vertical tornou-se rotina:
.parent { display: flex; } .child { margin: auto; }
https://codepen.io/bradwestfall/pen/GPNmbMNota : observe que, para elementos flexíveis, margin: auto é aplicado nos lados superior, inferior, direito e esquerdo para centralizar o elemento verticalmente e horizontalmente. Anteriormente, a centralização vertical não funcionava para elementos de bloco; portanto, a
margin: 0 auto;
bastante comum
margin: 0 auto;
Pergunta 9: Cálculos com unidades diferentes
O uso da função calc () é excelente quando você precisa trabalhar com duas unidades de medida que não podemos somar sozinhas ou quando precisamos
facilitar a compreensão das frações. Esta pergunta de teste nos permite descobrir como será o resultado da expressão
calc(100% + 1em)
, começando pelo fato de a largura do elemento div ser 100px. Isso pode ser um pouco confuso, porque, de fato, não importa que a largura do elemento div seja 100px. As porcentagens sempre começam na largura do pai, então a resposta correta foi: "100% do bloco contendo (pai) mais 1em".
Existem várias situações em que uso regularmente
calc()
. Primeiro, sempre que eu quiser mudar algo em 100%, mas também adicionar uma quantidade fixa de espaço extra. Os menus suspensos podem ser um bom exemplo disso:
A peculiaridade aqui é que queremos criar um menu suspenso que possa ser usado junto com os elementos de chamada de tamanhos diferentes (neste caso, dois tamanhos diferentes de botões). Não sabemos qual será a altura do elemento que chama este menu, mas sabemos que
top: 100%
colocará a borda superior do menu suspenso na borda inferior do botão de chamada. Se cada menu estiver na extremidade inferior do botão correspondente, mais 0,5em, isso pode ser alcançado com
calc(100% + 0.5em)
. Claro, também poderíamos usar
top: 110%
, mas esses 10% adicionais dependeriam da altura do botão de chamada e do contêiner.
Pergunta 10: Margens negativas
Diferentemente das margens positivas que repelem os irmãos, as margens negativas as aproximam umas das outras
sem mudar os irmãos . Esta pergunta de teste final oferece duas soluções, ambas tecnicamente eliminando a borda dupla em nosso grupo de botões, mas eu prefiro fortemente a técnica de margem negativa, porque excluir os quadros dificultaria a execução de certas técnicas, como o efeito de passagem do mouse.
O efeito resultante é uma "borda comum" exibida entre os botões. De fato, os botões não podem ter uma borda comum; portanto, precisamos usar uma técnica de margem negativa para que um quadro se sobreponha ao outro. Em seguida, uso o
z-index
para definir qual quadro eu quero ajustar acima de outro, com base no estado do cursor do mouse. Observe que o
z-index
é útil aqui, mesmo sem usar o posicionamento absoluto, mas você tinha que definir a
position: relative
. Se eu usasse a técnica de remover o quadro esquerdo do segundo botão, esse efeito seria muito mais difícil de implementar.
Tudo faz sentido.
Quero mostrar outra demonstração mais recente que usa muitos dos truques que discutimos anteriormente. A tarefa é criar blocos que se expandem para as bordas esquerda e direita do contêiner, levando em consideração o recuo. Por blocos, quero dizer a capacidade de ter uma lista de blocos que se ajustam à próxima linha quando não há espaço suficiente em largura. Passe o mouse sobre os blocos para ver o resultado:
Um obstáculo na execução desta tarefa é o recuo. Sem preenchimento, seria fácil obter peças adjacentes às bordas esquerda e direita do contêiner. O problema é que os recuos serão criados usando margens e, quando adicionamos margens em todos os lados do bloco, criamos dois problemas:
- A presença de três peças com uma largura de
33.33%
em conjunto com a margem não poderá caber em uma linha. Embora o box-sizing
nos permita ter preenchimento e bordas no elemento .tile
e caber em 33.33%
, isso não nos ajudará no caso de margens - o que significa que a largura calculada dos três blocos será superior a 100%, o que forçará o último bloco a passar para o próximo string.
- Os ladrilhos esquerdo e direito não ficam mais contra a borda do contêiner.
O primeiro problema pode ser resolvido com calc((100% / 3) - 1em)
. O que significa 33,33% menos as margens esquerda e direita de cada bloco. O colapso dos elementos irmãos adjacentes não ocorrerá aqui, pois é possível apenas nas margens superior e inferior. Como resultado, a distância horizontal entre dois blocos é a soma de duas margens (1em). Nesse caso, o recolhimento também não se aplica às margens superior e inferior, porque o primeiro e o último bloco não são tecnicamente adjacentes, mesmo que visualmente um esteja embaixo do outro.Ao tomar comcalc()
Três peças podem caber em uma linha, mas ainda não tocam as bordas do contêiner. Para fazer isso, podemos usar margens negativas no tamanho igual às margens esquerda e direita de cada bloco. A linha pontilhada verde no exemplo é um contêiner em que aplicamos margens negativas para desenhar blocos correspondentes à borda do conteúdo circundante. Podemos ver como eles se ajustam à área de preenchimento do elemento pai. Isso é normal porque as margens negativas não repelem os elementos vizinhos ao redor.Como resultado, obtemos blocos que possuem espaços suficientes que se expandem de ponta a ponta para que eles se alinhem com as tags de parágrafo adjacentes fora dos blocos.Existem muitas maneiras de fazer ladrilhos (e geralmente eles têm suas vantagens e desvantagens). Por exemplo, há uma solução CSS Grid bastante elegante sobre a qual Haydon Pickering falou. Esta solução é implementada usando uma técnica que simula solicitações de contêineres (mas usando o Grid Magic). Por fim, sua solução usando o Grid é melhor que o flexbox, o que demonstrei, mas tem um suporte pior ao navegador.Sumário
No começo, afirmei que estava inclinado a procurar padrões na solução de problemas. Este artigo não trata muito dos cenários demonstrados acima; trata-se mais de um conjunto de ferramentas que podem ser usadas para resolver esses e muitos outros problemas de digitação que todos nós podemos enfrentar. Espero que essas ferramentas o ajudem.A propósito, existem vários recursos excelentes que examinam cuidadosamente o tópico do modelo de bloco. Antes de tudo, artigos de Rachel Andrew e Jen Simmons, que definitivamente valem a pena ser lidos.