1. Pregunta y contexto
Una página de administración renderizada en el servidor tiene varios componentes que usan clases genéricas .title y .button. Una nueva tarjeta de marketing cambia el espaciado de los botones y los colores de los encabezados en páginas antiguas. Diseña una migración gradual con @scope preservando el first paint renderizado en el servidor, las variables de tema y el soporte para navegadores más antiguos. Los componentes permanecen en un único DOM de documento y no utilizan Shadow DOM.
2. Qué evalúa el entrevistador
- Entender que
@scopelimita la coincidencia de selectores pero no crea aislamiento de Shadow DOM. - Explicar scoping roots, scope limits opcionales, ámbitos anidados y el orden de la cascada en lugar de memorizar la sintaxis.
- Separar el estado de los componentes, las variables de tema, los resets globales y los estilos de terceros.
- Diseñar la detección de capacidades, el comportamiento de fallback, la regresión visual y las métricas de migración.
3. Preguntas para aclarar primero
- ¿Qué navegadores deben funcionar y un navegador no compatible debe ser idéntico a nivel de píxel o usar una ruta CSS heredada?
- ¿Los componentes comparten el DOM y heredan variables de tema, o necesitan un aislamiento real de DOM y eventos?
- ¿Los estilos se cargan por página, por componente o mediante paquetes de terceros, y durante cuánto tiempo coexistirán las reglas antiguas y las nuevas?
- ¿Debe un ámbito cubrir a todos los descendientes de la raíz o detenerse en un subárbol anidado?
4. Una respuesta de 30 segundos
Trataría @scope como un control de rango de selectores, no como Shadow DOM. Daría a cada componente una clase raíz estable y mantendría sus reglas dentro de ese ámbito; usaría to cuando la coincidencia deba detenerse en un límite anidado. Mantendría las variables de tema en una raíz explícita de página o componente y expresaría el estado con clases o atributos locales. Los navegadores compatibles usan reglas con ámbito; los no compatibles usan una compilación con espacios de nombres a partir de las mismas reglas semánticas. Migraría un componente a la vez con capas de cascada, regresión visual y métricas de acierto de reglas para que los selectores antiguos y nuevos no compitan silenciosamente.
5. Respuesta paso a paso
Paso 1: Separar el objetivo de aislamiento
Determina si el problema es una colisión de selectores, contaminación por herencia o un límite de seguridad de DOM/eventos. @scope aborda el alcance de los selectores al tiempo que permite la herencia de variables y el acceso mediante scripts a los mismos nodos del documento. Elige Shadow DOM cuando el DOM, los estilos y los eventos deban aislarse; elige CSS Modules cuando el objetivo principal sean nombres de clases modulares en tiempo de compilación.
Paso 2: Definir la raíz del ámbito y el límite
Usa la raíz del componente como la raíz del ámbito y mantén legibles los selectores internos:
@scope (.profile-card) {
.title { color: var(--card-title); }
.button { padding-inline: 0.75rem; }
}Para excluir un editor de terceros anidado dentro de .profile-card, usa @scope (.profile-card) to (.editor) { ... }. El límite solo indica dónde se detiene la coincidencia; no clona nodos ni bloquea la herencia de propiedades personalizadas. Documenta las raíces y los límites para ámbitos anidados de modo que un elemento no pertenezca accidentalmente a varios componentes.
Paso 3: Manejar la cascada, el estado y los temas
El ámbito no reemplaza la cascada. Coloca los resets, los valores predeterminados de los componentes y las sobreescrituras en @layer explícitos; usa :where() cuando una regla deba seguir siendo fácil de sobreescribir en lugar de aumentar la especificidad del selector. Expresa el estado en la raíz del componente con [data-state] o clases locales y asigna esas reglas a una capa definida. Las variables de tema pueden provenir de la raíz de la página o sobreescribirse en la raíz del componente, pero las variables internas no deben convertirse accidentalmente en un contrato global.
Paso 4: Diseñar la compatibilidad y la migración
Usa @supports selector(:scope) o una matriz de navegadores objetivo para elegir una ruta, y luego verifica los navegadores objetivo reales. Una compilación heredada puede expandir las mismas reglas a selectores con espacios de nombres como .profile-card .title; ambas rutas deben provenir de un único conjunto de reglas de origen. Migra componente por componente, compara capturas de pantalla mientras coexisten las reglas antiguas y nuevas, y elimina el selector global antiguo solo después de que la nueva ruta sea observable. No envuelvas toda la aplicación en un único ámbito gigante.
Paso 5: Verificar límites y reversión
Prueba clases con el mismo nombre en componentes anidados, la propia raíz del ámbito, elementos fuera de un límite to, cambios de tema, estados combinados, nodos insertados dinámicamente y subárboles de terceros. Rastrea la cobertura de reglas con ámbito, la distribución de capacidades del navegador, las diferencias visuales y las reglas no coincidentes en la salida de compilación. Si aumentan las regresiones, revierte el componente o deshabilita la ruta mejorada; no ocultes el error de límite agregando más especificidad.
6. Respuesta modelo
Primero decidiría si necesitamos límites de selectores o un aislamiento completo del DOM. Para un DOM de documento compartido,@scope (.profile-card)mantiene locales los selectores internos ytopuede detener la coincidencia en un editor anidado; no aísla eventos ni evita la herencia de propiedades personalizadas como Shadow DOM. Ubicaría el reset, los valores predeterminados y las sobreescrituras de estado en capas de cascada explícitas y definiría las variables de tema solo en las raíces de página o de componente. Los navegadores no compatibles usarían una compilación con espacios de nombres a partir de las mismas reglas. La migración sería componente por componente con diferencias de capturas de pantalla, distribución de capacidades y métricas de reglas no coincidentes. Una regresión de límites revierte ese componente en lugar de escalar la especificidad del selector.
7. Errores comunes
- Tratar
@scopecomo Shadow DOM: El DOM, los eventos y las variables siguen compartiéndose; elige la primitiva que se ajuste al objetivo de aislamiento. - Cambiar selectores pero ignorar las capas: Una regla antigua aún puede ganar en una capa superior; define las capas y la precedencia de estados en conjunto.
- Envolver toda la aplicación en un solo ámbito: Los límites de componentes ganan poco y la reversión se vuelve difícil; divide por raíz de componente.
- Permitir que los navegadores antiguos ignoren la regla: Los estilos críticos desaparecen; envía una ruta heredada con espacios de nombres y prueba una matriz de capacidades.
- Corregir regresiones con mayor especificidad: El acoplamiento y el costo de sobreescritura aumentan; remodela la raíz, el límite del ámbito y la capa de cascada.
8. Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Puede @scope evitar que un componente hijo herede variables de tema?
No. Limita principalmente la coincidencia de selectores; las propiedades personalizadas aún siguen la herencia de CSS. Sobreescribe las variables en la raíz del hijo o usa Shadow DOM cuando se requiera un entorno de variables separado.
Pregunta de seguimiento 2: ¿Qué gana cuando ámbitos anidados coinciden con el mismo elemento?
Siguen aplicando la cascada normal y el ordenamiento relativo al ámbito; "el ámbito interno siempre gana" no es una regla segura. Coloca los ámbitos en capas definidas e inspecciona una reproducción mínima en las DevTools del navegador.
Pregunta de seguimiento 3: ¿Cómo se mantienen consistentes los navegadores no compatibles?
Genera selectores con espacios de nombres o clases estables de CSS Modules a partir de las mismas reglas de origen, preservando variables, estado y semántica de capas. La detección en tiempo de ejecución selecciona una ruta de implementación; los componentes de negocio no deben mantener dos máquinas de estado.