El CSS :has()
pseudo clase se está implementando en muchos navegadores con Chrome y Safari ya lo apoya plenamente. A menudo se lo conoce como "el selector principal", ya que podemos seleccionar el estilo de un elemento principal desde un selector secundario, pero hay mucho más que :has()
puede ayudarnos a resolver. Una de esas cosas es reinventar el patrón de tarjeta en el que se puede hacer clic que a muchos de nosotros nos encanta usar de vez en cuando.
Echaremos un vistazo a cómo :has()
puede ayudarnos a manejar las tarjetas vinculadas, pero primero...
:has()
pseudo clase?
Que es esto Ya hay un manojo of maravillosa mensajes flotando alrededor que hacen un excelente trabajo explicando lo que :has()
es y para qué se usa, pero aún es lo suficientemente nuevo como para que también debamos decir algunas palabras al respecto aquí.
:has()
es una pseudoclase relacional que forma parte de la Borrador de trabajo de nivel 3 de selectores W4C. De eso se tratan los paréntesis: elementos coincidentes que están relacionados con, o, más exactamente, contienen, ciertos elementos secundarios.
/* Matches an article element that contains an image element */
article:has(img) { }
/* Matches an article element with an image contained immediately within it */
article:has(> img) { }
Entonces, puede ver por qué querríamos llamarlo selector "principal". Pero también podemos combinarlo con otras pseudoclases funcionales para ser más específicos. Digamos que queremos diseñar artículos que no no contener ninguna imagen. Podemos combinar los poderes relacionales de :has()
con los poderes de negación de :not()
Para hacer eso:
/* Matches an article without images */
article:not(:has(img)) { }
Pero eso es solo el comienzo de cómo podemos combinar poderes para hacer más con :has()
. Antes de pasar específicamente a resolver el enigma de la tarjeta en la que se puede hacer clic, veamos algunas formas en que actualmente las abordamos sin usar :has()
.
Cómo manejamos actualmente las tarjetas en las que se puede hacer clic
Hay tres enfoques principales sobre cómo las personas crean una tarjeta en la que se puede hacer clic en estos días y para comprender completamente el poder de esta pseudoclase, es bueno tener un poco de resumen.
El enfoque del “enlace como envoltorio”
Este enfoque es algo que se usa con bastante frecuencia. Nunca uso este enfoque, pero creé una demostración rápida para demostrarlo:
Hay muchas preocupaciones aquí, especialmente cuando se trata de accesibilidad. Cuando los usuarios navegan por su sitio web utilizando la función del rotor, escucharán el texto completo dentro de ese elemento: el encabezado, el texto y el enlace. Alguien podría no querer sentarse a través de todo eso. Podemos hacerlo mejor. Desde HTML5, podemos anidar elementos de bloque dentro de un
elemento. Pero nunca se siente bien para mí, especialmente por esta razón.
Pros:
- Rápido de implementar
- semánticamente correcto
Contras:
- Problemas de accesibilidad
- Texto no seleccionable
- Muchas molestias para sobrescribir los estilos que usó en sus enlaces predeterminados
El método JavaScript
Usando JavaScript, podemos adjuntar un enlace a nuestra tarjeta en lugar de escribirlo en el marcado. Encontré esta gran demostración de CodePen por costodesv quien también hizo que el texto de la tarjeta fuera seleccionable en el proceso:
Este enfoque tiene muchos beneficios. Nuestros enlaces son accesibles en foco e incluso podemos seleccionar texto. Pero hay algunos inconvenientes cuando se trata de estilo. Si queremos animar esas tarjetas, por ejemplo, tendríamos que agregar :hover
estilos en nuestro principal .card
envoltorio en lugar del enlace en sí. Tampoco nos beneficiaríamos de las animaciones cuando los enlaces están enfocados desde el teclado.
Pros:
- Se puede hacer perfectamente accesible
- Posibilidad de seleccionar texto
Contras:
- Requiere JavaScript
- No es posible hacer clic con el botón derecho (aunque podría solucionarse con algunas secuencias de comandos adicionales)
- Requerirá mucho estilo en la tarjeta en sí, lo que no funcionaría al enfocar el enlace
::after
enfoque selector
La Este método requiere que configuremos la tarjeta con un posicionamiento relativo, luego configuremos un posicionamiento absoluto en el enlace ::after
pseudoselector de un enlace. Esto no requiere JavaScript y es bastante fácil de implementar:
Aquí hay algunos inconvenientes, especialmente cuando se trata de seleccionar texto. A menos que proporcione un índice z más alto en el cuerpo de su tarjeta, no podrá seleccionar texto, pero si lo hace, tenga en cuenta que hacer clic en el texto no activará su enlace. Depende de usted si desea o no texto seleccionable. Creo que puede ser un problema de UX, pero depende del caso de uso. El texto aún es accesible para los lectores de pantalla, pero mi principal problema con el método es la falta de posibilidades de animación.
Pros:
- Fácil de implementar
- Enlace accesible sin texto inflado
- Funciona en hover y focus
Contras:
- El texto no es seleccionable
- Solo puede animar el enlace ya que este es el elemento que está desplazando.
::after
:has()
Un nuevo enfoque: usar Ahora que hemos establecido los enfoques existentes para las tarjetas en las que se puede hacer clic, quiero mostrar cómo introducir :has()
a la mezcla resuelve la mayoría de esas deficiencias.
De hecho, vamos a basar este enfoque en el último que vimos usando ::after
en el elemento de enlace. En realidad podemos usar :has()
allí para superar las limitaciones de animación de ese enfoque.
Comencemos con el marcado:
Some Heading
Curabitur convallis ac quam vitae laoreet. Nulla mauris ante, euismod sed lacus sit amet, congue bibendum eros. Etiam mattis lobortis porta. Vestibulum ultrices iaculis enim imperdiet egestas.
Mantendré las cosas lo más simples posible apuntando a elementos en el CSS en lugar de clases.
Para esta demostración, agregaremos un zoom de imagen y una sombra a la tarjeta al pasar el mouse por encima, y animaremos el enlace con una flecha que aparece y cambiaremos el color del texto del enlace. Para facilitar esto, vamos a agregar algunas propiedades personalizadas en el ámbito de nuestra tarjeta. Aquí está el estilo básico:
/* The card element */
article {
--img-scale: 1.001;
--title-color: black;
--link-icon-translate: -20px;
--link-icon-opacity: 0;
position: relative;
border-radius: 16px;
box-shadow: none;
background: #fff;
transform-origin: center;
transition: all 0.4s ease-in-out;
overflow: hidden;
}
/* The link's ::after pseudo */
article a::after {
content: "";
position: absolute;
inset-block: 0;
inset-inline: 0;
cursor: pointer;
}
¡Excelente! Agregamos una escala inicial para la imagen (--img-scale: 1.001
), el color inicial del encabezado de la tarjeta (--title-color: black
) y algunas propiedades adicionales que usaremos para hacer que nuestra flecha salga del enlace. También hemos establecido un estado vacío del box-shadow
declaración para animarla más tarde. Esto configura lo que necesitamos para la tarjeta en la que se puede hacer clic en este momento, así que agreguemos algunos reinicios y estilos agregando esas propiedades personalizadas a los elementos que queremos animar:
article h2 {
margin: 0 0 18px 0;
font-family: "Bebas Neue", cursive;
font-size: 1.9rem;
letter-spacing: 0.06em;
color: var(--title-color);
transition: color 0.3s ease-out;
}
article figure {
margin: 0;
padding: 0;
aspect-ratio: 16 / 9;
overflow: hidden;
}
article img {
max-width: 100%;
transform-origin: center;
transform: scale(var(--img-scale));
transition: transform 0.4s ease-in-out;
}
article a {
display: inline-flex;
align-items: center;
text-decoration: none;
color: #28666e;
}
article a:focus {
outline: 1px dotted #28666e;
}
article a .icon {
min-width: 24px;
width: 24px;
height: 24px;
margin-left: 5px;
transform: translateX(var(--link-icon-translate));
opacity: var(--link-icon-opacity);
transition: all 0.3s;
}
.article-body {
padding: 24px;
}
Seamos amables con la gente y también agreguemos un clase de lector de pantalla escondido detrás del enlace:
.sr-only:not(:focus):not(:active) {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
Nuestra tarjeta está empezando a verse bastante dulce. Es hora de agregarle un poco de magia. Con el :has()
pseudo clase, ahora podemos verificar si nuestro enlace está sobrevolado o enfocado, luego actualice nuestras propiedades personalizadas y agregue un box-shadow
. Con esta pequeña porción de CSS, nuestra tarjeta realmente cobra vida:
/* Matches an article element that contains a hover or focus state */
article:has(:hover, :focus) {
--img-scale: 1.1;
--title-color: #28666e;
--link-icon-translate: 0;
--link-icon-opacity: 1;
box-shadow: rgba(0, 0, 0, 0.16) 0px 10px 36px 0px, rgba(0, 0, 0, 0.06) 0px 0px 0px 1px;
}
¿Ves lo que hay ahí arriba? Ahora tenemos los estilos actualizados si cualquier elemento secundario en la tarjeta se desplaza o se enfoca. Y aunque el elemento de enlace es lo único que puede contener un estado de desplazamiento o enfoque en el ::after
enfoque de tarjeta en la que se puede hacer clic, podemos usarlo para hacer coincidir el elemento principal y aplicar las transiciones.
Y ahí lo tienes. Solo otro poderoso caso de uso para el :has()
selector. No solo podemos hacer coincidir un elemento padre al declarar otros elementos como argumentos, sino que también podemos hacer coincidir usando pseudos para hacer coincidir y dar estilo a los padres también.
Pros:
- LA EXCELENCIA
- Animables
- No se necesita JavaScript
- Usos
:hover
en el elemento correcto
Contras:
- El texto no es fácilmente seleccionable.
- El soporte del navegador está limitado a Chrome y Safari (es compatible con Firefox detrás de una bandera).
Aquí hay una demostración usando esta técnica. Es posible que notes un envoltorio adicional alrededor de la tarjeta, pero solo soy yo jugando con consultas de contenedor, que es solo una de esas otras cosas fantásticas que se implementan en todos los principales navegadores.
Tengo algunos Otros ejemplos deseas compartir? Otras soluciones o ideas son más que bienvenidas en la sección de comentarios.