Temas en componentes de Angular: Guía para principiantes

Cuando construyo componentes, siempre busco que sean flexibles. Solemos utilizarlos en muchos lugares y contextos diferentes donde, en ocasiones, necesitamos cambiar la estructura visual o los colores.
Por ejemplo, imagina que un cliente nos pide una lista de contactos: necesita mostrarse como una tarjeta con foto, nombre y detalles, o en modo lista con bordes marcados, pero en tonos gris y blanco por defecto y sin la foto.
Además, queremos tener la posibilidad de añadir nuevas estructuras y esquemas de colores en el futuro y aplicarlos fácilmente. En resumen, algo como esto:

Empecemos
Para lograrlo, combino tres herramientas fundamentales: la pseudoclase :host(), Angular y las CSS Custom Properties (variables de CSS).

La estructura inicial
Primero, creo el componente app-contact y el marcado HTML para los contactos.
ng g c contactSiguiendo la metodología BEM, asigno una clase a cada elemento para mantener la especificidad baja.
<div class="contact">
<h1 class="contact__firstname">{{contact.first_name}}</h1>
<p class="contact__lastname">{{contact.last_name}}</p>
<img class="contact__avatar" [src]="contact.avatar"/>
</div>Ahora edito el archivo Sass del componente con los estilos por defecto.
.contact {
background: grey;
font-family: monospace;
border: 1px solid black;
border-radius: 5px;
margin: 10px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
&__firstname {
font-size: 1.5em;
color: whitesmoke;
}
&__lastname {
font-size: 1.5em;
color: whitesmoke;
}
&__avatar {
display: none;
border: 1px solid black;
background-color: lightblue;
}
}Con esto, ya tenemos la estructura por defecto lista y funcionando:

La maquetación inicial funciona, pero cometimos un par de errores: los colores están fijos en el código (hardcoded), y la estructura y los colores se encuentran mezclados en el mismo archivo.
Temas y colores
Para solucionarlo, separo cada caso en archivos independientes para estructura y colores. Creamos una carpeta themes con los siguientes archivos:
- winter.scss
- winter-colors.scss
- summer.scss
- summer-colors.scss
Cómo usar :host y CSS Custom Properties
La pseudoclase :host nos ayuda a aplicar estilos únicamente cuando el componente coincide o tiene una clase CSS específica.
Puedes leer más sobre :host() en la documentación de Angular.
Por su parte, las CSS Custom Properties nos permiten almacenar valores reutilizables directamente en CSS, de forma similar a las variables de Sass.
Puedes consultar más detalles sobre CSS Custom Properties en MDN.
Con esto en mente, defino los colores de invierno usando variables CSS dentro de winter-colors.scss:
:host(.contact-winter-colors) {
--background-color: #424b68;
--primary-color: rgb(220, 59, 226);
--secondary-color: rgb(80, 245, 65);
--avatar-background: rgb(48, 109, 78);
}Luego, creo la estructura de invierno (winter.scss) usando las variables CSS para asignar los colores y adaptar los cambios de diseño en los elementos:
:host(.contact-winter) {
.contact {
background: var(--background-color);
font-family: monospace;
border: 1px solid black;
border-radius: 5px;
width: -moz-fit-content;
min-width: 150px;
flex-direction: column;
padding: 10px;
text-align: center;
display: table-cell;
&__firstname {
font-size: 1.5em;
color: var(--primary-color);
}
&__lastname {
font-size: 1.5em;
color: var(--secondary-color);
}
&__avatar {
display: block;
border: 1px solid black;
border-radius: 50%;
background-color: var(--avatar-background);
}
}
}Repite exactamente los mismos pasos para summer.scss y summer-colors.scss.
Importar temas y colores
Dentro de contact.component.scss, importo los archivos de estructura y temas de color:
/* Estructuras */
@import './themes/summer';
@import './themes/winter';
/* Temas de color */
@import './themes/summer-colors';
@import './themes/winter-colors';Puedes consultar más información sobre cómo importar archivos en Sass.
El archivo Sass principal del componente contiene los estilos por defecto y las referencias a las estructuras y colores.
Ahora queremos sustituir esos colores base por los temas de verano o invierno. Para lograrlo, nos apoyamos en los valores de reserva (fallback) de las CSS Custom Properties.
Puedes leer más sobre valores de reserva (fallback).
De esta manera, si --background-color tiene un valor definido, lo utiliza; si no, toma grey como valor por defecto:
background: var(--background-color, grey);Así, el estilo base queda preparado para recibir los valores dinámicos de las variables CSS o mantener el valor por defecto.
Dinamismo con ngClass y :host
Para alternar los colores y la estructura de forma dinámica, uso la directiva ngClass de Angular en la etiqueta del componente:
<app-contact [ngClass]="theme" *ngFor="let contact of contacts" [contact]="contact">
</app-contact>Para controlarlo en tiempo de ejecución, creo una propiedad theme en el componente y la actualizo con métodos como changeLayoutColor, addColor y un reinicio:
<div class="actions">
<button (click)="changeLayoutColor()">change</button>
<button (click)="addColor('contact-winter-colors')">Winter</button>
<button (click)="addColor('contact-summer-colors')">Summer</button>
<button (click)="theme = ''">reset</button>
<p>
current theme: {{theme}}
</p>
</div> theme = '';
changeLayoutColor() {
this.theme = this.theme === 'contact-winter' ? 'contact-summer' : 'contact-winter';
}
addColor(color:string) {
this.theme += ` ${color}`
}Al cambiar la clase en el host, la pseudoclase :host detecta la coincidencia y aplica los estilos correspondientes de inmediato.
Relacionar temas entre componentes
Cuando creamos un componente y lo reutilizamos en distintas partes de la aplicación, muchas veces su estilo visual necesita adaptarse al contenedor o al contexto donde vive.
Para estos casos, la pseudoclase :host-context permite vincular estilos con elementos ancestros o contenedores.
Puedes leer más en la documentación oficial sobre :host-context.
Por ejemplo, imagina que la aplicación principal tiene un componente de botón dentro de un componente de producto. El botón necesita adaptar sus estilos según el lugar donde se encuentre.
Si el componente producto o la aplicación principal cambian sus colores, el botón debe reaccionar en consecuencia. En resumen, algo como esto:

Veamos cómo implementar este comportamiento con el selector :host-context.
Cómo funciona :host-context()
El pseudoselector :host-context nos permite crear una relación directa con el entorno donde vive el componente. Por ejemplo, vincular el componente producto con el componente raíz my-app:
/* product.component.css */
:host-context(my-app.dark) .content {
background-color: black;
color: white;
}Cuando el componente my-app recibe la clase .dark, el componente producto aplica estos estilos de forma automática porque el selector CSS coincide en la jerarquía.
A partir de esta idea, podemos definir múltiples relaciones contextuales.
Múltiples relaciones contextuales
Ya sabemos cómo hacer coincidir un selector con el fondo. Ahora definamos varias reglas para nuestro botón:
- Fondo en blanco humo (
whitesmoke) cuandoapp-producttenga la clase.day. - Fondo azul cuando
app-producttenga la clase.dark. - Fondo rosa cuando
my-apptenga la clase.dark.
Edito el archivo button.component.css y agrego las siguientes reglas para modificar la clase .btn:
/* Archivo button.css */
/* Relación con app-product con la clase day */
:host-context(app-product.day) .btn {
background-color: whitesmoke;
}
/* Relación con app-product con la clase dark */
:host-context(app-product.dark) .btn {
background-color: black;
color: whitesmoke;
}
/* Relación con my-app con la clase dark */
:host-context(my-app.dark) .btn {
background-color: pink;
color: white;
}Con esto, el componente botón responde tanto a su contenedor directo como a la aplicación principal.
Si quieres probar el código en vivo y observar cómo reaccionan los componentes hijos según el contexto, preparé una demo interactiva en StackBlitz:
https://stackblitz.com/edit/host-context-angular-demo
Conclusión
¡Eso es todo! Espero que esta guía te sirva para organizar temas visuales, definir colores dinámicos y conectar estilos entre componentes aprovechando la pseudoclase :host-context y las variables CSS.
Si te gustó el artículo y te resultó útil, compártelo con tu equipo o en redes sociales.
También puedes explorar la demo desplegada aquí: https://theme-angular-components.surge.sh/
Foto de laura adai en Unsplash
Modern frontend development — Angular, React, TypeScript, accessibility, and performance.
Artículos Relacionados
Compartir este artículo
Si esta guía te resultó útil, compártela con tu equipo o con otros desarrolladores.
Software real. Lecciones reales.
Comparto las lecciones que aprendí por las malas, para que puedas evitarlas o estar preparado cuando ocurran.
Únete a 13,800+ desarrolladores y lectores.
Cero spam. Cancela tu suscripción en cualquier momento.