Criando cartões animados e clicáveis ​​com a pseudoclasse relacional :has() PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Criando cartões animados e clicáveis ​​com a pseudoclasse relacional :has()

O CSS :has() pseudoclasse está sendo lançada em muitos navegadores com Chrome e Safári já apoiando totalmente. Muitas vezes é chamado de “seletor pai” — como em, podemos selecionar o estilo de um elemento pai a partir de um seletor filho — mas há muito mais que isso. :has() pode nos ajudar a resolver. Uma dessas coisas é reinventar o padrão de cartão clicável que muitos de nós adoramos usar de vez em quando.

Vamos dar uma olhada em como :has() pode nos ajudar a lidar com cartões vinculados, mas primeiro…

O que é isso :has() pseudoclasse?

Já existe um grupo of ótimo posts flutuando ao redor que fazem um excelente trabalho explicando o que :has() é e para que é usado, mas ainda é novo o suficiente para que devêssemos dizer algumas palavras sobre ele aqui também.

:has() é uma pseudoclasse relacional que faz parte do Rascunho de trabalho dos Seletores W3C Nível 4. É disso que se trata os parênteses: combinar elementos que estão relacionados a - ou, mais precisamente, contêm - certos elementos filhos.

/* Matches an article element that contains an image element */
article:has(img) { }

/* Matches an article element with an image contained immediately within it */
article:has(> img) { }

Então, você pode ver por que podemos querer chamá-lo de seletor “pai”. Mas também podemos combiná-lo com outras pseudoclasses funcionais para sermos mais específicos. Digamos que queremos estilizar artigos que façam não conter quaisquer imagens. Podemos combinar os poderes relacionais de :has() com os poderes de negação de :not() fazer isso:

/* Matches an article without images  */
article:not(:has(img)) { }

Mas isso é apenas o começo de como podemos combinar poderes para fazer mais com :has(). Antes de nos voltarmos especificamente para a solução do enigma do cartão clicável, vejamos algumas maneiras pelas quais atualmente os abordamos sem usar :has().

Como atualmente lidamos com cartões clicáveis

Existem três abordagens principais sobre como as pessoas criam um cartão totalmente clicável atualmente e para entender completamente o poder dessa pseudo classe, é bom fazer um resumo.

Essa abordagem é algo usado com bastante frequência. Nunca uso essa abordagem, mas criei uma demonstração rápida para demonstrá-la:

Há muitas preocupações aqui, especialmente quando se trata de acessibilidade. Quando os usuários navegam em seu site usando a função do rotor, eles ouvirão o texto completo dentro desse elemento - o título, o texto e o link. Alguém pode não querer passar por tudo isso. Podemos fazer melhor. Desde o HTML5, podemos aninhar elementos de bloco dentro de um elemento. Mas nunca parece certo para mim, especialmente por esse motivo.

Prós:

  • Rápido de implementar
  • Semanticamente correto

Contras:

  • Preocupações com acessibilidade
  • Texto não selecionável
  • Muito trabalho para substituir estilos que você usou em seus links padrão

O método JavaScript

Usando JavaScript, podemos anexar um link ao nosso cartão em vez de escrevê-lo na marcação. Encontrei esta ótima demonstração do CodePen por custodev que também tornou o texto do cartão selecionável no processo:

Essa abordagem tem muitos benefícios. Nossos links são acessíveis em foco e podemos até selecionar texto. Mas existem algumas desvantagens quando se trata de estilo. Se quisermos animar esses cartões, por exemplo, teríamos que adicionar :hover estilos em nossos principais .card wrapper em vez do próprio link. Também não nos beneficiaríamos das animações quando os links estivessem em foco nas guias do teclado.

Prós:

  • Pode ser tornado perfeitamente acessível
  • Capacidade de selecionar texto

Contras:

  • Requer JavaScript
  • Não é possível clicar com o botão direito (embora possa ser corrigido com alguns scripts extras)
  • Exigirá muitos estilos no próprio cartão, o que não funcionaria ao focar o link

A ::after abordagem do seletor

Este método exige que definamos o cartão com posicionamento relativo e, em seguida, definamos o posicionamento absoluto no link. ::after pseudo seletor de um link. Isso não requer nenhum JavaScript e é muito fácil de implementar:

Existem algumas desvantagens aqui, especialmente quando se trata de selecionar texto. A menos que você forneça um índice z mais alto no corpo do cartão, você não poderá selecionar o texto, mas se o fizer, esteja avisado que clicar no texto não ativará o seu link. Se você deseja ou não um texto selecionável, depende de você. Acho que pode ser um problema de UX, mas depende do caso de uso. O texto ainda está acessível aos leitores de tela, mas meu principal problema com o método é a falta de possibilidades de animação.

Prós:

  • Fácil de implementar
  • Link acessível sem texto inchado
  • Funciona em foco e foco

Contras:

  • O texto não é selecionável
  • Você só pode animar o link, pois este é o elemento que você está pairando.

Uma nova abordagem: usando ::after de :has()

Agora que estabelecemos as abordagens existentes para cartões clicáveis, quero mostrar como introduzir :has() à mistura resolve a maioria dessas deficiências.

Na verdade, vamos basear esta abordagem na última que vimos usando ::after no elemento de link. Na verdade, podemos usar :has() existe para superar as restrições de animação dessa abordagem.

Vamos começar com a marcação:

Fluffy gray and white tabby kitten snuggled up in a ball.

Some Heading

Curabitur convallis ac quam vitae laoreet. Nulla mauris ante, euismod sed lacus sit amet, congue bibendum eros. Etiam mattis lobortis porta. Vestibulum ultrices iaculis enim imperdiet egestas.

Manterei as coisas o mais simples possível, visando elementos no CSS em vez de classes.

Para esta demonstração, adicionaremos um zoom e uma sombra de imagem ao cartão ao passar o mouse e animaremos o link com uma seta aparecendo e enquanto alteramos a cor do texto do link. Para facilitar isso, adicionaremos algumas propriedades personalizadas no escopo do nosso cartão. Aqui está o estilo básico:

/* The card element */
article {
  --img-scale: 1.001;
  --title-color: black;
  --link-icon-translate: -20px;
  --link-icon-opacity: 0;

  position: relative;
  border-radius: 16px;
  box-shadow: none;
  background: #fff;
  transform-origin: center;
  transition: all 0.4s ease-in-out;
  overflow: hidden;
}
/* The link's ::after pseudo */
article a::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline: 0;
  cursor: pointer;
}

Ótimo! Adicionamos uma escala inicial para a imagem (--img-scale: 1.001), a cor inicial do cabeçalho do cartão (--title-color: black) e algumas propriedades extras que usaremos para fazer nossa seta sair do link. Também definimos um estado vazio do box-shadow declaração para animá-la mais tarde. Isso configura o que precisamos para o cartão clicável agora, então vamos adicionar algumas redefinições e estilos adicionando essas propriedades personalizadas aos elementos que queremos animar:

article h2 {
  margin: 0 0 18px 0;
  font-family: "Bebas Neue", cursive;
  font-size: 1.9rem;
  letter-spacing: 0.06em;
  color: var(--title-color);
  transition: color 0.3s ease-out;
}
article figure {
  margin: 0;
  padding: 0;
  aspect-ratio: 16 / 9;
  overflow: hidden;
}
article img {
  max-width: 100%;
  transform-origin: center;
  transform: scale(var(--img-scale));
  transition: transform 0.4s ease-in-out;
}
article a {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  color: #28666e;
}
article a:focus {
  outline: 1px dotted #28666e;
}
article a .icon {
  min-width: 24px;
  width: 24px;
  height: 24px;
  margin-left: 5px;
  transform: translateX(var(--link-icon-translate));
  opacity: var(--link-icon-opacity);
  transition: all 0.3s;
}

.article-body {
  padding: 24px;
}

Vamos ser gentis com as pessoas e também adicionar um aula de leitor de tela escondido atrás do link:

.sr-only:not(:focus):not(:active) {
  clip: rect(0 0 0 0); 
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap; 
  width: 1px;
}

Nosso cartão está começando a ficar muito fofo. É hora de adicionar um pouco de magia a isso. Com o :has() pseudoclasse, agora podemos verificar se nosso link está pairando ou em foco, então atualizar nossas propriedades personalizadas e adicionar um box-shadow. Com este pequeno pedaço de CSS nosso cartão realmente ganha vida:

/* Matches an article element that contains a hover or focus state */
article:has(:hover, :focus) {
  --img-scale: 1.1;
  --title-color: #28666e;
  --link-icon-translate: 0;
  --link-icon-opacity: 1;

  box-shadow: rgba(0, 0, 0, 0.16) 0px 10px 36px 0px, rgba(0, 0, 0, 0.06) 0px 0px 0px 1px;
}

Veja o que há lá em cima? Agora obtemos os estilos atualizados se qualquer o elemento filho no cartão é pairado ou focado. E mesmo que o elemento link seja a única coisa que pode conter um estado de foco ou foco no ::after abordagem de cartão clicável, podemos usá-la para corresponder ao elemento pai e aplicar as transições.

E aí está. Apenas mais um caso de uso poderoso para o :has() seletor. Não apenas podemos combinar um elemento pai declarando outros elementos como argumentos, mas também podemos combinar usando pseudos para combinar e estilizar os pais.

Prós:

  • Acessível
  • Animável
  • Não é necessário JavaScript
  • Uso :hover no elemento correto

Contras:

  • O texto não é facilmente selecionável.
  • O suporte do navegador é limitado ao Chrome e Safari (é compatível com o Firefox atrás de uma bandeira).

Aqui está uma demonstração usando esta técnica. Você pode notar um invólucro extra ao redor do cartão, mas sou só eu brincando com consultas de contêiner, que é apenas uma daquelas outras coisas fantásticas lançadas em todos os principais navegadores.

Tenho alguns outros exemplos você deseja compartilhar? Outras soluções ou ideias são mais que bem-vindas na seção de comentários.

Carimbo de hora:

Mais de Truques CSS