Adicione efeitos de revelação interativos e de alto desempenho ao seu site com máscaras CSS e propriedades personalizadas registradas. Ao usar um gradiente radial como máscara e registrar os valores de parada, podemos fazer uma transição suave de entrada e saída, seguindo o ponteiro do usuário com o mínimo de JavaScript.
Implementação
1. Registrar propriedades personalizadas
Para permitir a interpolação suave de valores de parada de gradiente, registre as variáveis usando @property. Isso informa ao mecanismo do navegador sobre o tipo de dados, permitindo que ele faça a transição entre valores durante as atualizações.
/* Register the spotlight inner and outer sizes to enable interpolation */
@property --inner-size{
syntax: "<length-percentage>";
inherits: true;
initial-value: 0px;
}
@property --outer-size{
syntax: "<length-percentage>";
inherits: true;
initial-value: 0px;
}
As propriedades personalizadas que rastreiam a posição do ponteiro não precisam ser transferidas, então não é necessário registrá-las.
2. Definir a camada de mascaramento
Aplique o mask-image ao elemento que você quer revelar. Use um radial-gradient que faça referência às propriedades registradas.
.reveal-layer {
/* Only transition the size properties, NOT the position variables */
transition: --inner-size 0.2s ease-in-out, --outer-size 0.2s ease-in-out;
/* The spotlight is defined by the transparent center of the mask */
mask-image: radial-gradient(
circle at var(--mouse-x) var(--mouse-y),
black var(--inner-size, 0%),
transparent var(--outer-size, 0%)
);
/* Ensure the mask doesn't repeat if the element is large */
mask-repeat: no-repeat;
/* Make the mask layer non-interactive */
pointer-events: none;
}
/* Update the gradients stops on interaction */
.reveal-layer:hover {
--inner-size: 100px;
--outer-size: 120px;
}
3. Atualizar coordenadas com JavaScript
Rastreie a posição do ponteiro e atualize as variáveis CSS. Como as propriedades estão registradas e têm um transition definido, o destaque vai se mover de forma suave, mesmo que os eventos de ponteiro sejam pouco frequentes.
const container = document.querySelector('.container');
// Store the container's bounding rect
let rect = container.getBoundingClientRect();
// Update the rect when the container is resized
const resizeObserver = new ResizeObserver(()=>{
rect = container.getBoundingClientRect();
})
resizeObserver.observe(container);
container.addEventListener('pointermove', (e) => {
// Calculate position as a percentage of the container.
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
// Update the registered properties
container.style.setProperty('--mouse-x', `${x}%`);
container.style.setProperty('--mouse-y', `${y}%`);
});
4. Acessibilidade e interação
Garantia de acessibilidade OBRIGATÓRIA: esse padrão depende de interações com o ponteiro para revelar um destaque visual. Você PRECISA garantir que todo o conteúdo subjacente permaneça totalmente visível, legível e acessível de forma independente pelo teclado por padrão no layout subjacente, usando a camada de destaque apenas como uma melhoria visual não essencial para usuários de ponteiro. Nunca use esse efeito para ocultar ou bloquear conteúdo essencial para usuários que usam apenas o teclado ou tecnologias adaptativas.
- Eventos de ponteiro: defina
pointer-events: nonena camada de sobreposição de máscara para permitir que as interações padrão de clique e toque passem para os controles abaixo. - Substituição de movimento reduzido: desativa a interpolação de transição suave para usuários que solicitam movimento reduzido.
/* MANDATORY Copy-Paste Safety: Disable transition scaling for motion-sensitive users */
@media (prefers-reduced-motion: reduce) {
.reveal-layer {
transition: none !important;
}
}
Estratégias de fallback
Substituto de propriedade não registrada
Os navegadores que oferecem suporte a mask-image, mas não a @property, ainda vão mostrar o destaque, mas o movimento vai alternar entre os estados ativado e desativado porque eles não podem interpolar valores dentro de um radial-gradient. Forneça valores substitutos ao usar var().
.reveal-layer {
mask-image: radial-gradient(
circle at var(--mouse-x) var(--mouse-y),
/* Use fallback values when using the `var()` function for browsers that don't get an initial value from the @property registration. */
black var(--inner-size, 0%),
transparent var(--outer-size, 0%)
);
}
Suporte básico para máscaras
Para navegadores que não são compatíveis com o mascaramento de CSS:
- Propriedade com prefixo: use a propriedade com o prefixo
-webkit-mask-imagepara maior compatibilidade com navegadores. - Aprimoramento progressivo: projete o estado base da interface para que seja totalmente funcional e legível sem o efeito de revelação. Isso é útil quando o efeito apenas adiciona um toque visual, mas não é um requisito para ler o conteúdo.