This commit is contained in:
2026-08-06 20:51:46 +02:00
parent f86db1556c
commit d1b70ea7d2
30 changed files with 1601 additions and 260 deletions

View File

@@ -20,10 +20,31 @@ componente — solo redefine tokens (ver `_alternative.scss`, `_minimal.scss`,
## Cómo se aplica un tema
La forma recomendada es `PiThemeService` — inyectable, con persistencia en
`localStorage` y sin tener que tocar el DOM a mano:
```ts
import { inject } from '@angular/core';
import { PiThemeService } from '@pi/ui';
const themeService = inject(PiThemeService);
themeService.setTheme('alternative'); // pinta pi-shell pi-shell__alternative en <body>
themeService.toggleDarkMode(); // pinta/quita dark-mode en <html>
```
`theme`/`darkMode` son señales normales: también se pueden mutar con un
`[(value)]`/`[(checked)]` dos vías directo sobre la señal (ver el uso real en
`apps/playground/src/app/app.ts`/`app.html`). Por debajo, cada cambio dispara
un `effect()` que aplica la clase y persiste — cualquier camino de escritura
queda cubierto por igual, no solo los setters.
Si no quieres el servicio (p. ej. fuera de un contexto de inyección), el
mecanismo de bajo nivel sigue disponible:
```ts
import { ShellClass } from '@pi/ui';
// en el elemento raíz (normalmente <body> o el contenedor de la app)
document.body.className = ShellClass.Alternative; // 'pi-shell pi-shell__alternative'
```
@@ -122,6 +143,8 @@ siempre el *fallback* de `var()` (el valor que tenían antes de tokenizar).
| `--pi-theme-tooltip-radius` | `var(--pi-border-radius-medium)` | `var(--pi-border-radius-xx-small)` | `_mat-tooltip.scss` |
| `--pi-theme-tooltip-shadow` | `var(--pi-box-shadow-panel-double)` | `none` | `_mat-tooltip.scss` |
| `--pi-theme-accordion-radius` | `var(--pi-border-radius-large)` | `var(--pi-border-radius-xx-small)` | `accordion.component.scss`, `_horizontal-accordion.scss`, `_vertical-accordion.scss` (×6, incluye un `calc(... - 0.0625rem)`) |
| `--pi-theme-overlay-divider-color` | `var(--pi-color-grey-100)` | — | `drawer.component.scss`, `hub.component.scss` (borde bajo la cabecera) |
| `--pi-theme-hub-grabber-color` | `var(--pi-color-grey-100)` | — | `hub.component.scss` (el asa superior) |
### Puente con Angular Material (`--mat-sys-*`)
@@ -236,12 +259,55 @@ compilación y no forman parte del contrato de theming en runtime.
## Cómo añadir un tema nuevo
La forma recomendada es el generador:
```bash
nx g @pi/ui:theme mi-marca
```
Crea `libs/ui/src/lib/styles/themes/_mi-marca.scss` (en blanco, con los
grupos de tokens más habituales comentados a modo de chuleta), lo registra en
`themes/index.scss`, añade la entrada a `ShellClass` en `styles/configs.ts` y
extiende `ThemeVariant`/`PiThemeService` — el tema queda seleccionable con
`themeService.setTheme('mi-marca')` sin ningún paso manual más. Solo falta
descomentar y ajustar los tokens que definen el aspecto, y (si tu app expone un
selector de tema) añadirlo a esa lista.
Estos son los pasos que da por ti, si prefieres hacerlo a mano o entender qué
toca:
1. Crea `libs/ui/src/lib/styles/themes/_<nombre>.scss` con un mixin
`<nombre>ThemeTokens` que redefina solo los tokens `--pi-theme-*` (y, si
aplica, `--pi-color-*`) que cambian respecto a Default.
2. Aplícalo a `.pi-shell__<nombre>`.
3. Impórtalo desde `themes/index.scss`.
4. Añade la entrada a `ShellClass` en `styles/configs.ts`.
5. Añade `'<nombre>'` a `ThemeVariant` y una entrada a `shellClassByVariant`
en `util/theme/theme.service.ts`.
No hace falta tocar ningún componente: si consumen los tokens en vez de
valores literales, el tema nuevo se propaga solo.
## Lint de tokens: evitar que un color de marca se filtre sin querer
El bug que motivó tokenizar accordion/tab/select-option/drawer/hub esta misma
sesión (un `var(--pi-color-*)` a pelo en el componente, así que ningún tema ni
el modo oscuro puede redefinir esa superficie) tiene ahora una regla de
stylelint que lo detecta automáticamente: `pi/no-raw-color-token`
(`tools/stylelint-plugin-pi-theme/`).
```bash
nx run ui:lint-styles
```
Marca cualquier `--pi-color-*` que aparezca fuera del *fallback* de un
`var(--pi-theme-*, ...)` — es decir, exactamente el patrón contrario al de
["El patrón de fallback"](#el-patrón-de-fallback). Están exentos
`styles/themes/**` (ahí `--pi-color-*` es legítimo: son las definiciones) y
`styles/common/abstracts/**` (donde se declaran las primitivas de la paleta).
**Estado actual: es un target aparte, no está enganchado a `nx lint` ni a CI.**
Al escribirse, señala ~500 usos preexistentes repartidos por componentes que
esta sesión no llegó a tokenizar — adoptarlo como gate exige ir migrándolos
primero (mismo patrón que en "Cómo tokenizar una superficie nueva"). Úsalo para
no introducir *más* mientras se migra el resto.