# Efeito hover com gradiente CSS: quatro formas de o animar

[Canonical HTML page](https://gradiently.design/pt-pt/guide/css-hover-gradient)

Se escreveres um gradiente novo em :hover, ele salta em vez de suavizar. Os browsers não conseguem fazer transição de um gradiente para outro, mas conseguem movê-lo, esbatê-lo ou animar as cores lá dentro. Aqui fica cada método, com o código.

## The short version

- Um efeito hover com gradiente CSS não se faz com transition em background-image, porque os browsers trocam os gradientes instantaneamente em vez de os interpolar.
- A técnica mais compatível é um gradiente sobredimensionado com transition em background-position, que desliza as novas cores para a vista.
- Registar paragens de cor ou ângulos com @property permite ao browser fazer a transição suavemente, e funciona em todos os principais browsers atuais.
- Esbater a opacidade de um segundo gradiente num pseudo-elemento é a técnica mais barata de renderizar, porque as mudanças de opacidade não obrigam a repintar.
- Os efeitos hover devem estar dentro de uma media query de hover e respeitar prefers-reduced-motion.

Um **efeito hover com gradiente CSS** precisa de um truque, porque os browsers não fazem transition de `background-image`: muda o gradiente em `:hover` e ele salta. Quatro técnicas dão um resultado suave. Deslizar um gradiente sobredimensionado com `background-position`, animar paragens de cor registadas com `@property`, fundir um segundo gradiente num pseudo-elemento, ou mover um brilho radial com o cursor. Cada uma serve elementos diferentes, e todas estão abaixo com código.

## Porque é que o transition não funciona em gradientes

Para o browser, um gradiente é uma imagem. Sabe interpolar um número ou uma cor, mas não uma imagem noutra, por isso `background-image` não é interpolado e o novo gradiente substitui o antigo de imediato. As propriedades personalizadas usadas dentro do gradiente comportam-se da mesma forma, a não ser que as registes, porque uma propriedade não registada é apenas texto.

```css
/* This snaps, with no easing */
.btn {
  background-image: linear-gradient(135deg, #1e1b4b, #6d28d9);
  transition: background-image 400ms ease;
}
.btn:hover {
  background-image: linear-gradient(135deg, #6d28d9, #f472b6);
}
```

A linha transition é ignorada nos gradientes. Todas as técnicas abaixo contornam isto.

- Estado de repouso: `linear-gradient(135deg, #1e1b4b 0%, #6d28d9 100%)`
- Estado hover: `linear-gradient(135deg, #6d28d9 0%, #f472b6 100%)`

Os dois estados usados nos exemplos. O objetivo é uma viagem suave da amostra da esquerda para a da direita.

## Técnica 1: deslizar um gradiente sobredimensionado

Faz o fundo maior do que o elemento, pinta lá dentro os dois estados e depois move-o. O `background-position` pode ter transition, por isso as novas cores deslizam para dentro. Funciona em todos os browsers e não precisa de registo.

```css
.btn {
  background-image: linear-gradient(90deg, #1e1b4b 0%, #6d28d9 50%, #f472b6 100%);
  background-size: 200% 100%;
  background-position: 0% 50%;
  transition: background-position 500ms ease;
}

.btn:hover {
  background-position: 100% 50%;
}
```

Em repouso vês a metade esquerda, de índigo a violeta. Em hover, a metade direita, de violeta a rosa, desliza para o lugar.

A faixa completa a 200%: `linear-gradient(90deg, #1e1b4b 0%, #6d28d9 50%, #f472b6 100%)`

Este é o fundo inteiro. O botão só mostra metade de cada vez.

O movimento lê-se como uma varredura, o que serve botões e ligações. É menos convincente em cartões grandes, onde se vê o gradiente a viajar. Animar `background-position` obriga a repintar, o que não é problema em elementos pequenos; para uma superfície grande, usa a técnica 3. [Botões com gradiente em CSS](https://gradiently.design/pt-pt/guide/css-gradient-button) tem mais padrões de botões construídos sobre isto.

## Técnica 2: animar paragens de cor com @property

Regista as cores das paragens como propriedades personalizadas tipadas e o browser consegue interpolá-las. O gradiente muda então de cor no mesmo sítio, sem deslizar. O `@property` funciona em todos os principais browsers atuais; os mais antigos limitam-se a saltar, o que é um recurso aceitável para um hover. As regras de registo estão em [CSS @property](https://gradiently.design/pt-pt/guide/css-property-animation).

```css
@property --stop-a {
  syntax: "<color>";
  inherits: false;
  initial-value: #1e1b4b;
}

@property --stop-b {
  syntax: "<color>";
  inherits: false;
  initial-value: #6d28d9;
}

.card {
  background-image: linear-gradient(135deg, var(--stop-a), var(--stop-b));
  transition: --stop-a 450ms ease, --stop-b 450ms ease;
}

.card:hover {
  --stop-a: #6d28d9;
  --stop-b: #f472b6;
}
```

Cada paragem suaviza-se de forma independente. Dá-lhes durações diferentes e a cor parece fluir de um canto para o outro.

O mesmo truque anima o ângulo. Regista `--angle` com `syntax: "<angle>"`, usa-o em `linear-gradient(var(--angle), ...)` e muda-o no hover: um gradiente que roda de 135 para 225 graus quando o ponteiro chega é um toque subtil e cuidado para cartões e [contornos com gradiente](https://gradiently.design/pt-pt/guide/css-gradient-border).

As duas técnicas até aqui também funcionam em [texto com gradiente](https://gradiently.design/pt-pt/guide/css-gradient-text). O texto recortado com `background-clip: text` mostra o que o fundo fizer, por isso um deslize ou uma transição de paragens num título faz a cor correr pelas letras no hover. Mantém a mudança suave em títulos que as pessoas precisam de ler e garante que os dois estados têm contraste suficiente com a página.

## Técnica 3: fundir dois gradientes

Põe o gradiente de hover num pseudo-elemento por cima da base e esbate a sua opacidade. As mudanças de opacidade são tratadas pelo compositor sem repintar, por isso esta é a escolha mais suave para cartões grandes, painéis de destaque e tudo o que esteja numa grelha longa.

```css
.tile {
  position: relative;
  isolation: isolate;
  background-image: linear-gradient(135deg, #1e1b4b, #6d28d9);
}

.tile::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background-image: linear-gradient(135deg, #6d28d9, #f472b6);
  opacity: 0;
  transition: opacity 400ms ease;
}

.tile:hover::before {
  opacity: 1;
}
```

`z-index: -1` com `isolation: isolate` mantém o pseudo-elemento atrás do texto do bloco, mas por cima do gradiente de base.

## Técnica 4: um brilho que segue o cursor

Um gradiente radial posicionado com propriedades personalizadas consegue seguir o ponteiro. Define `--x` e `--y` a partir de um pequeno script e a luz acompanha a mão. Mantém-no suave e longe do texto: deve parecer luz sobre uma superfície, nunca um halo à volta das palavras.

```css
.panel {
  background-image:
    radial-gradient(circle 220px at var(--x, 50%) var(--y, 50%), rgb(244 114 182 / 0.35), transparent 70%),
    linear-gradient(135deg, #1e1b4b, #312e81);
}

/* JavaScript:
   panel.addEventListener("pointermove", (e) => {
     const r = panel.getBoundingClientRect();
     panel.style.setProperty("--x", `${e.clientX - r.left}px`);
     panel.style.setProperty("--y", `${e.clientY - r.top}px`);
   });
*/
```

O brilho é uma camada de topo sobre uma base escura. Atualizar duas propriedades personalizadas é barato, mas cada movimento repinta, por isso usa-o em poucos painéis, não em todos os cartões de uma página.

## Escolher uma técnica

| Técnica | Parece | Custo de renderização | Ideal para |
| --- | --- | --- | --- |
| Deslizar com background-position | Uma varredura de cor nova | Repinta durante a transição | Botões, ligações, etiquetas |
| Paragens de cor com @property | Cores a mudar no mesmo sítio | Repinta durante a transição | Cartões, emblemas, contornos |
| Fusão por opacidade | Um gradiente a dissolver-se noutro | Composto, sem repintar | Painéis grandes, grelhas longas |
| Brilho do cursor | Luz que segue o ponteiro | Repinta a cada movimento | Alguns painéis de destaque |

Na dúvida, funde por opacidade. É a mais barata e funciona em todo o lado.

Para gradientes que se movem sozinhos e não no hover, vê [gradientes animados em CSS](https://gradiently.design/pt-pt/guide/css-animated-gradient). Para uma visão mais ampla do que custa tempo de pintura, vê [desempenho dos gradientes CSS](https://gradiently.design/pt-pt/guide/css-gradient-performance).

## Ecrãs táteis e movimento reduzido

### Evita

- Estilos de hover que ficam presos depois de um toque no telemóvel
- Informação que só aparece no hover
- Animações de hover longas e em ciclo
- Ignorar a definição de movimento reduzido

### Faz

- Envolve as regras de hover em `@media (hover: hover)`
- Mostra o mesmo estado em `:active` para toque
- Mantém as transições entre 200 e 500 ms
- Corta o movimento com `prefers-reduced-motion` e mantém a mudança de cor

```css
@media (hover: hover) {
  .btn:hover { background-position: 100% 50%; }
}

@media (prefers-reduced-motion: reduce) {
  .btn { transition: none; }
}
```

A cor continua a mudar para todos; só o movimento é retirado a quem pediu menos.

Os efeitos hover dão vida a um site. Nas publicações sociais não há ponteiro, por isso o movimento tem de viver no próprio design. Os Marks do Gradiently podem trazer o seu próprio movimento suave e, com o Pro, um design exporta-se em MP4, WebM ou GIF onde o browser suporta gravação, pronto para um story ou uma publicação.

## FAQ

### É possível fazer transition de um gradiente CSS no hover?

Não diretamente. O `background-image` muda de imediato. Faz transition de `background-position` num gradiente sobredimensionado, anima paragens registadas com `@property` ou esbate a opacidade de um segundo gradiente.

### Como faço um botão com gradiente mudar de cor suavemente no hover?

Usa `background-size: 200% 100%` com um gradiente de três paragens e depois faz transition de `background-position` de `0% 50%` para `100% 50%` no hover.

### O @property funciona em todos os browsers?

Funciona nas versões atuais do Chrome, Edge, Safari e Firefox. Os browsers mais antigos saltam entre estados em vez de os suavizar.

### Qual é o efeito hover com gradiente de melhor desempenho?

A fusão por opacidade: o gradiente de hover fica num pseudo-elemento e só muda a sua opacidade, o que evita repintar.

### Os efeitos hover com gradiente devem respeitar o movimento reduzido?

Sim. Com `prefers-reduced-motion: reduce`, retira o movimento e mantém uma simples mudança de cor.
