Magic Flip Cards: Resolvendo um problema comum de dimensionamento
Publicados: 2022-03-10Quais são as chances de seu próximo cliente usar a palavra interativo ao apresentar seu projeto? Na minha experiência, a resposta é 100% , então estou sempre procurando por técnicas CSS robustas para me ajudar a entregar os vários recursos e efeitos que surgem ao discutir esse objetivo.
Um pequeno pedaço de interatividade que me pedem para implementar repetidamente são os flip cards – blocos de conteúdo que giram quando passam o mouse ou tocam para revelar o conteúdo em seu verso. É um efeito interessante que incentiva a navegação divertida e outra maneira de mostrar mais informações sem sair da página. Mas o método padrão tem um problema quando se trata de acomodar diferentes comprimentos de conteúdo de cartão.
Neste tutorial, vamos construir um flip card grid que resolve esse problema com alguns conceitos básicos de CSS — transforms, flex e grid. Você precisará estar familiarizado com isso, e isso ajudará a ter uma boa compreensão das técnicas de posicionamento CSS. Iremos cobrir:
- Como os flip cards geralmente são implementados usando posicionamento absoluto;
- O problema de dimensionamento que o posicionamento absoluto introduz; e
- Uma solução geral para dimensionamento automático de conteúdo sobreposto.
Criando um flip card básico
Com um bom suporte de navegador moderno para transformações tridimensionais, a criação de um flip card básico é relativamente simples. O método usual é colocar as faces frontal e traseira do cartão em um container pai e posicionar absolutamente a face posterior para que ela possa corresponder ao tamanho da face frontal. Adicione uma transformação do eixo x à face traseira para fazê-la parecer invertida, adicione outra ao próprio cartão ao passar o mouse e estamos no negócio.
.cards { display: grid; } .card { perspective: 40rem; } .card-body { transform-style: preserve-3d; transition: var(--time) transform; .card:hover & { transform: rotateX(-180deg); } } .card-front, .card-back { backface-visibility: hidden; } .card-back { position: absolute; top: 0; right: 0; bottom: 0; left: 0; transform: rotateX(-180deg); }
O que poderia dar errado?
No entanto, nossa solução padrão tem um grande problema: ela não funciona quando a face traseira precisa de mais espaço do que a face frontal oferece. Dar ao cartão um tamanho grande e fixo é uma solução, mas essa abordagem também pode falhar em algum ponto para alguns conjuntos de tamanhos de tela.
As composições de design apresentam naturalmente caixas de aparência elegante com texto que se encaixa perfeitamente. Mas ao iniciar o desenvolvimento, pode ser difícil obter um layout de página e cartão que funcione para o conteúdo real. E ao exibir conteúdo dinâmico de um CMS, pode ser impossível! Mesmo com limites de palavras ou caracteres, geralmente não há solução que funcione de maneira confiável em todos os dispositivos.
Devemos sempre nos esforçar para criar implementações de layout que tolerem uma ampla variedade de tamanhos de conteúdo. Mas não é fácil! Muitas vezes tive a oportunidade de voltar a usar dimensionamento e posição fixos, seja devido a restrições de tempo, suporte insuficiente ao navegador, design de referência fraco ou apenas minha própria inexperiência.
Ao longo dos anos, aprendi que um bom processo iterativo e um diálogo saudável com o designer podem ajudar muito na hora de lidar com essas questões, e muitas vezes você pode se encontrar em algum lugar no meio para obter um layout robusto com alguma interatividade. Mas voltando à tarefa em mãos – isso pode ser feito?
Pensando fora da caixa
Na verdade, é possível dimensionar os cartões com base no conteúdo da frente e do verso, e não é tão difícil quanto parece à primeira vista. Só precisamos ser metódicos e persistentes!
Restringindo o problema
Vamos começar fazendo uma lista dos requisitos do nosso layout. Tentar escrever exatamente o que você quer pode parecer uma tarefa árdua, mas é uma ótima maneira de descobrir restrições que podem ser simplificadas para resolver um problema. Digamos:
- Queremos ver um ou mais cartões retangulares, organizados em uma grade de coluna única ou de várias colunas;
- Queremos que os cartões vire ao passar o mouse ou toque para revelar um segundo conjunto de conteúdo na face traseira;
- Queremos que os cartões sejam sempre grandes o suficiente para mostrar todo o conteúdo da frente e do verso, independentemente do tamanho ou estilo do conteúdo; e
- No caso de várias colunas, idealmente, queremos que todos os cartões tenham o mesmo tamanho para que as linhas se alinhem bem.
Pensando nesses requisitos, podemos notar algumas coisas que simplificam o problema:
- Se os cartões forem apresentados em uma grade, temos uma restrição em sua largura — ou seja, suas larguras são funções da janela de visualização ou do contêiner da grade, e não de seu próprio conteúdo;
- Dado que sabemos a largura de um cartão (como uma porcentagem de seu pai, pelo menos), resolvemos a dimensão horizontal e só precisamos que a altura do cartão se expanda para se ajustar ao mais alto de sua face frontal ou traseira; e
- Se pudermos fazer isso e cada cartão for autodimensionado verticalmente, podemos usar
grid-auto-rows
do CSS Grid para tornar todas as linhas de cartões tão altas quanto o cartão mais alto.
Descobrindo o truque do cartão
Então, como dimensionamos os cartões? Agora que simplificamos nosso problema, estamos ao alcance da solução.
Esqueça, por um momento, a ideia de colocar conteúdo em cima de outro conteúdo e concentre-se em nosso novo requisito: um pai que seja tão alto quanto seu filho mais alto. Isso é fácil! Usando colunas, podemos fazer com que um pai se expanda até a altura de seu filho mais alto. Então, só precisamos empregar um pouco de prestidigitação para alinhar os filhos:
- Defina os filhos para terem a mesma largura do pai
- Permitir que o segundo filho transborde para a direita
- Transforme-o para a esquerda de volta ao seu devido lugar
.cards { display: grid; } .card-body { display: flex; } .card-front, .card-back { min-width: 100%; mix-blend-mode: multiply; // Preview both faces } .card-back { transform: translate(-100%, 0); }
Se essa abordagem parece óbvia, tenha certeza de que passei muitas horas passando por algumas ideias realmente terríveis antes de pensar nisso. No início, eu tinha planejado imprimir uma versão duplicada oculta do texto da face traseira dentro da face frontal para expandir o cartão para o tamanho correto. E quando pensei em usar estouro de coluna, estava originalmente cortando a coluna da direita usando overflow:hidden
e transformando-a apenas no último momento quando o foco começou, pois ainda não havia percebido que poderia mantê-lo transformado desde o início e use outro método, como opacity
ou backface-visibility
para ativá-lo e desativá-lo conforme necessário.
Em outras palavras, soluções óbvias são o resultado de muito trabalho! Se você sente que está batendo a cabeça contra sua mesa por horas em um problema de layout, é importante dar um passo atrás e decidir se você está gastando o tempo do seu cliente com sabedoria: se deve sugerir que eles alterem o design e se buscar a solução em seu próprio tempo como um exercício de aprendizado quando a pressão estiver baixa. Mas quando você inventa métodos simples, nunca se sinta estúpido porque levou muito tempo . Agora, vamos revisar nossa solução completa.
.cards { display: grid; } .card { perspective: 40rem; } .card-body { display: flex; transform-style: preserve-3d; transition: var(--time) transform; .card:hover & { transform: rotateX(-180deg); } } .card-front, .card-back { backface-visibility: hidden; min-width: 100%; } .card-back { transform: rotateX(-180deg) translate(-100%, 0); }
Há alguma ressalva?
A solução funciona bem em geral, com apenas algumas pequenas ressalvas a serem lembradas:
- Os cartões devem estar presentes em um layout de grade ou em algum outro contexto em que suas larguras não dependam do conteúdo.
- Os cartões exigem um invólucro de conteúdo de algum tipo (nosso
card-body
) para que a área de foco não mude durante as animações. Se o cartão em si for animado, você verá algumas falhas à medida que a animação para e reinicia rapidamente. - Estilos como planos de fundo e sombras de caixa são melhor colocados diretamente nas faces frontal e traseira, pois quaisquer efeitos no cartão em si não serão animados. Cuidado com estilos como sombras de caixa no corpo do cartão, pois naturalmente eles serão virados de cabeça para baixo.
- As faces frontal e traseira dos cartões precisam de sua propriedade
box-sizing
definida comoborder-box
se tiverem seu próprio preenchimento, devido ao requisitomin-width
mínima, caso contrário, eles transbordarão. - O Safari ainda requer
-webkit-backface-visibility
, em sua forma prefixada pelo fornecedor.
Adicionando um pouco de polonês
Agora que resolvemos o problema difícil, vamos ver alguns ajustes que podemos fazer para que toda a interação funcione da maneira mais suave possível.
Primeiro, verifique se as cartas se sobrepõem ao virar. Isso dependerá se você estiver usando várias colunas, a largura da calha da coluna, a orientação do flip e o valor da perspectiva do cartão, mas é provável que isso aconteça. Você pode aumentar a duração da animação para ver as coisas com mais clareza. Ao passar o mouse, não parece natural que o cartão pairado vire abaixo de seus vizinhos posteriores, então precisamos colocá-lo no topo usando z-index
. Fácil o suficiente, mas cuidado! Precisamos esperar até que a animação de saída seja concluída antes de restaurar o z-index
. Insira transition-delay
:
.card { transition: z-index; transition-delay: var(--time); z-index: 0; &:hover { transition-delay: 0s; z-index: 1; } }
Em seguida, considere criar um estado ativo para os cartões. Eu costumo tentar fazer com que cartões como esses sejam vinculados a algum lugar relevante - mesmo que não especificado pelo designer - já que elementos com efeitos de foco como esse parecem muito tocáveis, então é bom fornecer um destino para os leitores que tentam a sorte. Eu gosto de uma transformação de escala curta e sutil, pois funciona razoavelmente bem se a segunda metade da animação é cortada ou não pelo carregamento da página de destino (eu adoraria que os navegadores concluíssem as animações em voo de forma limpa antes da navegação, embora Tenho certeza de que isso seria muito mais difícil de implementar na prática do que parece).
Essa também é uma ótima oportunidade para pensar em quão acessível é o conteúdo do verso dos nossos cartões. Nossa marcação é concisa e bem ordenada, então cobrimos leitores de tela e outros casos de uso que ignoram o estilo, mas e os usuários de teclado? Se formos tornar os próprios cartões âncoras, eles receberão o foco conforme os usuários do teclado guiam pela página. Vamos reutilizar o estado de foco do cartão como um estado de foco, e o conteúdo de volta aparecerá naturalmente durante a navegação pelo teclado.
.card { transition: z-index, transform calc(var(--time) / 4); transition-delay: var(--time), 0s; z-index: 0; &:hover { transition-delay: 0s; z-index: 1; } &:active { transform: scale(0.975); } } .card-body { .card:hover &, .card:focus & { transform: rotateX(-180deg); } }
Por fim, não se esqueça de que agora o cartão é dimensionado automaticamente para caber em seu conteúdo, você pode usar praticamente qualquer técnica de alinhamento e espaçamento que desejar dentro dos contêineres frontais e traseiros. Use o alinhamento flexível para centralizar títulos, adicionar preenchimento e até mesmo colocar outra grade dentro do cartão. Essa é a beleza das boas soluções de layout que se adaptam ao seu conteúdo — redução do acoplamento dos filhos aos pais e a modularidade que permite que você se concentre em uma coisa de cada vez.
.card-front, .card-back { display: flex; align-items: center; background-color: white; box-shadow: 0 5px 10px black; border-radius: 0.25rem; padding: 1.5rem; }
Empacotando
Espero que você ache esta técnica CSS útil! Por que não tentar algumas variações na animação, como um efeito de escala ou um simples cross-fade? A técnica também não se limita ao formato dos cartões. Pode ser usado em qualquer lugar onde a responsabilidade pelo dimensionamento vertical caia para mais de um elemento. Imagine um site de revista com fotos grandes com legendas sobrepostas - você pode usá-lo para acomodar imagens com proporções altas e texto dinâmico longo.
Acima de tudo, lembre-se dos benefícios de dedicar algum tempo para realmente pensar se existe uma maneira de implementar um design que pareça funcionar apenas com dimensionamento e posição fixos. Muitas vezes, há, e não importa o quão complicado o problema possa parecer no início, anotar todos os seus requisitos, reservar algum tempo para criar um caso de teste mínimo e passar por ele metodicamente é sempre a melhor aposta.