Devlog · Fase 0 · 6/7
Un design system Catppuccin, glass-first
Veintiséis tokens Catppuccin por sabor, un glassmorphism al 62 % de transparencia y tres familias tipográficas autoalojadas: la piel de aubia.dev, del token CSS al reduced-motion.
- design-system
- catppuccin
- react

Una vez ordenado el backend en módulos, hubo que dar un rostro a aubia.dev. Quería una identidad ceñida, oscura por defecto, que respirase cuidado sin gritar. El resultado se reduce a cuatro decisiones: una paleta Catppuccin, un theming que se ajusta solo, un lenguaje visual en cristal y un movimiento medido. El código real del repositorio respalda cada una de ellas.
Catppuccin: cuatro sabores, dos por defecto
La paleta viene de Catppuccin, una familia de colores bien conocida en el ecosistema dev. Aubia incluye cuatro sabores, pero la landing solo expone dos: Mocha en oscuro, Latte en claro. Los veintiséis tokens de cada sabor se declaran como variables CSS en el bloque @theme de resources/css/app.css. Tailwind v4 se configura aquí por completo en CSS, sin ningún archivo tailwind.config.js.
@theme {
--color-ctp-base: #1e1e2e;
--color-ctp-mantle: #181825;
--color-ctp-text: #cdd6f4;
--color-ctp-mauve: #cba6f7;
/* ... vingt-six tokens pour Mocha */
}
El mauve es mi acento primario. Los otros dos sabores, Frappé y Macchiato, se incluyen como variantes oscuras para la Fase 1, pero quedan fuera del selector público mientras la landing conserve su identidad ceñida. El punto clave: ningún componente referencia un hexadecimal escrito a mano. Una clase como bg-ctp-mauve cambia de valor con el sabor activo, sin que el JSX sepa nada de ello.
El theming se ajusta solo
La elección de tema hoy sigue una regla simple: oscuro por defecto. En abril, la landing partía de lo contrario, el sistema primero, leyendo prefers-color-scheme al montar para aplicar Mocha o Latte según el ajuste del SO. Cambié ese valor por defecto a oscuro en junio. Ahora un visitante llega en Mocha pase lo que pase, y solo sigue el ajuste de su SO si elige explícitamente la opción «sistema» en el selector. El sabor activo se fija en <html data-theme="...">, y los valores de Latte anulan los de Mocha a través de ese atributo. Toda elección explícita, claro, oscuro o sistema, se conserva en localStorage y tiene prioridad sobre el valor por defecto en la siguiente visita.
Este cambio no es cosmético. El público objetivo de Aubia son desarrolladores que viven en modo oscuro: mejor recibirlos en su entorno natural que infligirles un destello blanco al abrir la página.
Glass-first: la transparencia como postura
El lenguaje visual de la landing se apoya en tarjetas glassmorphism: fondo semitransparente, borde fino, desenfoque de fondo. Es lo que deja adivinar el degradado de la página a través de cada superficie y da esa impresión de profundidad amortiguada. En lugar de repetir la misma pila de clases en cada tarjeta, la receta vive en una utility de Tailwind v4 declarada en CSS:
@utility glass-card {
background: color-mix(in oklch, var(--glass-bg-card) 62%, transparent);
backdrop-filter: blur(40px) saturate(1.6) brightness(1.02);
border: 1px solid color-mix(in oklch, var(--glass-stroke) 12%, transparent);
box-shadow: inset 0 1px 0 color-mix(in oklch, var(--glass-highlight) 8%, transparent);
}
El detalle que hace o deshace el efecto es la transparencia. Un fondo opaco anula el backdrop-filter: sin el 62 % del color-mix, el desenfoque ya no tiene nada que desenfocar y la tarjeta se vuelve un simple rectángulo. El saturate y el brightness devuelven al cristal algo de la vida que el desenfoque por sí solo apagaría, y la sombra es interior, un simple hilo de luz en el borde superior. La disciplina es estricta: fondo translúcido, borde apenas marcado, nunca una sombra proyectada pesada que rompería la ilusión.
Tres familias tipográficas autoalojadas
La tipografía se apoya en tres familias, todas alojadas en local: Geist para el cuerpo y la interfaz, Geist Mono para el código y las rutas de archivo, Chakra Petch para los grandes títulos de display.
Ninguna llamada de red a una fundición externa: las @font-face se generan en el build por el plugin de fuentes de Laravel, a partir de los paquetes instalados. En junio, moví la carga a la directiva @fonts, que inyecta estas declaraciones en el <head> antes de la hoja de estilos principal.
Alojar en local va más allá del rendimiento: llamar a una fundición externa filtraría la IP de cada visitante a un subprocesador fuera de la UE, algo que rechazo para un sitio web que se quiere irreprochable en materia de privacidad. Compuse ese trío a propósito. El wordmark de Aubia impone Chakra Petch, y conservar un Inter para el cuerpo habría creado una divergencia permanente con ese título. Geist y Geist Mono vienen de la misma familia, su alineación vertical entre prosa y código es nativa.
El bloque @theme sigue siendo la fuente de verdad de las tres pilas de fuentes:
--font-display: 'Chakra Petch', 'Chakra Petch fallback', system-ui, sans-serif;
--font-sans: 'Geist', 'Geist fallback', system-ui, sans-serif;
--font-mono: 'Geist Mono', 'Geist Mono fallback', ui-monospace, monospace;
La fuente de reserva que sigue a cada familia neutraliza el desplazamiento de maquetación en el momento del swap. Solo Chakra Petch se precarga, porque sostiene el título principal y pesa en el renderizado percibido.
El movimiento, con un seguro
Las animaciones pasan por Motion. Centralicé las variantes reutilizables en un solo archivo, motion-presets.ts, en lugar de redefinirlas en cada componente. La aparición estándar de las secciones es un fundido ascendente, con el easing característico de Luma:
export const fadeUpSpring: Variants = {
hidden: { opacity: 0, y: 24 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.6, ease: EASE_LUMA },
},
};
El seguro es la accesibilidad. Un visitante que ha pedido menos movimiento a nivel de sistema no debe sufrir los fundidos. Motion respeta prefers-reduced-motion de forma nativa, y normalizo su valor con un hook useSafeReducedMotion() que siempre devuelve un booleano, incluso durante el renderizado en servidor donde la media query aún no se evalúa. El presupuesto de animación es deliberadamente ajustado: rara vez más de dos elementos animados a la vez en el campo visible, para jerarquizar la atención en lugar de dispersarla.
Estas decisiones visuales sirven a una convicción de producto que detallo en la sección Visión de la landing. El último artículo de la serie observa lo que los propios motores ven de todo esto: el SEO multilingüe y la extracción por los motores de respuesta de IA. Si el espíritu de Aubia le habla, puede unirse a la lista de espera.
Esta obra se cuenta aquí, artículo a artículo. Súbase a bordo: sus comentarios dibujarán lo que viene.
Únase a la lista de espera