Aislamiento de CSS de Blazor de ASP.NET Core
De Dave Brock
Aísle los estilos CSS a páginas, vistas y componentes individuales para reducir o evitar lo siguiente:
- Dependencias de estilos globales que pueden ser difíciles de mantener.
- Conflictos de estilo en el contenido anidado.
Habilitación del aislamiento de CSS
Para definir estilos específicos de un componente, cree un archivo .razor.css cuyo nombre coincida con el del archivo .razor del componente en la misma carpeta. El archivo .razor.css es un archivo CSS con ámbito.
En un componente Example de un archivo Example.razor, cree un archivo junto al componente denominado Example.razor.css. El archivo Example.razor.css debe residir en la misma carpeta que el componente Example (Example.razor). El nombre base "Example" del archivo no distingue mayúsculas de minúsculas.
Pages/Example.razor:
@page "/example"
<h1>Scoped CSS Example</h1>
Pages/Example.razor.css:
h1 {
color: brown;
font-family: Tahoma, Geneva, Verdana, sans-serif;
}
Los estilos definidos en Example.razor.css solo se aplican a la salida representada del componente Example . El aislamiento de CSS se aplica a los elementos HTML en el archivo Razor coincidente. Cualquier declaración CSS h1 definida en otra ubicación de la aplicación no entra en conflicto con los estilos del componente Example.
Nota
Para garantizar el aislamiento de estilo en el momento de la unión, no se admite la importación de CSS en bloques de código Razor.
Unión del aislamiento de CSS
El aislamiento de CSS se produce en tiempo de compilación. Durante este proceso, Blazor reescribe los selectores de CSS para que coincidan con el marcado representado por el componente. Estos estilos de CSS reescritos se unen y se generan como un recurso estático. Se hace referencia a la hoja de estilos dentro de la etiqueta <head> de wwwroot/index.html (Blazor WebAssembly) o Pages/_Layout.cshtml (Blazor Server). El siguiente elemento <link> se agrega de forma predeterminada a una aplicación creada a partir de las plantillas de proyecto Blazor, donde el marcador de posición {ASSEMBLY NAME} es el nombre del ensamblado del proyecto:
<link href="{ASSEMBLY NAME}.styles.css" rel="stylesheet">
El ejemplo siguiente es de una aplicación Blazor WebAssembly Client hospedada. El nombre del ensamblado de la aplicación es BlazorSample.Client, y la plantilla de proyecto Blazor WebAssembly agrega <link> cuando el proyecto se crea con la opción Hospedado (opción -ho|--hosted usando la CLI de .NET o la casilla ASP.NET Core hospedado con Visual Studio):
<link href="BlazorSample.Client.styles.css" rel="stylesheet">
En el archivo unido, cada componente está asociado a un identificador de ámbito. Para cada componente con estilo, se anexa un atributo HTML con el formato b-{STRING}, donde el marcador de posición {STRING} es una cadena de diez caracteres generada por el marco. El identificador es único para cada aplicación. En el componente Counter representado, Blazor anexa un identificador de ámbito al elemento h1:
<h1 b-3xxtam6d07>
El archivo {ASSEMBLY NAME}.styles.css usa el identificador de ámbito para agrupar una declaración de estilo con su componente. En el ejemplo siguiente se proporciona el estilo del elemento <h1> anterior:
/* /Pages/Counter.razor.rz.scp.css */
h1[b-3xxtam6d07] {
color: brown;
}
En tiempo de compilación, se crea un conjunto del proyecto con la convención {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css, donde el marcador de posición {STATIC WEB ASSETS BASE PATH} es la ruta de acceso base a los recursos web estáticos.
Si se usan otros proyectos, como paquetes NuGet o bibliotecas de clases de Razor, el archivo unido:
- Hace referencia a los estilos mediante importaciones de CSS.
- No se publica como recurso web estático de la aplicación que consume los estilos.
Compatibilidad de componente secundario
De forma predeterminada, el aislamiento de CSS solo se aplica al componente que se asocia con el formato {COMPONENT NAME}.razor.css, donde el marcador de posición {COMPONENT NAME} suele ser el nombre del componente. Para aplicar cambios a un componente secundario, use el combinador ::deep con los elementos descendientes del archivo .razor.css del componente primario. El combinador ::deep selecciona los elementos que son descendientes del identificador de ámbito generado de un elemento.
En el ejemplo siguiente se muestra un componente primario denominado Parent con un componente secundario denominado Child.
Pages/Parent.razor:
@page "/parent"
<div>
<h1>Parent component</h1>
<Child />
</div>
Shared/Child.razor:
<h1>Child Component</h1>
Actualice la declaración h1 de Parent.razor.css con el combinador ::deep para indicar que la declaración de estilo de h1 debe aplicarse al componente primario y a sus elementos secundarios.
Pages/Parent.razor.css:
::deep h1 {
color: red;
}
El estilo de h1 ahora se aplica a los componentes Parent y Child sin necesidad de crear un archivo CSS con ámbito independiente para el componente secundario.
El combinador ::deep solo funciona con elementos descendientes. El marcado siguiente aplica los estilos de h1 a los componentes según lo esperado. El identificador de ámbito del componente primario se aplica al elemento div, por lo que se sabe que el explorador hereda los estilos del componente primario.
Pages/Parent.razor:
<div>
<h1>Parent</h1>
<Child />
</div>
Sin embargo, si se excluye el elemento div, se quita la relación descendiente. En el ejemplo siguiente, el estilo no se aplica al componente secundario.
Pages/Parent.razor:
<h1>Parent</h1>
<Child />
Compatibilidad del preprocesador de CSS
Los preprocesadores de CSS son útiles para mejorar el desarrollo de CSS mediante el uso de características como variables, anidamiento, módulos, mixins y herencia. Aunque el aislamiento de CSS no admite de forma nativa preprocesadores de CSS como Sass o Less, la integración de preprocesadores de CSS se realiza sin problemas siempre que se produzca la compilación del preprocesador antes de que Blazor reescriba los selectores de CSS durante el proceso de compilación. Con Visual Studio, por ejemplo, configure la compilación del preprocesador existente como una tarea Antes de la compilación en el Explorador del Ejecutor de tareas de Visual Studio.
Muchos paquetes NuGet de terceros, como Delegate.SassBuilder, pueden compilar archivos SASS/SCSS al principio del proceso de compilación antes de que se produzca el aislamiento de CSS y no requieren ninguna configuración adicional.
Configuración del aislamiento de CSS
El aislamiento de CSS está diseñado para poder trabajar de inmediato, pero proporciona configuración para algunos escenarios avanzados, por ejemplo cuando hay dependencias en herramientas o flujos de trabajo existentes.
Personalización del formato del identificador de ámbito
De forma predeterminada, los identificadores de ámbito usan el formato b-{STRING}, donde el marcador de posición {STRING} es una cadena de diez caracteres generada por el marco. Para personalizar el formato de un identificador de ámbito, actualice el archivo del proyecto a un patrón deseado:
<ItemGroup>
<None Update="Pages/Example.razor.css" CssScope="custom-scope-identifier" />
</ItemGroup>
En el ejemplo anterior, el CSS generado para Example.razor.css cambia su identificador de ámbito de b-{STRING} a custom-scope-identifier.
Use identificadores de ámbito para lograr la herencia con archivos CSS de ámbito. En el siguiente ejemplo del archivo del proyecto, un archivo BaseComponent.razor.css contiene estilos comunes en todos los componentes. Un archivo DerivedComponent.razor.css hereda estos estilos.
<ItemGroup>
<None Update="Pages/BaseComponent.razor.css" CssScope="custom-scope-identifier" />
<None Update="Pages/DerivedComponent.razor.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Use el operador comodín (*) para compartir los identificadores de ámbito en varios archivos:
<ItemGroup>
<None Update="Pages/*.razor.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Cambio de la ruta de acceso base de recursos web estáticos
El archivo scoped.styles.css se genera en la raíz de la aplicación. En el archivo del proyecto, use la propiedad StaticWebAssetBasePath para cambiar la ruta de acceso predeterminada. En el ejemplo siguiente se coloca el archivo scoped.styles.css, y el resto de los recursos de la aplicación, en la ruta de acceso _content:
<PropertyGroup>
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>
Deshabilitación de la unión automática
Para no usar el modo en que Blazor publica y carga archivos con ámbito en tiempo de ejecución, use la propiedad DisableScopedCssBundling. Al usar esta propiedad, otras herramientas o procesos son responsables de tomar los archivos CSS aislados del directorio obj y de publicarlos y cargarlos en tiempo de ejecución:
<PropertyGroup>
<DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>
Razor Compatibilidad con la biblioteca de clases (RCL)
Cuando una Razor biblioteca de clases (RCL) proporciona estilos aislados, el atributo href de la etiqueta <link> apunta a {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css, donde los marcadores de posición son los siguientes:
{STATIC WEB ASSET BASE PATH}: ruta de acceso base del recurso web estático.{PACKAGE ID}: el id. de paquete de la biblioteca. El id. de paquete tiene como valor predeterminado el nombre de ensamblado del proyecto si<PackageId>no se especifica en el archivo del proyecto.
En el ejemplo siguiente:
- La ruta de acceso base del recurso web estático es
_content/ClassLib. - El nombre del ensamblado de la biblioteca de clases es
ClassLib.
wwwroot/index.html (Blazor WebAssembly) o Pages/_Layout.cshtml (Blazor Server):
<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">
Para obtener más información sobre RCL, vea los siguientes artículos:
- Consumo de componentes Razor de ASP.NET Core de bibliotecas de clases Razor
- Interfaz de usuario reutilizable de Razor en bibliotecas de clases con ASP.NET Core
Recursos adicionales
El aislamiento de CSS simplifica la superficie de CSS de una aplicación al evitar dependencias en estilos globales y ayuda a evitar conflictos de estilo entre componentes y bibliotecas.
Habilitación del aislamiento de CSS
Para definir estilos específicos de un componente, cree un archivo .razor.css cuyo nombre coincida con el del archivo .razor del componente en la misma carpeta. El archivo .razor.css es un archivo CSS con ámbito.
En un componente Example de un archivo Example.razor, cree un archivo junto al componente denominado Example.razor.css. El archivo Example.razor.css debe residir en la misma carpeta que el componente Example (Example.razor). El nombre base "Example" del archivo no distingue mayúsculas de minúsculas.
Pages/Example.razor:
@page "/example"
<h1>Scoped CSS Example</h1>
Pages/Example.razor.css:
h1 {
color: brown;
font-family: Tahoma, Geneva, Verdana, sans-serif;
}
Los estilos definidos en Example.razor.css solo se aplican a la salida representada del componente Example . El aislamiento de CSS se aplica a los elementos HTML en el archivo Razor coincidente. Cualquier declaración CSS h1 definida en otra ubicación de la aplicación no entra en conflicto con los estilos del componente Example.
Nota
Para garantizar el aislamiento de estilo en el momento de la unión, no se admite la importación de CSS en bloques de código Razor.
Unión del aislamiento de CSS
El aislamiento de CSS se produce en tiempo de compilación. Durante este proceso, Blazor reescribe los selectores de CSS para que coincidan con el marcado representado por el componente. Estos estilos de CSS reescritos se unen y se generan como un recurso estático. Se hace referencia a la hoja de estilos dentro de la etiqueta <head> de wwwroot/index.html (Blazor WebAssembly) o Pages/_Host.cshtml (Blazor Server). El siguiente elemento <link> se agrega de forma predeterminada a una aplicación creada a partir de las plantillas de proyecto Blazor, donde el marcador de posición {ASSEMBLY NAME} es el nombre del ensamblado del proyecto:
<link href="{ASSEMBLY NAME}.styles.css" rel="stylesheet">
El ejemplo siguiente es de una aplicación Blazor WebAssembly Client hospedada. El nombre del ensamblado de la aplicación es BlazorSample.Client, y la plantilla de proyecto Blazor WebAssembly agrega <link> cuando el proyecto se crea con la opción Hospedado (opción -ho|--hosted usando la CLI de .NET o la casilla ASP.NET Core hospedado con Visual Studio):
<link href="BlazorSample.Client.styles.css" rel="stylesheet">
En el archivo unido, cada componente está asociado a un identificador de ámbito. En cada componente con estilo, se anexa un atributo HTML con el formato b-<10-character-string>. El identificador es único y diferente para cada aplicación. En el componente Counter representado, Blazor anexa un identificador de ámbito al elemento h1:
<h1 b-3xxtam6d07>
El archivo {ASSEMBLY NAME}.styles.css usa el identificador de ámbito para agrupar una declaración de estilo con su componente. En el ejemplo siguiente se proporciona el estilo del elemento <h1> anterior:
/* /Pages/Counter.razor.rz.scp.css */
h1[b-3xxtam6d07] {
color: brown;
}
En tiempo de compilación, se crea un conjunto del proyecto con la convención {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css, donde el marcador de posición {STATIC WEB ASSETS BASE PATH} es la ruta de acceso base a los recursos web estáticos.
Si se usan otros proyectos, como paquetes NuGet o bibliotecas de clases de Razor, el archivo unido:
- Hace referencia a los estilos mediante importaciones de CSS.
- No se publica como recurso web estático de la aplicación que consume los estilos.
Compatibilidad de componente secundario
De forma predeterminada, el aislamiento de CSS solo se aplica al componente que se asocia con el formato {COMPONENT NAME}.razor.css, donde el marcador de posición {COMPONENT NAME} suele ser el nombre del componente. Para aplicar cambios a un componente secundario, use el combinador ::deep con los elementos descendientes del archivo .razor.css del componente primario. El combinador ::deep selecciona los elementos que son descendientes del identificador de ámbito generado de un elemento.
En el ejemplo siguiente se muestra un componente primario denominado Parent con un componente secundario denominado Child.
Pages/Parent.razor:
@page "/parent"
<div>
<h1>Parent component</h1>
<Child />
</div>
Shared/Child.razor:
<h1>Child Component</h1>
Actualice la declaración h1 de Parent.razor.css con el combinador ::deep para indicar que la declaración de estilo de h1 debe aplicarse al componente primario y a sus elementos secundarios.
Pages/Parent.razor.css:
::deep h1 {
color: red;
}
El estilo de h1 ahora se aplica a los componentes Parent y Child sin necesidad de crear un archivo CSS con ámbito independiente para el componente secundario.
El combinador ::deep solo funciona con elementos descendientes. El marcado siguiente aplica los estilos de h1 a los componentes según lo esperado. El identificador de ámbito del componente primario se aplica al elemento div, por lo que se sabe que el explorador hereda los estilos del componente primario.
Pages/Parent.razor:
<div>
<h1>Parent</h1>
<Child />
</div>
Sin embargo, si se excluye el elemento div, se quita la relación descendiente. En el ejemplo siguiente, el estilo no se aplica al componente secundario.
Pages/Parent.razor:
<h1>Parent</h1>
<Child />
Compatibilidad del preprocesador de CSS
Los preprocesadores de CSS son útiles para mejorar el desarrollo de CSS mediante el uso de características como variables, anidamiento, módulos, mixins y herencia. Aunque el aislamiento de CSS no admite de forma nativa preprocesadores de CSS como Sass o Less, la integración de preprocesadores de CSS se realiza sin problemas siempre que se produzca la compilación del preprocesador antes de que Blazor reescriba los selectores de CSS durante el proceso de compilación. Con Visual Studio, por ejemplo, configure la compilación del preprocesador existente como una tarea Antes de la compilación en el Explorador del Ejecutor de tareas de Visual Studio.
Muchos paquetes NuGet de terceros, como Delegate.SassBuilder, pueden compilar archivos SASS/SCSS al principio del proceso de compilación antes de que se produzca el aislamiento de CSS y no requieren ninguna configuración adicional.
Configuración del aislamiento de CSS
El aislamiento de CSS está diseñado para poder trabajar de inmediato, pero proporciona configuración para algunos escenarios avanzados, por ejemplo cuando hay dependencias en herramientas o flujos de trabajo existentes.
Personalización del formato del identificador de ámbito
De forma predeterminada, los identificadores de ámbito usan el formato b-<10-character-string>. Para personalizar el formato de un identificador de ámbito, actualice el archivo del proyecto a un patrón deseado:
<ItemGroup>
<None Update="Pages/Example.razor.css" CssScope="my-custom-scope-identifier" />
</ItemGroup>
En el ejemplo anterior, el CSS generado para Example.razor.css cambia su identificador de ámbito de b-<10-character-string> a my-custom-scope-identifier.
Use identificadores de ámbito para lograr la herencia con archivos CSS de ámbito. En el siguiente ejemplo del archivo del proyecto, un archivo BaseComponent.razor.css contiene estilos comunes en todos los componentes. Un archivo DerivedComponent.razor.css hereda estos estilos.
<ItemGroup>
<None Update="Pages/BaseComponent.razor.css" CssScope="my-custom-scope-identifier" />
<None Update="Pages/DerivedComponent.razor.css" CssScope="my-custom-scope-identifier" />
</ItemGroup>
Use el operador comodín (*) para compartir los identificadores de ámbito en varios archivos:
<ItemGroup>
<None Update="Pages/*.razor.css" CssScope="my-custom-scope-identifier" />
</ItemGroup>
Cambio de la ruta de acceso base de recursos web estáticos
El archivo scoped.styles.css se genera en la raíz de la aplicación. En el archivo del proyecto, use la propiedad StaticWebAssetBasePath para cambiar la ruta de acceso predeterminada. En el ejemplo siguiente se coloca el archivo scoped.styles.css, y el resto de los recursos de la aplicación, en la ruta de acceso _content:
<PropertyGroup>
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>
Deshabilitación de la unión automática
Para no usar el modo en que Blazor publica y carga archivos con ámbito en tiempo de ejecución, use la propiedad DisableScopedCssBundling. Al usar esta propiedad, otras herramientas o procesos son responsables de tomar los archivos CSS aislados del directorio obj y de publicarlos y cargarlos en tiempo de ejecución:
<PropertyGroup>
<DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>
Razor Compatibilidad con la biblioteca de clases (RCL)
Cuando una Razor biblioteca de clases (RCL) proporciona estilos aislados, el atributo href de la etiqueta <link> apunta a {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css, donde los marcadores de posición son los siguientes:
{STATIC WEB ASSET BASE PATH}: ruta de acceso base del recurso web estático.{PACKAGE ID}: el id. de paquete de la biblioteca de clases. El id. de paquete tiene como valor predeterminado el nombre de ensamblado del proyecto si<PackageId>no se especifica en el archivo de proyecto de la biblioteca.
En el ejemplo siguiente:
- La ruta de acceso base del recurso web estático es
_content/ClassLib. - El id. de paquete de la biblioteca de clases es
ClassLib.
wwwroot/index.html (Blazor WebAssembly) o Pages_Host.cshtml (Blazor Server):
<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">
Para obtener más información sobre RCL, vea los siguientes artículos: