Una guía para los selectores de pseudoclase de CSS modernos y recientemente admitidos
Publicado: 2022-03-10 Los selectores de pseudoclase son los que comienzan con el carácter de dos puntos “ :
” y coinciden en función del estado del elemento actual. El estado puede ser relativo al árbol del documento o en respuesta a un cambio de estado como :hover
o :checked
.
:any-link
Aunque se define en el nivel 4 de selectores, esta pseudoclase ha tenido compatibilidad entre navegadores durante bastante tiempo. La any-link
coincidirá con un hipervínculo ancla siempre que tenga un href
. Coincidirá de una manera equivalente a hacer coincidir tanto :link
como :visited
a la vez. Esencialmente, esto puede reducir sus estilos en un selector si está agregando propiedades básicas como el color
que le gustaría aplicar a todos los enlaces, independientemente de su estado de visita.
:any-link { color: blue; text-underline-offset: 0.05em; }
Una nota importante sobre la especificidad es que :any-link
ganará contra a
como selector incluso si a
se coloca más abajo en la cascada ya que tiene la especificidad de una clase. En el siguiente ejemplo, los enlaces serán de color púrpura:
:any-link { color: purple; } a { color: red; }
Entonces, si introduce :any-link
, tenga en cuenta que deberá incluirlo en las instancias de a
como selector si estarán en competencia directa por la especificidad.
:focus-visible
Apuesto a que una de las infracciones de accesibilidad más comunes en la web es eliminar el outline
de elementos interactivos como enlaces, botones y entradas de formulario para su :focus
. Uno de los propósitos principales de ese outline
es servir como un indicador visual para los usuarios que utilizan principalmente teclados para navegar. Un estado de enfoque visible es fundamental como una herramienta de orientación a medida que los usuarios navegan por una interfaz y para ayudar a reforzar lo que es un elemento interactivo. Específicamente, el foco visible está cubierto en el Criterio de Conformidad 2.4.11 de las WCAG: Aspecto del foco (mínimo).
La :focus-visible
está diseñada para mostrar solo un anillo de enfoque cuando el agente de usuario determina a través de la heurística que debe ser visible. Dicho de otra manera: los navegadores determinarán cuándo aplicar :focus-visible
en función de elementos como el método de entrada, el tipo de elemento y el contexto de la interacción. Para fines de prueba a través de una computadora de escritorio con entrada de teclado y mouse, debería ver :focus-visible
adjuntos cuando ingresa a un elemento interactivo, pero no cuando hace clic en él, con la excepción de las entradas de texto y las áreas de texto que deben mostrar :focus-visible
para todos los tipos de entrada de enfoque.
Nota : Para obtener más detalles, revise el borrador de trabajo de la especificación :focus-visible
.
Las últimas versiones de los navegadores Firefox y Chromium ahora parecen manejar :focus-visible
en las entradas de formulario de acuerdo con la especificación que dice que la UA debe eliminar :focus
estilos cuando :focus-visible
coincide. Safari aún no es compatible con :focus-visible
, por lo que debemos asegurarnos de que se incluya un estilo de :focus
como respaldo para evitar eliminar el outline
para accesibilidad.
Dado un botón y una entrada de texto con el siguiente conjunto de estilos, veamos qué sucede:
input:focus, button:focus { outline: 2px solid blue; outline-offset: 0.25em; } input:focus-visible { outline: 2px solid transparent; border-color: blue; } button:focus:not(:focus-visible) { outline: none; } button:focus-visible { outline: 2px solid transparent; box-shadow: 0 0 0 2px #fff, 0 0 0 4px blue; }
cromo y firefox
-
input
Elimine correctamente:focus
cuando los elementos se enfocan a través de la entrada del mouse a favor de:focus-visible
lo que resulta en cambiar elborder-color
y ocultar eloutline
en la entrada del teclado -
button
No solo usa:focus-visible
sin la regla adicional parabutton:focus:not(:focus-visible)
que elimina el contorno en:focus
, pero permitirá la visibilidad de labox-shadow
solo en la entrada del teclado
Safari
-
input
Continúa usando solo los estilos:focus
-
button
Esto parece ahora respetar parcialmente la intención de:focus-visible
en el botón al ocultar los:focus
styles al hacer clic, pero aún muestra los:focus
styles en la interacción del teclado
Entonces, por ahora, la recomendación sería continuar incluyendo :focus
y luego mejorar progresivamente hasta usar :focus-visible
que permite el código de demostración. Aquí hay un CodePen para que continúe probando con:
:focus-within
La :focus-within
es compatible con todos los navegadores modernos y actúa casi como un selector principal, pero solo para una condición muy específica. Cuando se adjunta a un elemento contenedor y un elemento secundario coincide con :focus
, los estilos se pueden agregar al elemento contenedor y/o a cualquier otro elemento dentro del contenedor.
Una mejora práctica para usar este comportamiento es diseñar una etiqueta de formulario cuando la entrada asociada tiene el foco. Para que esto funcione, envolvemos la etiqueta y la entrada en un contenedor, y luego adjuntamos :focus-within
a ese contenedor y seleccionamos la etiqueta:
.form-group:focus-within label { color: blue; }
Esto da como resultado que la etiqueta se vuelva azul cuando la entrada tiene foco.
Esta demostración de CodePen también incluye agregar un esquema directamente al contenedor .form-group
:
:is()
También conocida como la pseudoclase "coincide con cualquier", :is()
puede tomar una lista de selectores para intentar compararlos. Por ejemplo, en lugar de enumerar los estilos de encabezado individualmente, puede agruparlos bajo el selector de :is(h1, h2, h3)
.
Un par de comportamientos únicos sobre la lista de selectores :is()
:
- Si un selector de la lista no es válido, la regla seguirá coincidiendo con los selectores válidos. Dado
:is(-ua-invalid, article, p)
la regla coincidirá conarticle
yp
. - La especificidad calculada será igual a la del selector pasado con la especificidad más alta. Por ejemplo,
:is(#id, p)
tendrá la especificidad de#id
— 1.0.0 — mientras que:is(p, a)
tendrá una especificidad de 0.0.1.
El primer comportamiento de ignorar los selectores no válidos es un beneficio clave. Al usar otros selectores en un grupo donde un selector no es válido, el navegador descartará toda la regla. Esto entra en juego en algunos casos en los que los prefijos del proveedor aún son necesarios, y la agrupación de selectores con prefijo y sin prefijo hace que la regla falle en todos los navegadores. Con :is()
puede agrupar con seguridad esos estilos y se aplicarán cuando coincidan y se ignorarán cuando no coincidan.
Para mí, agrupar estilos de encabezado como se mencionó anteriormente ya es una gran victoria con este selector. También es el tipo de regla con el que me sentiría cómodo usando sin respaldo al aplicar estilos no críticos, como:
:is(h1, h2, h3) { line-height: 1.2; } :is(h2, h3):not(:first-child) { margin-top: 2em; }
En este ejemplo (que proviene de los estilos de documento en mi proyecto SmolCSS), tener la mayor line-height
heredada de los estilos base o la falta del margin-top
no es realmente un problema para los navegadores que no son compatibles. Es simplemente menos que ideal. Lo que no querría usar :is()
todavía serían estilos de diseño críticos como Grid o Flex que controlan significativamente su interfaz.
Además, cuando se encadena a otro selector, puede probar si el selector base coincide con un selector descendiente dentro de :is()
. Por ejemplo, la siguiente regla selecciona solo párrafos que son descendientes directos de artículos. El selector universal se utiliza como referencia al selector de base p
.
p:is(article > *)
Para obtener el mejor soporte actual, si desea comenzar a usarlo, también querrá duplicar los estilos al incluir reglas duplicadas usando :-webkit-any()
y :matches()
. ¡Recuerde hacer estas reglas individuales, o incluso el navegador compatible lo eliminará! En otras palabras, incluya todo lo siguiente:
:matches(h1, h2, h3) { } :-webkit-any(h1, h2, h3) { } :is(h1, h2, h3) { }
Vale la pena mencionar en este punto que, junto con los selectores más nuevos, hay una variación actualizada de @supports
, que es el @supports selector
. Esto también está disponible como @supports not selector
.
Nota : en la actualidad (de los navegadores modernos), solo Safari no admite esta regla.
Podría verificar la compatibilidad con :is()
con algo como lo siguiente, pero en realidad perdería la compatibilidad con Safari, ya que Safari admite :is()
pero no admite @supports selector
.
@supports selector(:is(h1)) { :is(h1, h2, h3) { line-height: 1.1; } }
:where()
La pseudoclase :where()
es casi idéntica a :is()
excepto por una diferencia crítica: siempre tendrá cero especificidad. Esto tiene implicaciones increíbles para las personas que están construyendo marcos, temas y sistemas de diseño . Usando :where()
, un autor puede establecer valores predeterminados y los desarrolladores posteriores pueden incluir anulaciones o extensiones sin conflictos de especificidad.
Considere el siguiente conjunto de estilos img
. Usando :where()
, incluso con un selector de mayor especificidad, la especificidad sigue siendo cero. En el siguiente ejemplo, ¿qué borde de color crees que tendrá la imagen?
:where(article img:not(:first-child)) { border: 5px solid red; } :where(article) img { border: 5px solid green; } img { border: 5px solid orange; }
La primera regla tiene cero especificidad ya que está completamente contenida dentro de :where()
. Entonces, directamente contra la segunda regla, la segunda regla gana. Al presentar el selector de solo elemento img
como la última regla, ganará debido a la cascada. Esto se debe a que calculará con la misma especificidad que la regla :where(article) img
ya que la porción :where()
no aumenta la especificidad.
Usar :where()
junto con las alternativas es un poco más difícil debido a la función de especificidad cero, ya que es probable que esa función sea la razón por la que querría usarla sobre :is()
. Y si agrega reglas de respaldo, es probable que superen a :where()
debido a su propia naturaleza. Y tiene un mejor soporte general que el @supports selector
por lo que no es probable que tratar de usarlo para crear una alternativa proporcione mucha ganancia (si la hay). Básicamente, tenga en cuenta la incapacidad de crear correctamente respaldos para :where()
y verifique cuidadosamente sus propios datos para determinar si es seguro comenzar a usarlos para su audiencia única.
Puede seguir probando :where()
con el siguiente CodePen que usa los selectores img
de arriba:
Mejorado :not()
El selector base :not()
ha sido compatible desde Internet Explorer 9. Pero el nivel 4 de selectores mejora :not()
al permitirle tomar una lista de selectores, al igual que :is()
y :where()
.
Las siguientes reglas proporcionan el mismo resultado en navegadores compatibles:
article :not(h2):not(h3):not(h4) { margin-bottom: 1.5em; } article :not(h2, h3, h4) { margin-bottom: 1.5em; }
La capacidad de :not()
para aceptar una lista de selectores tiene una gran compatibilidad con los navegadores modernos.
Como vimos con :is()
, Enhanced :not()
también puede contener una referencia al selector base como descendiente usando *
. Este CodePen demuestra esta capacidad al seleccionar enlaces que no son descendientes de nav
.
Bonificación : la demostración anterior también incluye un ejemplo de encadenamiento de :not()
y :is()
para seleccionar imágenes que no son hermanos adyacentes de los elementos h2
o h3
.
Propuesto pero “en riesgo” — :has()
La pseudo-clase final que es una propuesta muy emocionante pero que no tiene ningún navegador actual que la implemente, incluso de forma experimental, es :has()
. De hecho, aparece en el Borrador del Editor del Nivel 4 del Selector como “en riesgo”, lo que significa que se reconoce que tiene dificultades para completar su implementación y, por lo tanto, puede eliminarse de la recomendación.
Si se implementa, :has()
sería esencialmente el "selector principal" que muchas personas de CSS anhelaban tener disponible. Funcionaría con una lógica similar a una combinación de :focus-within
y :is()
con selectores de descendientes, donde está buscando la presencia de descendientes pero el estilo aplicado sería para el elemento principal.
Dada la siguiente regla, si la navegación contenía un botón, entonces la navegación habría disminuido el relleno superior e inferior:
nav { padding: 0.75rem 0.25rem; nav:has(button) { padding-top: 0.25rem; padding-bottom: 0.25rem; }
Una vez más, esto no está implementado actualmente en ningún navegador, ni siquiera de forma experimental, ¡pero es divertido pensar en ello! Robin Rendle proporcionó información adicional sobre este futuro selector en CSS-Tricks.
Mención de Honor del Nivel 3: :empty
Una pseudoclase útil que puede haber pasado por alto del nivel 3 de selectores es :empty
, que coincide con un elemento cuando no tiene elementos secundarios, incluidos los nodos de texto.
La regla p:empty
coincidirá con <p></p>
pero no con <p>Hello</p>
.
Una forma de usar :empty
es ocultar elementos que quizás sean marcadores de posición para contenido dinámico que se completa con JavaScript. Tal vez tenga un div que recibirá los resultados de la búsqueda y, cuando se complete, tendrá un borde y algo de relleno. Pero sin resultados todavía, no querrás que ocupe espacio en la página. Usando :empty
puedes ocultarlo con:
.search-results:empty { display: none; }
Es posible que esté pensando en agregar un mensaje en estado vacío y tenga la tentación de agregarlo con un pseudoelemento y content
. El escollo aquí es que los mensajes pueden no estar disponibles para los usuarios de tecnología de asistencia que no son consistentes en cuanto a si pueden acceder al content
. En otras palabras, para asegurarse de que un tipo de mensaje "sin resultados" sea accesible , querrá agregarlo como un elemento real como un párrafo (una aria-label
ya no sería accesible para un div oculto).
Recursos para aprender acerca de los selectores
CSS tiene muchos más selectores que incluyen pseudoclases. Aquí hay algunos lugares más para obtener más información sobre lo que está disponible:
- La documentación de los selectores CSS de MDN incluye una lista categorizada completa;
- He escrito una guía de dos partes para selectores de CSS avanzados, puede comenzar con la primera parte;
- Diviértete aprendiendo sobre selectores CSS con el juego CSS Diner;
- Kitty Giraudel creó una herramienta de explicación del selector que desglosará y describirá partes de un selector proporcionado.