# Cristal

> El material, no la barra: velo, desenfoque, filo iluminado y un brillo que sigue al puntero.

Paquete: `@pantherkit/design-system` · Archivo: `src/components/cristal.css`

Se importa con:

```css
@import "@pantherkit/design-system/tokens.css";  /* siempre antes */
@import "@pantherkit/design-system/components.css";
```

La pieza se entrega como CSS, no como componente React: se aplica poniendo
las clases en tu propio marcado. No hay import de JavaScript que hacer.

## Qué es

Nació como la navbar de wearefaber.com y subió al sistema con nombre genérico: lo que hace que aquello se lea como cristal no tiene nada que ver con navegar. Sirve igual para una barra, una ficha flotante o un dock.

Son cuatro capas y las cuatro hacen falta: el velo translúcido de tinta con su degradado vertical, el desenfoque saturado de lo que pasa por debajo, **el filo** —luz en el canto de arriba, sombra en el de abajo, porque un cristal tiene grosor y sus dos caras no reciben la misma luz— y el brillo especular que sigue al puntero.

**Un cristal se reconoce por cómo refleja lo que tiene detrás.** Sobre un fondo plano y quieto no es cristal: es una caja gris. Esa frase es toda la regla de uso de esta pieza.

## Clases

| Clase | Qué hace |
| --- | --- |
| `.cristal` | El material. Radio de píldora por defecto. |
| `.cristal__brillo` | El brillo especular. Va en un elemento y no en un pseudo-elemento porque su posición la mueve el puntero por variable CSS, y porque el material deja ::before/::after libres para quien lo envuelva. Siempre `aria-hidden="true"`. |
| `.cristal--denso` | Despegado del contenido: más tinta y sombra proyectada. Se lee como una capa que flota, no como una franja pegada. El velo no cambia de color al densificarse, solo de densidad: el cambio a fondo claro sobre un hero oscuro se percibe como un parpadeo. |
| `.nav-bar / .nav-brillo / .is-scrolled` | Alias heredados. El HTML de la landing usa esos nombres y no se toca. En código nuevo no se usan. |

## Mandos

Custom properties que se redefinen desde fuera —con `style` o con una clase
propia— sin tocar el archivo del sistema.

| Propiedad | Por defecto | Qué controla |
| --- | --- | --- |
| `--cristal-radio` | `var(--radius-full)` | Píldora por defecto. Para una ficha o un dock, `var(--radius-xl)`. |
| `--cristal-desenfoque` | `20px` | El radio del blur. El sistema no tiene escala de desenfoque; por eso es una perilla y no un token. |
| `--cristal-saturacion` | `180%` | El color de detrás se apaga al desenfocar; esto lo devuelve. |
| `--cristal-brillo-ancho` | `18rem` | El tamaño del reflejo. En una pieza pequeña hay que bajarlo o el brillo la cubre entera. |
| `--cristal-brillo-alto` | `8rem` | Ídem, en vertical. |
| `--nx / --ny` | `18% 0%` | La posición del brillo. Registradas con @property e `inherits: true`, así que se pueden escribir en el contenedor. Reposo en la esquina superior izquierda: sin guion la luz se queda quieta ahí y sigue leyéndose como reflejo, no como hueco. |

## El marcado

Esto es exactamente lo que se pinta en la página `/componentes/cristal`:
el mismo texto se inyecta ahí como pieza viva y se imprime aquí. No pueden
separarse.

### Una barra, con contenido pasando por detrás

Es el uso para el que nació. La barra está pegada arriba y el contenido le desfila por debajo: el desenfoque tiene algo que desenfocar y el material se lee como lo que es. Baja la rueda dentro del recuadro.

*Nota: El recuadro con scroll es andamiaje de la vitrina, no parte de la pieza. En producción el contenido de detrás es la página entera y la barra va `fixed`.*

```html
<nav class="cristal flex items-center gap-4 px-5 py-3" data-cristal-luz>
  <span class="cristal__brillo" aria-hidden="true"></span>
  <span class="font-mono text-xs uppercase tracking-[0.18em]">Faber</span>
  <span class="flex-1"></span>
  <a href="/componentes" class="text-sm text-text-secondary">Componentes</a>
  <button type="button" class="fbtn fbtn--primario fbtn--compacto">Hablemos</button>
</nav>
```

### Denso

Cuando la barra se despega del contenido —al hacer scroll— gana tinta y proyecta sombra. Es el mismo material con más densidad, no otro color: pasar a fondo claro sobre un hero oscuro se percibiría como un parpadeo.

*Nota: Mismo andamiaje. En la landing este estado lo dispara `.is-scrolled` desde el layout.*

```html
<nav class="cristal cristal--denso flex items-center gap-4 px-5 py-3" data-cristal-luz>
  <span class="cristal__brillo" aria-hidden="true"></span>
  <span class="font-mono text-xs uppercase tracking-[0.18em]">Faber</span>
  <span class="flex-1"></span>
  <a href="/tokens" class="text-sm text-text-secondary">Tokens</a>
</nav>
```

### Una ficha flotante, con el radio cambiado

El material no sabe qué forma tiene. Se le cambia el radio desde fuera con la perilla y sirve para un panel sin tocar ni una capa. La píldora es el defecto porque el primer uso fue una barra, no porque el cristal sea redondo.

*Nota: Mismo andamiaje.*

```html
<div class="cristal p-5" style="--cristal-radio: var(--radius-xl); --cristal-brillo-ancho: 12rem; --cristal-brillo-alto: 6rem" data-cristal-luz>
  <span class="cristal__brillo" aria-hidden="true"></span>
  <p class="font-mono text-xs uppercase tracking-[0.18em] text-text-muted">Run activo</p>
  <p class="mt-2 text-sm text-text-secondary">El material no sabe si es una barra o un panel. Solo sabe que tiene algo detrás.</p>
</div>
```

## Cuándo NO usarla

La parte que más valor tiene. Cada una está aquí porque costó algo.

### En un pie de página. Se probó y se retiró.

«Se ve feo», y la razón es material, no de gusto: al final de la página no hay nada detrás que desenfocar. El cristal se queda sin su tercera y su cuarta capa y lo que queda es una caja gris con un borde. En la navbar sí funciona porque el contenido le pasa por debajo.

### Sobre un fondo plano y quieto.

Mismo motivo, dicho en general. Si detrás no hay contenido —o lo hay pero no se mueve— el desenfoque no aporta nada y estás pagando `backdrop-filter` (que compone en cada fotograma) por un rectángulo translúcido. Para una superficie estática existen `--color-bg-surface-1` y `-2`.

### Como superficie de lectura larga.

El velo deja pasar lo que hay debajo: un párrafo encima compite con el contenido de detrás y el contraste depende de qué esté pasando en ese momento. El cristal es para barras, chips y paneles cortos.

### Un cristal dentro de otro cristal.

`backdrop-filter` no se acumula: el de dentro solo ve lo que ya pintó el de fuera, así que el desenfoque anidado no desenfoca la página, desenfoca un velo. Cuesta el doble y se ve peor.

### Sin `.cristal__brillo`.

Funciona —el material no depende de él— pero pierde la capa que lo hace parecer una superficie curva y no una lámina. Si se omite, que sea una decisión, no un olvido.

## Tokens de los que depende

Si `tokens.css` no está importado antes, estas variables no existen y la
pieza se pinta con los valores iniciales del navegador —que no es un fallo
visible, es una pieza silenciosamente rota.

- `--color-bg-primary`
- `--color-bg-surface-1`
- `--color-bg-surface-2`
- `--color-text-primary`
- `--color-border`
- `--color-border-hover`
- `--color-shadow-ink`
- `--radius-full`
- `--dur-base`
- `--ease-out-expo`

## Qué queda cuando algo falla

- Sin guion del puntero la luz descansa en `18% 0%` y sigue leyéndose como un reflejo. El guion son dos líneas que escriben `--nx`/`--ny`.
- Sin `backdrop-filter` (`@supports not`) el velo cae a `--color-bg-surface-1` sólido (`-2` en denso). El filo se queda: el grosor del material no depende del desenfoque.
- `prefers-reduced-transparency: reduce` apaga el desenfoque y vuelve el velo opaco. Quien pide menos transparencia está pidiendo poder leer, no un efecto degradado.
- `prefers-reduced-motion: reduce` baja la transición a 0.01ms —no a `none`: con `none` algunos motores no disparan `transitionend`— y el guion del brillo se apaga solo bajo la misma consulta.

## Accesibilidad

- `.cristal__brillo` es `aria-hidden="true"`: es material, no contenido.
- El contenido va por encima del brillo por CSS (`z-index: 1` a todo hijo que no sea el brillo). No hay que ordenarlo a mano.
- El contraste del texto encima depende de lo que pase por detrás. Si el fondo puede ser claro, el velo denso no es opcional.

## Lo que el sistema todavía no resuelve

Valores literales y huecos conocidos. Se escriben para que nadie los
confunda con una decisión.

- No hay token de sombra proyectada: el `0 12px 40px` del denso se deriva de `--color-bg-primary` con `color-mix`. Funciona sobre #0b0b0c, pero si el lienzo se aclara la sombra se aclara con él.
- Tampoco hay escala de desenfoque. Los 20px son una perilla local; si aparece un segundo cristal con otro blur, el sistema no tiene dónde arbitrar.

---

Versión viva: <https://design.wearefaber.com/componentes/cristal>
Índice de componentes: </componentes.md>
