Um efeito de foco de galeria CSS puro com :not

Nó Fonte: 1723238

Muitas vezes, no passado, eu precisava descobrir como adicionar estilos a todos os elementos dentro do contêiner, mas não o pairado.

Demonstração do efeito “fade-out” esperado em irmãos para permitir que os usuários “foquem” em um elemento específico.

Este efeito requer a seleção dos irmãos de um elemento pairado. Eu costumava aplicar JavaScript para isso, adicionando ou removendo a classe que definia as regras CSS apropriadas em mouseenter e mouseleave eventos semelhantes a este:

Embora o código faça o truque, meu pressentimento sempre me disse que deve haver alguma maneira de CSS puro para alcançar o mesmo resultado. Alguns anos atrás, enquanto trabalhava em um determinado controle deslizante para minha empresa, criei uma solução semelhante a como Chris Geelhoed recriou a famosa animação da página inicial da Netflix e entendi que não precisava mais de JavaScript para isso.

Alguns meses atrás, eu estava tentando implementar a mesma abordagem para um feed baseado em grade no site da minha empresa e — bum — não funcionou por causa da lacuna entre os elementos!

Felizmente para mim, parecia que não precisava ficar assim, e mais uma vez eu não precisava de JavaScript para isso.

Marcação e CSS básico

Vamos começar a codificar preparando a marcação adequada:

  • .grid é baseado em grade

      Lista;

    • e .grid__child elementos são
    • crianças com quem queremos interagir.

    A marcação fica assim:

    O estilo deve ficar assim:

    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, 15rem);
      grid-gap: 1rem;
    }
    
    .grid__child {
      background: rgba(0, 0, 0, .1);
      border-radius: .5rem;
      aspect-ratio: 1/1;
    }

    Este código de exemplo criará três itens de lista ocupando três colunas em uma grade.

    O poder dos seletores CSS

    Agora, vamos adicionar alguma interatividade. A abordagem que apliquei inicialmente foi baseada em duas etapas:

    1. passar o mouse sobre o contêiner deve alterar os estilos de todos os elementos dentro…  
    2. …exceto aquele que o cursor está pairando no momento.

    Vamos começar pegando todos os filhos enquanto o cursor está pairando sobre o contêiner:

    .grid:hover .grid__child {
      /* ... */
    }

    Em segundo lugar, vamos excluir o item em foco no momento e reduzir o opacity de qualquer outra criança:

    .grid:hover .grid__child:not(:hover) {
      opacity: 0.3;
    }

    E isso seria perfeitamente suficiente para contêineres sem lacunas entre os elementos filhos:

    GIF animado de um cursor do mouse interagindo com elementos que não são separados por nenhuma lacuna.
    Demonstração de uma solução que funciona sem falhas.

    No entanto, no meu caso, não consegui remover essas lacunas:

    GIF animado de um cursor do mouse pairando sobre elementos. No entanto, quando o mouse entra em uma lacuna entre dois elementos, o efeito termina quando o mouse sai do elemento.
    Demonstração do problema encontrado quando as lacunas são introduzidas.

    Quando eu estava movendo o mouse entre os blocos, todos os elementos filhos estavam desaparecendo.

    Ignorando as lacunas

    Podemos supor que as lacunas são partes do contêiner que não são sobrepostas por seus filhos. Não queremos executar o efeito toda vez que o cursor entrar no container, mas sim quando ele passar o mouse sobre um dos elementos dentro dele. Podemos ignorar o cursor se movendo acima das lacunas então? 

    Sim, podemos, usando pointer-events: none na .grid recipiente e trazê-los de volta com pointer-events: auto em seus filhos:

    .grid {
      /* ... */
      pointer-events: none;
    }
    
    /* ... */
    
    .grid__child {
      /* ... */
      pointer-events: auto;
    }

    Vamos apenas adicionar uma transição legal na opacidade e temos um componente pronto:

    Provavelmente é ainda mais legal quando adicionamos mais blocos e criamos um layout bidimensional:

    O CSS final fica assim:

    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, 15rem);
      grid-gap: 3rem;
      pointer-events: none;
    }
    
    .grid:hover .grid__child:not(:hover) {
      opacity: 0.3;
    }
    
    .grid__child {
      background: rgba(0, 0, 0, .1);
      border-radius: .5rem;
      aspect-ratio: 1/1;
      pointer-events: auto;
      transition: opacity 300ms;
    }

    Com apenas 2 linhas adicionais de código, superamos o problema da lacuna!

    Possíveis problemas

    Embora seja uma solução compacta, existem algumas situações em que pode exigir algumas soluções alternativas.

    Infelizmente, esse truque não funcionará quando você quiser que o contêiner seja rolável, por exemplo, como em algum tipo de controle deslizante horizontal. o pointer-events: none style iria ignorar não apenas o evento hover, mas todos os outros também. Em tais situações, você pode embrulhar o .grid em outro container, assim:

    Resumo

    Eu encorajo você a experimentar e tentar encontrar uma abordagem mais simples e nativa para tarefas que normalmente devem ter algum nível de complexidade. As tecnologias da Web, como CSS, estão ficando cada vez mais poderosas e, usando soluções nativas prontas para uso, você pode obter ótimos resultados sem a necessidade de manter seu código e cedê-lo a fornecedores de navegadores.

    Espero que você tenha gostado deste pequeno tutorial e o tenha achado útil. Obrigado!

    O autor selecionou o Tecnologia Educação receber uma doação como parte do Escreva para doações .

    Carimbo de hora:

    Mais de Truques CSS