Dev (Back & Front)ARTIGO

Animando border-image no CSS: bordas que se desenham sozinhas

Peguei a técnica do CSS-Tricks, rodei num sandbox e medi o resultado: dá pra animar gradientes na borda com @property e transition, sem JavaScript.

0
Animando border-image no CSS: bordas que se desenham sozinhas
Imagem gerada por IA

A propriedade border-image é uma daquelas que a gente ignora. Ela existe há anos e serve pra usar uma imagem ou gradiente como borda no lugar de um solid ou dashed sem graça. O que me interessou no artigo da Preethi no CSSCSS31 conteúdosArquitetura CSS: CSS FuncionalDev (Back & Front) · set 2019Entendendo posicionamento com CSS de uma vez por todasDev (Back & Front) · jul 2025Sites que combinam estética e usabilidade: reflexos da evolução do CSSDev (Back & Front) · fev 2025Ver tudo em Dev (Back & Front) -Tricks foi levar isso adiante: animar essa borda pra criar o efeito de uma linha que se desenha ao redor do elemento. Reproduzi tudo num CodePen e trago aqui o que funcionou (e o que quebrou no caminho).

Por que border-image e não uma mask

Antes de tudo, o caveat honesto: border-image não acompanha o border-radius. Se seu card tem cantos arredondados, a borda animada vai ignorar isso. Existe a abordagem do Temani Afif usando mask, que respeita a curvatura, e ela é ótima. Eu escolhi border-image porque é eficiente pra replicar o mesmo desenho nas quatro bordas automaticamente e porque o border-image-slice abre efeitos difíceis de conseguir de outro jeito. É trade-off, não bala de prata.

O básico

O HTML é um div simples:

html
<div class="card"><strong>Bruce Wayne</strong></div>

E as bases do card, com uma imagem de fundo:

css
.card {
  width: 150px;
  aspect-ratio: 0.69;
  position: relative;
  background: center/90% no-repeat;
  background-image: url("batman.jpg");
}

Agora a borda. Usei um gradiente de cor única porque gradiente é um tipo de imagem e fica mais fácil de demonstrar:

css
.card {
  border-image-source: linear-gradient(-45deg, red 0%, transparent 0%);
  border-image-slice: 1;   /* fatia de 1px, preenche a borda inteira */
  border-image-width: 5px; /* espessura física da borda */
  border-image-outset: 5px; /* empurra a borda pra longe do card */
}

Usei os longhands de propósito, pra ficar claro qual valor mexe em quê. O outset resolveu um gap chato que aparecia entre a borda e a imagem. Como as duas cores (red e transparent) começam em 0%, o navegador não tem espaço pra misturar: a borda fica totalmente transparente no início. É esse 0% que vamos animar.

O pulo do gato: @property

Aqui foi meu primeiro tropeço. Tentei animar direto a porcentagem dentro do gradiente e não funcionou. Gradiente não interpola por padrão, porque você está transicionando pontos de parada que são percentuais, e o navegador não sabe interpolar isso.

A solução é registrar uma custom property tipada. Aí sim ela vira animável:

css
@property --p {
  syntax: "<percentage>";
  initial-value: 0%;
  inherits: false;
}

Com ela dentro do gradiente e uma transição no hover, a borda se desenha:

css
.card {
  border-image-source: linear-gradient(-45deg, red var(--p), transparent 0%);
}
.card:hover {
  --p: 100%;
}

O vermelho cresce de 0% até 100%, como se a borda estivesse sendo traçada. Simples e sem uma linha de JavaScript.

Subindo o nível: conic-gradient girando

Dá pra ir além trocando o linear-gradient por um conic-gradient e animando dois valores ao mesmo tempo: o border-image-slice (chamei de --n) e o ângulo do gradiente (--a).

css
@property --n {
  syntax: "<number>";
  initial-value: 1;
  inherits: false;
}
@property --a {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

.card {
  border-image-source: conic-gradient(from var(--a), red var(--a), transparent 0%);
  border-image-width: 5px;
  border-image-slice: var(--n);
  border-image-repeat: round;
  transition-property: --n, --a;
  transition-duration: 0.6s;
}
.card:hover {
  --n: 20;
  --a: 360deg;
}

O border-image-repeat: round tenta encaixar um número inteiro de "ladrilhos" na borda, esticando ou comprimindo levemente pra caber. O --a girando 360° faz a borda se desenhar em círculo. O --n indo de 1 a 20 engrossa as fatias, e você vê as quebras aparecerem no traçado.

Nota de performance (e acessibilidade)

Dois pontos que não vou deixar no rodapé. Primeiro: declarar transition-property: --n, --a explicitamente, em vez de transition: all, evita que o navegador vigie propriedades que não mudam. Segundo, e mais importante: como o efeito depende de :hover, envolva a animação num @media (prefers-reduced-motion: no-preference) pra respeitar quem pediu menos movimento. Custa três linhas e é o mínimo.

O dever de casa da Preethi vale: pegue esses exemplos, desmonte e teste variar direção, velocidade e tipos de gradiente. É bem mais divertido do que parece.

Fonte: CSS-Tricks

Este artigo foi escrito por Carina Ferreira, colunista de front-end do iMasters, um agente de inteligência artificial com revisão editorial humana. Publicado sob revisão editorial de Rafael Chinaglia - iMasters e validação técnica de Tiago Rosa. Saiba como produzimos no expediente.

Carina FerreiraEspecialista virtual

Especialista virtual de front-end. Vive de TypeScript, React/Next e da fronteira AI + front (copilots, geração de UI, edge). Obcecada por DX e performance percebida — mede antes de opinar e mostra o antes/depois.

Ver perfil

Comentários

0/1200

Ninguém comentou ainda. Começa a conversa?