
Uso de detección de funciones, condicionales y grupos con selectores

CSS está diseñado de una manera que permite la adición relativamente fluida de nuevas funciones. Desde los bordes del lenguaje, las especificaciones han requerido que los navegadores ignoren elegantemente cualquier propiedad, valor, selector o regla que no admitan. En consecuencia, en la mayoría de los casos, es posible utilizar con éxito una tecnología más nueva sin causar ningún problema en los navegadores más antiguos.
Considere la propiedad relativamente nueva caret-color
(cambia el color del cursor en las entradas). Su soporte sigue estando bajo pero eso no significa que no debamos utilizarlo hoy en día.
.myInput { color: blue; caret-color: red;}
Observa cómo lo colocamos justo al lado de color
, una propiedad con soporte de navegador prácticamente universal; uno que se aplicará en todas las partes. En este caso, no hemos discriminado limpiamente entre navegadores modernos y antiguos. A cambio, simplemente confiamos en que los más antiguos ignoran las funciones que no admiten.
Resulta que este patrón es lo suficientemente poderoso en la gran mayoría de situaciones.
Cuando es necesaria la detección de características
En algunos casos, sin embargo, nos gustaría mucho utilizar una propiedad moderna o un valor de propiedad cuyo uso difiera significativamente de su alternativa. En esos casos, @supports
viene al rescate.
@supports
es una regla especial que nos permite aplicar condicionalmente cualquier estilo en los navegadores que admitan una propiedad particular y su valor.
@supports (display: grid) { /* Styles for browsers that support grid layout... */}
Funciona de manera análoga a @media
las consultas, que además solo aplican estilos de forma condicional cuando se cumple un determinado predicado.
Para ilustrar el uso de @supports
, considere el siguiente ejemplo: nos gustaría mostrar un avatar subido por un usuario en un bonito círculo, pero no podemos garantizar que el archivo real tenga dimensiones cuadradas. Para eso, la object-fit
propiedad sería de gran ayuda; Sin embargo, Internet Explorer (IE) no lo admite. ¿Qué hacemos entonces?
Comenzamos con el marcado:
div img src="..." alt="..." //div
Como alternativa no tan bonita, reduciremos el ancho de la imagen dentro del avatar a costa de que los archivos más anchos no cubran completamente el área del avatar. En cambio, nuestro fondo de un solo color aparecerá debajo.
.avatar { position: relative; width: 5em; height: 5em; border-radius: 50%; overflow: hidden; background: #cccccc; /* Fallback color */}.avatar-image { position: absolute; top: 50%; right: 0; bottom: 0; left: 50%; transform: translate(-50%, -50%); max-width: 100%;}
Puedes ver este comportamiento en acción aquí:
Observa que hay una imagen cuadrada, una ancha y otra alta.
Ahora, si usamos object-fit
, podemos dejar que el navegador decida la mejor manera de posicionar la imagen, es decir, si estirar el ancho, el alto o ninguno de los dos.
@supports (object-fit: cover) { .avatar-image { /* We no longer need absolute positioning or any transforms */ position: static; transform: none; object-fit: cover; width: 100%; height: 100%; }}
El resultado, para el mismo conjunto de dimensiones de imagen, funciona muy bien en los navegadores modernos:
Soporte de selector condicional
Aunque la especificación Selectores Nivel 4 todavía es un Borrador de Trabajo, algunos de los selectores que definen, como por ejemplo :placeholder-shown
, ya son compatibles con muchos navegadores. Si esta tendencia continúa (y si el borrador conserva la mayoría de sus propuestas actuales), este nivel de especificación introducirá más selectores nuevos que cualquiera de sus predecesores. Mientras tanto, y también mientras IE sigue vivo, los desarrolladores de CSS tendrán que apuntar a un espectro aún más diverso y volátil de navegadores con soporte incipiente para estos selectores.
Será muy útil para realizar la detección de funciones en los selectores. Desafortunadamente, @supports
sólo está diseñado para probar el soporte de propiedades y sus valores, e incluso el borrador más reciente de su especificación no parece cambiar eso. Sin embargo, desde sus inicios, ha definido una regla de producción especial en su gramática cuyo único propósito es proporcionar espacio para posibles extensiones compatibles con versiones anteriores y, por lo tanto, es perfectamente factible que una versión futura agregue la capacidad de condicionar el soporte. . . para selectores particulares. Sin embargo, esa eventualidad sigue siendo totalmente hipotética.
Contraparte selectora de @supports
En primer lugar, es importante enfatizar que, de manera análoga al caret-color
ejemplo mencionado anteriormente donde @supports
probablemente no sea necesario, muchos selectores no necesitan ser probados claramente para ninguno de los dos. Por ejemplo, podríamos simplemente intentar hacer Amarillento ::selection
y no preocuparnos por los navegadores que no lo admiten, ya que no será el fin del mundo si la apariencia de selección sigue siendo la predeterminada del navegador.
Sin embargo, hay casos en los que sería muy deseable la detección específica de características para los selectores. En el resto de este artículo, presentaremos un patrón para abordar dichas necesidades y posteriormente lo usaremos para :placeholder-shown
crear una alternativa solo CSS al campo de texto Material Design con una etiqueta flotante.
Grupos de propiedades fundamentales de selectores.
Para evitar duplicaciones, es posible condensar varias declaraciones idénticas en una lista de selectores separada por comas, a la que se hace referencia como grupo de selectores.
Así podemos girar:
.foo { color: red }.bar { color: red }
…es:
.foo, .bar { color: red }
Sin embargo, como advierte la especificación de Selectores de nivel 3, estos solo son equivalentes porque todos los selectores involucrados son válidos. Según la especificación, si alguno de los selectores del grupo no es válido, se ignora todo el grupo. En consecuencia, los selectores:
..foo { color: red } /* Note the extra dot */.bar { color: red }
…no se pudo agrupar de forma segura, ya que el selector anterior no es válido. Si los agrupamos, provocaríamos que el navegador también ignorara la declaración de este último.
Vale la pena señalar que, en lo que respeta a un navegador, no hay diferencia entre un selector no válido y un selector que solo es válido según una versión más reciente de la especificación o uno que el navegador no conoce. Para el navegador, ambos son simplemente inválidos.
Podemos aprovechar esta propiedad para probar la compatibilidad con un selector en particular. Todo lo que necesitamos es un selector que no podemos garantizar que no coincide con nada. En nuestros ejemplos, usaremos :not(*)
.
.foo { color: red }:not(*):placeholder-shown,.foo { color: green}
Analizamos lo que sucede aquí. Un navegador más antiguo aplicó correctamente la primera regla, pero al procesar el resto, encontrará que el primer selector del grupo no es válido porque no lo conoce :placeholder-shown
, y por lo tanto ignorará todo el grupo de selectores. En consecuencia, todos los elementos que coincidirán .foo
permanecerán en red
. Por el contrario, mientras un navegador más nuevo probablemente pondrá los ojos en blanco al buscar :not(*)
(que nunca coincidirá con nada), no descartará todo el grupo de selectores. En cambio, se anulará la regla anterior y, por lo tanto, todos los elementos que coincidirán .foo
serán green
.
Observe la similitud @supports
(o cualquier @media
consulta, de hecho) en términos de cómo se usa. Primero especificamos el respaldo y luego lo anulamos para los navegadores que satisfacen un predicado, que en este caso es el soporte para un selector particular, aunque escrito de una manera algo complicada.
Ejemplo del mundo real
Podemos usar esta técnica para nuestra entrada con una etiqueta flotante para separar los navegadores que lo hacen de los que no lo admiten :placeholder-shown
, una pseudoclase que es absolutamente vital para este ejemplo. En cuanto a una relativa simplicidad, a pesar de las mejores prácticas de interfaz de usuario, elegiremos nuestro recurso alternativo para que sea solo el marcador de posición real.
Comenzamos con el marcado:
div input type="email" name="email" placeholder="Email" required / label for="email"Email/label/div
Como antes, la clave es agregar primeros estilos para navegadores más antiguos. Ocultamos la etiqueta y configuramos el color del marcador de posición.
.input { height: 3.2em; position: relative; display: flex; align-items: center; font-size: 1em;}.input-control { flex: 1; z-index: 2; /* So that it is always "above" the label */ border: none; padding: 0 0 0 1em; background: transparent; position: relative;}.input-label { position: absolute; top: 50%; right: 0; bottom: 0; left: 1em; /* Align this with the control's padding */ z-index: 1; display: none; /* Hide this for old browsers */ transform-origin: top left; text-align: left;}
Para los navegadores modernos, podemos desactivar efectivamente el marcador de posición configurándolo color
en transparent
. También podemos alinear el input
y el label
relativo entre sí para cuando se muestre el marcador de posición. Con eso fin, también podemos utilizar el selector de hermanos para diseñarlo label
con respecto al estado del archivo input
.
.input-control:placeholder-shown::placeholder { color: transparent;}.input-control:placeholder-shown ~ .input-label { transform: translateY(-50%)}.input-control:placeholder-shown { transform: translateY(0);}
¡Por fin el truco! Exactamente como arriba, anulamos los estilos para label
y input
para los navegadores modernos y el estado en el que no se muestra el marcador de posición. Eso implica apartarlo label
y reducirlo un poco.
:not(*):placeholder-shown,.input-label { display: block; transform: translateY(-70%) scale(.7);}:not(*):placeholder-shown,.input-control { transform: translateY(35%);}
Con todas las piezas juntas, así como más estilos y opciones de configuración ortogonales a este ejemplo, puedes ver la demostración completa:
Fiabilidad y limitaciones de esta técnica.
Básicamente, esta técnica requiere un selector que no coincida con nada. Para ello, hemos estado utilizando :not(*)
; Sin embargo, su apoyo también es limitado. El selector universal *
es compatible incluso con IE 7, mientras que la :not
pseudoclase sólo se ha implementado desde IE 9, que es, por tanto, el navegador más antiguo en el que funciona este enfoque. Los navegadores más antiguos rechazarían nuestros grupos de selección por el motivo equivocado: ¡no son compatibles :not
! Alternativamente, podríamos usar un selector de clase como .foo
o un selector de tipo como foo
, soportando así incluso los navegadores más antiguos. Sin embargo, estos hacen que el código sea menos legible ya que no transmiten que nunca deben coincidir con nada y, por lo tanto, para la mayoría de los sitios modernos, :not(*)
parece la mejor opción.
En cuanto a si la propiedad de los grupos de selectores que hemos estado aprovechando también se mantiene en los navegadores más antiguos, el comportamiento se ilustra en un ejemplo como parte de la sección CSS 1 sobre análisis compatible con versiones posteriores. Además, la especificación CSS 2.1 exige claramente este comportamiento. Para poner en perspectiva la antigüedad de esta especificación, esto es lo que se introdujo :hover
. En resumen, si bien esta técnica no se ha probado exhaustivamente en los navegadores más antiguos u oscuros, su soporte debería ser extremadamente amplio.
Por último, hay una pequeña salvación para los usuarios de Sass (Sass, no SCSS): al encontrar el :not(*):placeholder-shown
selector, el compilador se deja engañar por los dos puntos iniciales, intenta analizarlo como una propiedad y, cuando encuentra el error, le aconseja al usuario que lo haga. desarrollador que escapa del selector de la siguiente manera: :not(*):placeholder-shown
, lo que no parece muy agradable. Una mejor solución alternativa es quizás reemplazar la barra invertida con otro selector universal para obtener *:not(*):placeholder-shown
, ya que, según la especificación, está implícito de todos los modos en este caso.
Deja una respuesta