Soporte real de @scope y estrategia en 2026
Cuándo llegó a cada motor, por qué su modo de fallo es peor que el de casi cualquier otra novedad de CSS, cómo detectarlo de verdad y qué poner dentro del bloque.
@scope es de las pocas características modernas de CSS donde la pregunta del soporte no se despacha con un “ya está en los cuatro motores”. Está, sí, pero llegó al último de ellos hace muy poco, y sobre todo falla de una manera que no se parece a como falla el resto del lenguaje: cuando un motor no lo entiende, no pierdes una declaración, pierdes el bloque entero. Esa asimetría es la que debe gobernar tu estrategia de adopción, no la tabla de compatibilidad.
- Situar con precisión cuándo llegó
@scopea cada motor y qué implica en 2026. - Explicar por qué el modo de fallo de una regla
@scopees peor que el de una propiedad nueva. - Detectar el soporte con un método que funcione en los cuatro motores.
- Decidir qué contenido merece la pena meter dentro de un
@scopey cuál no.
Dónde está el soporte de verdad
Chrome lo envió en la versión 118, en octubre de 2023. Safari lo siguió en 17.4, en marzo de 2024. Y Firefox lo implementó en la versión 146, a finales de 2025, cerrando el ciclo con casi dos años de retraso sobre el primero.
Eso lo coloca, en agosto de 2026, en la categoría de disponible pero reciente: está en los cuatro motores por defecto, y aun así la base instalada no se ha renovado del todo. El caso que más importa en la práctica es Firefox ESR, la línea de soporte extendido que usan administraciones públicas, universidades y muchos entornos corporativos: va deliberadamente por detrás de la versión normal, y una organización que esté en la ESR anterior a la 146 no tiene @scope aunque su navegador sea perfectamente actual desde el punto de vista de seguridad.
Compáralo con lo que ya has usado en niveles anteriores. :has() cerró el círculo en diciembre de 2023 y lleva casi tres años; el anidamiento nativo, otro tanto. @scope es de otra generación, y merece otra cautela.
Cómo se degrada cuando no está
Esta es la parte que la tabla de compatibilidad no cuenta, y es la que decide todo lo demás.
Cuando un motor encuentra una propiedad que no conoce, descarta esa declaración y conserva el resto de la regla. Es el mecanismo que hace que la mejora progresiva sea barata: escribes el valor de reserva, escribes el moderno debajo, y cada navegador se queda con el último que entiende.
.card {
background: #333; /* todos */
background: color-mix(in oklch, black 80%, blue); /* los que sepan */
}
Cuando un motor encuentra una regla arroba que no conoce, descarta el bloque completo, con todo lo que haya dentro. No hay grados. Un @scope con ochenta declaraciones se convierte en cero declaraciones.
/* En un motor sin @scope, aqui no se aplica absolutamente nada. */
@scope (.prosa) to (.no-prosa) {
:scope { max-inline-size: 68ch; }
p { line-height: 1.7; margin-block: 1em; }
a { text-decoration: underline; }
ul { padding-inline-start: 1.5em; }
img { max-inline-size: 100%; }
}
El resultado no es “el artículo se ve un poco peor”: es “el artículo no tiene estilos”. Y el fallo es silencioso, porque el analizador está haciendo exactamente lo que manda la especificación al encontrar una regla que no reconoce.
La tentación al adoptar @scope es mover el componente entero dentro del bloque, porque queda ordenado y porque la especificidad cero es atractiva. Es justo lo contrario de lo que conviene. Todo lo que pongas ahí desaparece por completo donde no haya soporte, así que el bloque debe contener solo lo que de verdad necesita el ámbito, y el estilo base debe vivir fuera.
Detectarlo
La forma limpia sería @supports at-rule(@scope), que es parte de CSS Conditional 5 y hace exactamente la pregunta correcta. El problema es que la propia función at-rule() es más nueva que @scope: solo la implementa Chromium, y en una versión muy reciente. Preguntar con ella en 2026 significa recibir un “no” en Firefox y Safari aunque ambos soporten @scope perfectamente, que es el peor error posible.
La detección que sí funciona en los cuatro motores se hace desde JavaScript, aprovechando que insertRule lanza una excepción cuando no puede analizar la regla:
function soportaScope() {
try {
const hoja = new CSSStyleSheet();
hoja.insertRule('@scope (.a) to (.b) { .c { color: red } }');
return hoja.cssRules.length === 1;
} catch {
return false;
}
}
document.documentElement.classList.toggle('sin-scope', !soportaScope());
Con esa clase en la raíz puedes escribir el plan B en CSS normal. Es una detección en tiempo de ejecución, con el coste que eso implica —el CSS de reserva llega después del primer pintado si no lo previenes—, así que conviene usarla para ajustes y no para el layout principal.
Sobre transpilar: la raíz y el límite se pueden reescribir a selectores planos con bastante fidelidad, así que en teoría una herramienta de compilación podría emitir una versión de reserva. Lo que no se puede emular es la proximidad, porque es un criterio de la cascada que depende del árbol y no del selector. Cualquier transpilación es, por construcción, una aproximación que falla precisamente en el caso de los temas anidados, que es el motivo principal para querer @scope.
Una estrategia por casos
No todos los usos de @scope tienen el mismo perfil de riesgo. Ordénalos por lo que se pierde sin soporte.
El donut sobre contenido. Riesgo alto si metes dentro el estilo base de la prosa, riesgo bajo si dentro solo va lo que el límite protege. La versión segura es escribir las reglas de prosa fuera, con selectores normales, y usar el @scope únicamente para las excepciones que el donut resuelve mejor que un :not().
/* Fuera: funciona en todas partes. */
.prosa p { line-height: 1.7; }
.prosa a { text-decoration: underline; }
.prosa img { max-inline-size: 100%; }
/* Dentro: solo lo que necesita el suelo, y con un fallback aceptable. */
.prosa .no-prosa p { line-height: normal; }
@scope (.prosa) to (.no-prosa) {
p { text-wrap: pretty; }
}
Los temas anidados. No hay reserva equivalente, así que la pregunta es qué aspecto tiene el fallo. Sin @scope, el orden de aparición decide y el caso anidado sale mal; con él, sale bien. Si tu producto anida temas de verdad y no es aceptable que se vea mal en un navegador antiguo, @scope todavía no es la respuesta y toca resolverlo con clases explícitas. Si el anidamiento es infrecuente, es un candidato perfecto: mejora donde puede y no rompe donde no.
La especificidad cero de las librerías. El más peligroso, porque el atractivo es grande y el fallo es total. Publicar un sistema de diseño cuyas reglas viven dentro de @scope significa que en un navegador sin soporte tu librería no aplica nada. Hasta que la base instalada se renueve, la respuesta correcta para ese objetivo sigue siendo :where() y @layer, que están disponibles desde hace años y consiguen lo mismo en el noventa por ciento de los casos.
Los entornos controlados. Una aplicación interna, un panel de administración, un contenedor embebido con versión fijada. Ahí la tabla de compatibilidad no aplica y puedes usar @scope sin reservas desde hoy.
Hay una asimetría entre las dos formas de novedad en CSS que cambia por completo la aritmética de la mejora progresiva, y que casi nadie enuncia porque durante quince años solo hicimos la mejora progresiva de una de las dos. Con una propiedad nueva, el riesgo de adoptarla es proporcional a lo mucho que dependas de ella en una declaración concreta, y como el analizador descarta declaración a declaración, el coste de equivocarse está acotado a esa línea: cuanto más código escribas alrededor, más código sobrevive. Con una regla arroba nueva, la unidad de descarte es el bloque, así que el riesgo es proporcional a cuánto has metido dentro, y crece con el tamaño del bloque. Es una inversión completa del incentivo: en el primer caso la adopción es aditiva y en el segundo es apostar. Esto tiene una consecuencia operativa muy concreta que conviene convertir en regla del equipo: dentro de un @scope va exclusivamente lo que no se puede expresar fuera, es decir, lo que necesita el suelo del donut o lo que necesita la proximidad. Todo lo demás —el estilo base, la tipografía, los colores, el layout— se escribe fuera con selectores normales, aunque duela la duplicación aparente del selector raíz. Y el mismo razonamiento se aplica en su día a @container, se aplicará a @scope durante otro año y se aplicará a lo siguiente que llegue como regla arroba: la pregunta al adoptar no es solo si el motor la entiende, sino cuánto se lleva por delante cuando no la entiende. Es la diferencia entre una degradación y un apagón.
- Coge un
@scopereal y cuenta cuántas declaraciones desaparecen si el motor no lo soporta. - Reescríbelo para que solo quede dentro lo imprescindible y vuelve a contar.
- Implementa la detección con
insertRuley comprueba que devuelve lo correcto en tu navegador. - Explica por qué
@supports at-rule(@scope)es hoy una detección peor que ninguna. - Clasifica los usos de
@scopede un proyecto tuyo en los cuatro casos de riesgo.