Comprobar selectores y formatos de fuente
La función selector() para preguntar por sintaxis de selectores, font-tech() y font-format() para preguntar por archivos de fuente, y qué prometen exactamente esas respuestas.
Una condición de soporte con una declaración dentro solo sabe hablar de propiedades y valores. Pero la mitad de lo que llega nuevo a CSS no es una propiedad: son selectores, pseudo-clases, pseudo-elementos y capacidades del cargador de fuentes. Para esos, la gramática de @supports tiene funciones específicas, y cada una promete algo distinto de lo que la gente asume.
- Escribir condiciones con
selector()para pseudo-clases y pseudo-elementos. - Explicar qué garantiza y qué no garantiza un positivo de
selector(). - Detectar formatos y tecnologías de fuente con
font-format()yfont-tech(). - Combinar varias funciones de condición en una sola regla.
selector() y la pregunta que hace
@supports selector(:has(a)) {
.tarjeta:has(a) { border-color: var(--acento); }
}
La función recibe un selector completo y responde si el navegador sabe analizarlo. Igual que con las declaraciones, es una pregunta sobre la gramática, no sobre el motor de emparejamiento.
Aquí hay un detalle de la especificación que la hace más fiable de lo que parece: un selector que contiene una parte desconocida es inválido entero, y un navegador debe descartar toda la regla que lo use. Por eso un motor que no conoce :has() no puede aplicar a medias una regla que lo contenga, y por eso selector() es una detección honesta en la práctica: si el selector no se analiza, la regla no habría hecho nada de todas formas.
El soporte de la propia función es antiguo: Chrome 83, Firefox 69 y Safari 14.1. Es sintaxis segura desde hace años, lo cual importa porque una función de condición que no se entiende cae también en general enclosed y evalúa a falso; si selector() fuera reciente, detectar con ella daría negativos falsos en navegadores capaces.
Los casos donde de verdad se usa:
/* pseudo-clases relacionales y de estado */
@supports selector(:has(*)) { }
@supports selector(:focus-visible) { }
@supports selector(:user-invalid) { }
/* pseudo-elementos */
@supports selector(::backdrop) { }
@supports selector(::marker) { }
/* combinadores y sintaxis de anidamiento */
@supports selector(& > *) { }
Fíjate en :has(*) en lugar de :has(a): cuando lo único que quieres saber es si la pseudo-clase existe, el argumento más simple posible evita que un selector interno raro contamine la respuesta.
Lo que un positivo no promete
Tres límites reales.
No promete rendimiento. :has() se analiza igual en todos los motores que lo implementan, pero el coste de evaluarlo depende del selector concreto y del árbol. Una condición verdadera no te dice si tu :has() va a invalidar medio documento en cada cambio de clase.
No promete el mismo alcance. Un pseudo-elemento puede analizarse en un motor y estar limitado a ciertos elementos o a ciertos contextos. selector(::backdrop) es verdadero mucho antes de que todas las propiedades sean estilables ahí.
No promete prefijos. selector(::-webkit-scrollbar) da verdadero en Chromium y falso en el resto, que es correcto, pero mucha gente lo usa esperando una detección de “puedo estilar la barra de scroll” que no es lo mismo, porque el modelo estándar de la barra usa otras propiedades.
Toda la gramática de @supports está diseñada para que lo desconocido evalúe a falso sin romper nada. Eso significa que una condición escrita con una función que tu navegador objetivo no implementa siempre te manda al camino de fallback, sin ningún aviso. Es el modo de fallo correcto para una at-rule condicional, y a la vez la razón por la que detectar con sintaxis reciente es peligroso: no distingues “no soporta la característica” de “no soporta la pregunta”.
Fuentes: font-format() y font-tech()
Las otras dos funciones de condición preguntan por el cargador de fuentes. Están en los tres motores desde hace tiempo: Chrome 108, Firefox 106 y Safari 17.
font-format() responde si el navegador sabe decodificar un formato de archivo.
@supports font-format(woff2) {
/* practicamente siempre verdadero en 2026 */
}
Los valores son palabras clave sin comillas: collection, embedded-opentype, opentype, svg, truetype, woff, woff2.
font-tech() responde si el navegador soporta una tecnología concreta dentro del archivo, que es la pregunta interesante.
@supports font-tech(variations) {
.titulo { font-variation-settings: 'GRAD' 120; }
}
@supports font-tech(color-COLRv1) {
.emoji-marca { font-family: 'MarcaColor'; }
}
Las tecnologías incluyen features-opentype, features-aat, features-graphite, variations, palettes, incremental y la familia de color color-COLRv0, color-COLRv1, color-SVG, color-sbix, color-CBDT. Esa familia es donde hay diferencias reales entre motores: COLRv1 está en Chromium y Firefox y no en Safari, y los formatos de bitmap de color están repartidos de forma desigual entre Chromium y Safari.
Estas mismas palabras clave aparecen en el descriptor src con la función tech(), y ahí resuelven el mismo problema sin necesidad de @supports, porque el navegador elige la primera fuente de la lista que sabe usar:
@font-face {
font-family: 'Marca';
src: url('marca-color.woff2') format('woff2') tech(color-COLRv1),
url('marca-plana.woff2') format('woff2');
}
Esa es casi siempre la forma correcta: la lista ordenada de src es la detección, y no necesitas una at-rule condicional para nada. @supports font-tech() queda para cuando lo que cambia no es el archivo sino otras propiedades del diseño.
Combinar funciones
Las tres formas de condición conviven en la misma gramática, así que se combinan con los mismos operadores que una declaración normal:
@supports selector(:has(*)) and (container-type: inline-size) {
.panel:has(> .destacado) { container-type: inline-size; }
}
@supports (font-variation-settings: 'wght' 400) and font-tech(variations) {
.cuerpo { font-weight: 380; }
}
La segunda es un buen ejemplo de detección redundante bien hecha: la primera parte comprueba que el motor analiza la propiedad, la segunda que sabe usar el eje del archivo. Son dos capacidades distintas y en teoría podrían no ir juntas.
Todas las funciones de condición de @supports comparten la misma limitación y conviene verla como una sola idea en lugar de como tres notas al pie: el navegador solo puede responder por su analizador porque es lo único que puede evaluar sin ejecutar nada. Contestar “¿implementas bien :has()?” exigiría un test de comportamiento, es decir, construir un árbol, aplicar el selector y comparar el resultado con lo esperado; eso es un test unitario, no una condición declarativa, y ningún lenguaje de hojas de estilo va a incorporarlo jamás. La consecuencia práctica es que existe una clase entera de bugs que @supports no puede protegerte de, y esa clase es precisamente la que te va a morder: no los navegadores que no tienen la característica, que son fáciles y degradan solos, sino los que la tienen a medias. La disciplina que sí funciona es no confiar en la condición como si fuera una garantía sino tratarla como lo que es —un filtro barato que elimina el caso trivial— y complementarla con lo único que detecta comportamiento de verdad, que es abrir el navegador en cuestión y mirar. Si tu estrategia de soporte cabe entera en @supports, no tienes una estrategia de soporte: tienes una condición.