Lección 3 de 311 min
Bibliotecas de componentes: Material UI
Qué resuelve una biblioteca de componentes, cómo se tematiza Material UI y qué buscar al elegir una.
Un selector de fechas parece sencillo hasta que lo construyes: tiene que abrirse con el teclado, anunciar el mes a los lectores de pantalla, respetar el idioma del usuario, no salirse de la pantalla en el móvil y cerrarse con Escape. Ahora multiplica eso por los diálogos, los menús, las pestañas y los autocompletados de una app real.
Una biblioteca de componentes te ahorra ese trabajo con piezas ya probadas. En esta lección verás qué resuelve exactamente, cómo funcionan Material Design, Material UI (MUI) y su equivalente en Angular, y la alternativa de las bibliotecas «headless», que te dan el comportamiento sin imponer el aspecto.
Qué resuelve una biblioteca de componentes
Qué es una biblioteca de componentes
Un catálogo de piezas de interfaz ya construidas, accesibles y probadas.
Una biblioteca de componentes es un paquete de botones, campos, diálogos, tablas o menús listos para usar en tu framework. Lo valioso no es tanto el aspecto como lo que no se ve: accesibilidad (roles ARIA, navegación con teclado, gestión del foco), comportamiento en el móvil, internacionalización y años de errores ya corregidos.
También aporta consistencia: si toda la app usa el mismo campo de formulario o el mismo botón, todos los formularios se comportan igual, y una mejora en la biblioteca llega a todas las pantallas a la vez.
Úsalo cuando
Casi siempre que la app tenga formularios, diálogos o tablas no triviales, y sobre todo en aplicaciones de gestión, donde importa que todo funcione igual en todas partes.
Ten en cuenta
Atas tu interfaz a sus decisiones y a su calendario de versiones: una actualización mayor puede obligarte a migrar. Y añade peso, así que importa solo los componentes que uses.
import { inject } from "@angular/core";
import { MatDialog } from "@angular/material/dialog";
export class ListaPedidos {
private readonly dialogo = inject(MatDialog);
cancelar(pedido: Pedido) {
// El diálogo atrapa el foco, se cierra con Escape
// y, al cerrarse, devuelve el foco al botón que lo abrió
this.dialogo.open(ConfirmarCancelacion, { data: pedido });
}
}Sistemas de diseño
Las reglas visuales y de uso de una marca; la biblioteca es su implementación.
- Ejemplos:
- Material, Fluent, Carbon
Un sistema de diseño reúne las decisiones de una marca: paleta, tipografía, espaciados, iconos, tono de los textos y reglas de uso, como «un solo botón principal por pantalla». Suele incluir un kit para diseñadores (por ejemplo, en Figma) y una biblioteca de componentes que lo lleva al código.
Lo que conecta ambos mundos son los design tokens: variables con nombre, como color-primario o espacio-m, que comparten diseño y desarrollo. En el código suelen acabar como custom properties de CSS o como variables de un tema.
Úsalo cuando
Cuando varios equipos o productos deben verse y comportarse igual. Para una sola app pequeña, adoptar uno existente, como Material, suele ser más práctico que crear el tuyo.
Ten en cuenta
Un sistema de diseño no es solo un archivo de Figma ni solo una librería: si el diseño y el código evolucionan por separado, se desincronizan enseguida.
/* Las decisiones de diseño, con nombre */
:root {
--color-primario: #6d28d9;
--color-texto: #18181b;
--radio-control: 0.5rem;
--espacio-m: 1rem;
}
.boton {
background: var(--color-primario);
border-radius: var(--radio-control);
padding: var(--espacio-m);
}Material Design
El sistema de diseño de Google: guías, componentes y tokens.
- Creado por:
- Desde:
- 2014
- Versión actual:
- Material 3
Material Design es el sistema de diseño que Google presentó en 2014. Define cómo deben verse y comportarse los botones, los campos de texto, las tarjetas o las barras de navegación, con reglas sobre color, tipografía, elevación y movimiento.
Su versión actual, Material 3 (también conocida como Material You), se apoya en tokens y en esquemas de color generados a partir de un color de origen, con variantes clara y oscura. Google lo usa en Android y en sus propios productos, y distintas bibliotecas lo implementan para cada framework: MUI para React y Angular Material para Angular.
Úsalo cuando
Si quieres un lenguaje visual completo y documentado sin diseñarlo desde cero, o si tu app convive con Android y quieres que resulte familiar.
Ten en cuenta
Material Design es una especificación, no una librería: no se «instala». Lo que instalas es una implementación, y cada una puede ir por detrás de la última versión de la especificación.
Material UI y Angular Material
Material UI es la implementación de Material Design más conocida en React; Angular Material es la de Angular. Merece la pena conocer las dos: la mayoría de las ideas (un tema global, ajustes puntuales, componentes con estilo propio) se repiten en casi cualquier biblioteca.
Material UI (MUI)
La biblioteca de componentes de React que implementa Material Design.
- Framework:
- React
- Paquete:
- @mui/material
Material UI, del equipo MUI, es una de las bibliotecas de componentes más usadas en React. Ofrece decenas de componentes (Button, TextField, Dialog, Autocomplete…) que siguen Material Design y se configuran con props, como cualquier componente de React.
Se instala con el paquete @mui/material y, por defecto, genera sus estilos con Emotion, una librería de CSS-in-JS: los estilos se escriben en JavaScript y se convierten en CSS al renderizar.
MUI es solo para React. Si trabajas con Angular, su equivalente es Angular Material, que verás justo después; las ideas de tema y personalización son muy parecidas.
Úsalo cuando
En apps de React, sobre todo paneles y aplicaciones de gestión, cuando el aspecto Material te sirve y quieres muchos componentes resueltos desde el primer día.
Ten en cuenta
Es una dependencia grande y con opiniones fuertes: alejarse mucho del aspecto Material cuesta trabajo. Y algunos componentes avanzados de su familia MUI X, como las versiones Pro de la tabla de datos, son de pago.
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import TextField from "@mui/material/TextField";
export function Suscripcion() {
return (
<Stack direction="row" spacing={2}>
<TextField label="Correo" type="email" size="small" />
<Button variant="contained">Suscribirme</Button>
</Stack>
);
}Angular Material
La biblioteca oficial del equipo de Angular que implementa Material Design.
- Framework:
- Angular
- Mantiene:
- el equipo de Angular
Angular Material es la contrapartida de MUI en Angular, y la mantiene el propio equipo de Angular. Se añade con ng add @angular/material y ofrece campos de formulario, diálogos, tablas, menús o selectores de fecha integrados con los formularios reactivos.
Sus piezas se usan como componentes o directivas en las plantillas (<mat-form-field>, matInput). En las versiones recientes sigue Material 3, y el tema se define en Sass con el mixin mat.theme(), que genera custom properties que luego puedes ajustar.
Está construida sobre el Angular CDK, que verás al final de la lección: el CDK aporta el comportamiento y Angular Material le pone el aspecto.
Úsalo cuando
En apps Angular que aceptan el aspecto Material: es la opción mejor integrada con el framework y la mantiene el mismo equipo.
Ten en cuenta
Sobrescribir sus clases internas (.mat-mdc-…) es frágil y puede romperse al actualizar. Personaliza a través del tema y de los mixins de sobrescritura que ofrece cada componente.
// styles.scss
@use "@angular/material" as mat;
html {
color-scheme: light dark;
@include mat.theme((color: mat.$violet-palette, typography: Roboto, density: 0));
}
<!-- registro.component.html -->
<mat-form-field>
<mat-label>Correo</mat-label>
<input matInput type="email" [formControl]="correo" />
<mat-error>Escribe un correo válido</mat-error>
</mat-form-field>Temas en MUI: createTheme y ThemeProvider
Un objeto de tema define colores, tipografía y formas para toda la app.
En MUI, el aspecto global se describe en un tema: un objeto que creas con createTheme() y que contiene la paleta (primary, secondary, error…), la tipografía, el espaciado y el radio de las esquinas. A partir del color principal, MUI calcula sus tonos claro y oscuro y el color de texto que contrasta.
Envuelves la aplicación en <ThemeProvider theme={tema}> y todos los componentes de dentro lo leen. <CssBaseline /> añade un reseteo de estilos coherente con el tema, y las versiones recientes admiten los modos claro y oscuro desde el mismo tema.
Úsalo cuando
Al empezar el proyecto, para que tu marca llegue a todos los componentes desde un solo sitio en vez de retocarlos uno a uno.
Ten en cuenta
Si te descubres sobrescribiendo el mismo estilo en muchos componentes, probablemente debería ir en el tema: su clave components permite ajustar los estilos y las props por defecto de cada componente.
import { createTheme, ThemeProvider } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
const tema = createTheme({
palette: { primary: { main: "#6d28d9" } },
shape: { borderRadius: 12 },
typography: { fontFamily: "Inter, sans-serif" },
});
export function App() {
return (
<ThemeProvider theme={tema}>
<CssBaseline />
<Tienda />
</ThemeProvider>
);
}Estilos en MUI: la prop sx y styled()
sx para retoques puntuales; styled() para componentes con estilo reutilizables.
La prop sx acepta un objeto de estilos que entiende el tema: p: 2 es un relleno de dos unidades de espaciado (16px con el valor por defecto), bgcolor: "primary.main" usa el color del tema y { xs: 1, md: 3 } cambia el valor según el breakpoint.
styled() crea un componente nuevo a partir de otro, con estilos fijos y acceso al tema. Es la opción cuando el mismo estilo se repite en muchos sitios: lo defines una vez y lo usas como un componente más.
Úsalo cuando
sx para ajustes de un solo uso, como un margen o un ancho; styled() para piezas que se reutilizan, como una tarjeta de producto con el estilo de tu marca.
Ten en cuenta
Un sx enorme copiado en diez sitios indica que necesitas styled() o un cambio en el tema. Como estos estilos se calculan al renderizar, evita objetos sx grandes en listas de miles de elementos.
import Box from "@mui/material/Box";
import Card from "@mui/material/Card";
import { styled } from "@mui/material/styles";
const TarjetaProducto = styled(Card)(({ theme }) => ({
padding: theme.spacing(2),
border: `1px solid ${theme.palette.divider}`,
}));
export function Producto() {
return (
<TarjetaProducto>
<Box sx={{ p: { xs: 1, md: 3 }, bgcolor: "primary.main", color: "white" }}>
Oferta de la semana
</Box>
</TarjetaProducto>
);
}Con estilos o sin estilos
Las bibliotecas que has visto hasta ahora son con estilos: traen comportamiento y aspecto juntos. Si tu marca no se parece a Material, existe otra familia que separa las dos cosas.
Bibliotecas headless
Comportamiento y accesibilidad sin estilos: el aspecto lo pones tú.
- Ejemplos:
- Radix, React Aria, Angular CDK
Una biblioteca headless («sin cabeza», es decir, sin capa visual) te da la parte difícil de un componente (gestión del foco, navegación con teclado, roles ARIA, posicionamiento de menús) y ningún estilo. Tú decides el aspecto, normalmente con Tailwind o con tu propio CSS.
En React, las más conocidas son Radix UI, React Aria y Headless UI. Sobre ellas se construyen colecciones como shadcn/ui, que no se instala como dependencia: copias el código de cada componente a tu proyecto y lo modificas a tu gusto.
Úsalo cuando
Cuando tienes un sistema de diseño propio que no se parece a Material, pero no quieres reimplementar la accesibilidad de un diálogo o de un menú desplegable.
Ten en cuenta
Te toca a ti todo lo visual, incluidos los estados de foco, error y desactivado, y el modo oscuro. Si el aspecto Material te sirve, una biblioteca con estilos es mucho más rápida.
import * as Dialog from "@radix-ui/react-dialog";
export function EditarPerfil() {
return (
<Dialog.Root>
<Dialog.Trigger className="rounded-lg bg-zinc-900 px-3 py-2 text-white">
Editar perfil
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/50" />
<Dialog.Content className="fixed top-1/2 left-1/2 -translate-1/2 rounded-xl bg-white p-6">
<Dialog.Title className="font-semibold">Editar perfil</Dialog.Title>
{/* Radix gestiona el foco, la tecla Escape y los atributos ARIA */}
<Dialog.Close>Cerrar</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}Angular CDK
El kit de comportamiento de Angular: overlays, foco, arrastrar y soltar, sin estilos.
- Paquete:
- @angular/cdk
- Estilos:
- ninguno
El Component Dev Kit (@angular/cdk) es la base headless sobre la que está hecha Angular Material, y puedes usarlo por separado. Incluye piezas para ventanas flotantes posicionadas (overlay), accesibilidad (atrapar el foco, avisar a los lectores de pantalla), arrastrar y soltar, listas virtuales para miles de filas o copiar al portapapeles.
Es la opción natural en Angular cuando tu diseño no es Material: construyes tus propios componentes con tu CSS o con Tailwind y delegas en el CDK el comportamiento difícil.
Úsalo cuando
Para construir en Angular la biblioteca de tu propio sistema de diseño, o para añadir una pieza concreta (una lista reordenable, un menú flotante) a cualquier app.
Ten en cuenta
El CDK da comportamiento, no componentes terminados: un diálogo hecho con el CDK requiere que montes tú el contenido, los estilos y las animaciones.
import { Component, signal } from "@angular/core";
import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from "@angular/cdk/drag-drop";
@Component({
selector: "app-tareas",
imports: [CdkDropList, CdkDrag],
template: `
<ul cdkDropList (cdkDropListDropped)="soltar($event)">
@for (tarea of tareas(); track tarea) {
<li cdkDrag>{{ tarea }}</li>
}
</ul>
`,
})
export class Tareas {
readonly tareas = signal(["Diseñar", "Programar", "Probar"]);
soltar(evento: CdkDragDrop<string[]>) {
const lista = [...this.tareas()];
moveItemInArray(lista, evento.previousIndex, evento.currentIndex);
this.tareas.set(lista);
}
}Nota
Con estilos o sin ellos, revisa siempre la accesibilidad de lo que montas: navega la pantalla solo con el teclado y comprueba que el foco se ve y no se pierde. Es una prueba de dos minutos que destapa muchos problemas.
Interactivo
Cuatro formas de tener componentes
Activa o desactiva opciones para compararlas cara a cara.
| Característica | Material UI | Angular Material | Angular CDK |
|---|---|---|---|
| Framework | React | Angular | Angular |
| Trae estilos | Sí | Sí | No |
| Sistema de diseño | Material Design | Material Design (Material 3) | Ninguno: pones el tuyo |
| Cómo se personaliza | Tema con createTheme, sx y styled() | Mixin mat.theme() y custom properties | Tu propio CSS o Tailwind |
| Ideal para | Apps React que aceptan el aspecto Material | Apps Angular que aceptan el aspecto Material | Componentes propios y accesibles en Angular |
Ponlo en práctica
Saber qué es cada cosa es la mitad del trabajo. La otra mitad es reconocer cuándo usarla.
Interactivo
¿Qué usarías?
Lee cada situación y elige la opción que mejor la resuelve. Fallar aquí no cuenta: es para practicar.
Tu app Angular de gestión interna necesita tablas, formularios y diálogos coherentes, y al equipo le parece bien el aspecto Material.
Para recordar
- Una biblioteca de componentes aporta, sobre todo, accesibilidad y comportamiento ya probados, y consistencia en toda la app.
- Material Design es el sistema de diseño de Google; MUI (React) y Angular Material (Angular) son implementaciones suyas.
- En MUI, las decisiones globales van en el tema (
createThemeyThemeProvider), los ajustes puntuales ensxy los estilos reutilizables enstyled(). - Las bibliotecas headless, como Radix o el Angular CDK, dan el comportamiento sin estilos: encajan con un sistema de diseño propio.
Comprueba lo aprendido
Cuestionario
Responde las 5 preguntas para completar la lección.
1.¿Qué es lo más valioso que aporta una buena biblioteca de componentes?
2.¿Cuál es el equivalente de MUI en el mundo Angular?
3.En MUI, con el tema por defecto, ¿qué hace
sx={{ p: 2 }}?4.¿Qué distingue a una biblioteca headless como Radix o el Angular CDK?
5.Has sobrescrito clases internas como
.mat-mdc-buttonen veinte sitios y, tras actualizar Angular Material, varios estilos se han roto. ¿Qué deberías haber hecho?