Introducción y objetivo
Este tutorial muestra, con todo lujo de detalles, cómo crear tarjetas (cards) con bordes animados usando conic-gradient y técnicas modernas de CSS. Verás varias implementaciones: una versión estática, una versión animada continuamente, una versión que se activa al pasar el ratón, y cómo integrar el código en WordPress (Custom CSS, functions.php). También se incluyen consideraciones de accesibilidad, rendimiento y soluciones a problemas comunes.
Concepto básico: ¿por qué conic-gradient para bordes?
conic-gradient genera un degradado en forma circular alrededor de un centro. Si lo colocamos en un pseudo-elemento que se sitúa detrás o delante del contenido y lo enmascaramos o recortamos correctamente, podemos mostrar únicamente una franja exterior que actúe como borde. Al animar el pseudo-elemento (por ejemplo rotándolo), el degradado parece desplazarse alrededor de la tarjeta, creando un borde animado muy atractivo sin imágenes ni SVG extras.
Ventajas de esta técnica
- No necesita imágenes: todo es CSS puro.
- Alta personalización: colores, grosor, velocidad, dirección y forma.
- Buena performance si se usa con transform y compositing adecuados.
- Se integra fácilmente en WordPress mediante CSS adicional o un archivo de estilos.
HTML básico de la tarjeta
Usa esta estructura mínima dentro de un bloque HTML o en el editor de bloques (Bloque HTML o Bloque de Código). El contenido de la tarjeta se puede ajustar libremente.
ltdiv class=cg-cardgt
ltdiv class=cg-card__contentgt
lth3gtTítulo de la tarjetalt/h3gt
ltpgtTexto descriptivo, botón o enlaces.lt/pgt
lt/divgt
lt/divgt
CSS: tarjeta con borde animado continuo (versión base)
Explicación breve antes del código: creamos un pseudo-elemento ::before que ocupa todo el contenedor, con un conic-gradient como fondo. Para mostrar solo el borde usamos padding en el elemento principal y aplicamos background-clip / mask o recortamos con inset en el pseudo-elemento. A continuación un ejemplo completo y comentado.
/ Variables para fácil personalización /
.cg-card {
--border-size: 4px / grosor del borde /
--radius: 12px / radio de la tarjeta /
--gap-color: #0f1720 / color interior / fondo de la tarjeta /
--speed: 4s / duración de la animación /
--colors: #ff0080 0deg, #ff8c00 120deg, #40e0d0 240deg / paradas: color ángulo opcional /
position: relative
padding: 1rem / espaciado interior para el contenido /
border-radius: var(--radius)
background: var(--gap-color) / fondo interno visible entre borde y contenido /
overflow: hidden
isolation: isolate / crea un nuevo contexto de apilado /
}
/ Contenido de la tarjeta (dentro del borde) /
.cg-card__content {
position: relative
z-index: 2 / por encima del pseudo-elemento /
color: #fff
}
/ Pseudo-elemento que dibuja el borde con conic-gradient /
.cg-card::before {
content:
position: absolute
inset: calc(-1 var(--border-size)) / dejamos espacio para el borde fuera del elemento real /
z-index: 1
border-radius: calc(var(--radius) var(--border-size))
background: conic-gradient(from 0deg, var(--colors))
-webkit-mask:
linear-gradient(#fff, #fff) content-box,
linear-gradient(#000, #000)
-webkit-mask-composite: xor / para WebKit, recortar centro (ver fallback más abajo) /
mask-composite: exclude / para navegadores con soporte moderno /
padding: var(--border-size) / define el ancho visible del borde /
pointer-events: none
animation: cg-rotate var(--speed) linear infinite
}
/ Alternativa con máscara usando mask con SVG o con mask shorthand (mejor compatibilidad considerar polyfills) /
@keyframes cg-rotate {
to { transform: rotate(1turn) }
}
/ Ajustes para dispositivos pequeños o cuando no queremos animación perpetua /
@media (prefers-reduced-motion: reduce) {
.cg-card::before { animation: none }
}
Notas sobre el código anterior
- El uso de inset con valor negativo sitúa el pseudo-elemento por debajo y alrededor permitiendo que el borde parezca rodear el elemento.
- La máscara (mask / -webkit-mask) recorta el centro para que solo se vea la franja exterior. Si tu entorno no soporta mask-composite, puedes usar técnicas alternativas como dos pseudo-elementos o SVG.
- La animación rota el pseudo-elemento completo con la propiedad transform, lo que suele aprovechar la aceleración por GPU.
Versión: borde que aparece al pasar el ratón (hover)
Si prefieres que la animación se active solo en hover, controla la opacidad y la animación en :hover.
.cg-card:hover::before {
opacity: 1
transform: rotate(0)
animation: cg-rotate var(--speed) linear infinite
}
/ Inicio oculto /
.cg-card::before {
opacity: 0
transition: opacity .25s ease
transform: rotate(0deg)
}
Implementación alternativa usando dos pseudo-elementos (mejor compatibilidad)
Si la máscara compuesta no funciona bien en algunos navegadores, usa dos pseudo-elementos: uno para el degradado grande y otro para cubrir el centro.
.cg-card {
position: relative
border-radius: var(--radius)
background: var(--gap-color)
padding: 1rem
overflow: visible
}
.cg-card::before,
.cg-card::after {
content:
position: absolute
inset: 0
border-radius: calc(var(--radius) var(--border-size))
pointer-events: none
z-index: 1
}
/ Degradado animado, se expande para dejar sitio al frame /
.cg-card::before {
transform: translateZ(0)
background: conic-gradient(from 0deg, var(--colors))
padding: var(--border-size)
inset: calc(-1 var(--border-size))
animation: cg-rotate var(--speed) linear infinite
}
/ Capa superior que cubre el centro dejando solo el borde visible /
.cg-card::after {
inset: var(--border-size)
background: var(--gap-color)
border-radius: var(--radius)
}
Ejemplo completo: HTML CSS listo para pegar
Aquí un bloque completo, listo para pegar en un bloque HTML y añadir el CSS en el área de CSS adicional o en tu tema.
lt!-- HTML de la tarjeta --gt
ltdiv class=cg-cardgt
ltdiv class=cg-card__contentgt
lth3gtTítulo bonitolt/h3gt
ltpgtUna descripción corta que encaje con el diseño.lt/pgt
lt/divgt
lt/divgt
/ CSS completo (pegable en Customizer > Additional CSS) /
.cg-card {
--border-size: 6px
--radius: 14px
--gap-color: #0f1720
--speed: 5s
--colors: #ff3cac 0deg, #784ba0 120deg, #2b86c5 240deg
position: relative
padding: 1.25rem
border-radius: var(--radius)
background: var(--gap-color)
color: #fff
overflow: visible
}
.cg-card__content { position: relative z-index: 2 }
/ Uso de dos pseudo-elementos para máxima compatibilidad /
.cg-card::before,
.cg-card::after { content: position: absolute z-index: 1 border-radius: calc(var(--radius) var(--border-size)) pointer-events: none }
/ degradado y animación /
.cg-card::before {
inset: calc(-1 var(--border-size))
background: conic-gradient(from 0deg, var(--colors))
animation: cg-rotate var(--speed) linear infinite
}
/ tapa central /
.cg-card::after {
inset: var(--border-size)
background: var(--gap-color)
border-radius: var(--radius)
}
/ animación /
@keyframes cg-rotate { to { transform: rotate(1turn) } }
@media (prefers-reduced-motion: reduce) {
.cg-card::before { animation: none }
}
Integración específica en WordPress
Opción A: Customizer gt CSS adicional
- Ve a Apariencia gt Personalizar gt CSS adicional.
- Pega el CSS completo del ejemplo. Guarda y aplica la clase cg-card al bloque HTML o al contenedor que uses.
Opción B: Encolar un archivo CSS desde functions.php
Si prefieres añadir un archivo en tu tema hijo, crea un CSS y encola con este fragmento en functions.php del tema hijo.
/ functions.php (tema hijo) /
function mi_tema_enqueue_cg_card_styles() {
wp_enqueue_style( cg-cards-style, get_stylesheet_directory_uri() . /css/cg-cards.css, array(), 1.0 )
}
add_action( wp_enqueue_scripts, mi_tema_enqueue_cg_card_styles )
Opción C: Bloque HTML en Gutenberg
- Añade un bloque HTML y pega el HTML de la tarjeta.
- Añade el CSS en el Personalizador o usa un plugin de CSS adicional.
Personalizaciones y variantes
- Cambiar dirección / velocidad: modifica la variable –speed o el ángulo inicial de conic-gradient.
- Grosor del borde: ajusta –border-size.
- Bordes no uniformes: usa gradientes con paradas y transparente para conseguir secciones distintas.
- Modo hover solo: activa animación en :hover y deja el pseudo-elemento en opacidad 0 por defecto.
- Compatibilidad con temas oscuros claros: cambia –gap-color con clases específicas del tema.
Consideraciones de accesibilidad y rendimiento
- Reduce movimiento: Respeta prefer-reduced-motion y no forces animaciones en usuarios que lo han solicitado.
- Contraste: asegúrate de que el texto dentro de la tarjeta tenga contraste suficiente respecto al fondo interno.
- GPU friendly: anima transform en lugar de propiedades costosas (como top/left). La rotación del pseudo-elemento suele ser eficiente.
- Carga: evita animaciones excesivas si hay muchas tarjetas en la pantalla simultáneamente para no cargar el render.
Problemas comunes y soluciones rápidas
- La máscara no funciona en algún navegador: usa la versión con dos pseudo-elementos (antes explicada) que tiene mejor compatibilidad.
- El borde parece pixelado: aumenta el tamaño del pseudo-elemento o evita background-size pequeños también prueba transform: translateZ(0) para forzar composición por GPU.
- Animación no se reproduce en móvil: comprueba prefers-reduced-motion y que no haya reglas específicas del tema que sobrescriban las animaciones.
- El borde se solapa con otros elementos: usa z-index y asegúrate de que el contenedor tenga position: relative e isolation: isolate si es necesario.
Ejemplos avanzados (tips rápidos)
- Crear efecto marco 3D: añade sombra y un segundo pseudo-elemento con otro degradado más sutil.
- Sin fin de colores: usa variables CSS para inyectar colores desde el editor de bloques o ACF y cambia –colors dinámicamente.
- Animación por scroll: combina con IntersectionObserver (JS) para iniciar la animación cuando la tarjeta entra en viewport.
Fragmento opcional: iniciar animación al entrar en viewport (JS)
Si deseas que la animación empiece solo cuando la tarjeta entra en pantalla, puedes usar IntersectionObserver y añadir una clase que active la animación. Pega este JS en un archivo o en un bloque de scripts de tu tema (este código es mínimo).
// Activar animación al entrar en viewport
document.addEventListener(DOMContentLoaded, function () {
const cards = document.querySelectorAll(.cg-card)
if (!(IntersectionObserver in window)) {
// Fallback: activar todas
cards.forEach(c => c.classList.add(in-view))
return
}
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.classList.add(in-view)
// si quieres observar solo una vez:
io.unobserve(e.target)
}
})
}, { threshold: 0.2 })
cards.forEach(c => io.observe(c))
})
/ En CSS activa animación solo cuando .in-view está presente /
.cg-card::before { animation: none }
.cg-card.in-view::before { animation: cg-rotate var(--speed) linear infinite }
Resumen
Crear tarjetas con bordes animados mediante conic-gradient es una técnica versátil, eficiente y altamente personalizable. Puedes implementarla desde el Customizer de WordPress, inyectando CSS, o encolando archivos en tu tema hijo. Usando pseudo-elementos y máscaras (o la alternativa de dos pseudo-elementos) obtendrás resultados consistentes. Ten en cuenta accesibilidad, prefer-reduced-motion y optimización para mantener buena experiencia en todas las plataformas.
Leave a Reply