Tokens
Variables OKLCH, su clase de Tailwind, y el sistema dual-tema usado por el dashboard.
Todos los tokens viven en apps/client/app/globals.css. El sistema soporta dos temas vía clase en <html>: .light y .dark. Cada nombre de token resuelve a un valor distinto por tema; la misma utility de Tailwind (bg-card, text-foreground, border-border, etc.) hace lo correcto en ambos contextos.
La escala base viene de un reverse-engineering del Geist design system de vercel.com (research en docs/design-research/vercel/STYLE-GUIDE.md). El body es 15px (seteado en html); las primitivas shadcn, text-sm (~13px), y text-base (15px) derivan de ahí.
Siempre referenciá la clase semántica. Nunca hardcodees el literal OKLCH en un componente.
Superficies (elevadas sobre el bg de la página)
| Token | Clase | Light | Dark |
|---|---|---|---|
--background | bg-background | oklch(0.985 0 0) (~#FAFAFA) | oklch(0 0 0) (#000) |
--foreground | text-foreground | oklch(0.21 0 0) (~#171717) | oklch(0.94 0 0) (~#EDEDED) |
--card | bg-card | oklch(1 0 0) (#FFF) | oklch(0.087 0 0) (~#0A0A0A) |
--popover | bg-popover | oklch(1 0 0) | oklch(0.087 0 0) |
--muted | bg-muted | oklch(0.95 0 0) | oklch(0.21 0 0) |
--muted-foreground | text-muted-foreground | oklch(0.45 0 0) | oklch(0.62 0 0) |
El bg de página (--background) es el color más plano; cards, popovers y demás superficies elevadas usan --card, que está un paso de luminosidad hacia el inverso. Esto evita problemas de stacking de alpha cuando una card va sobre otra.
CTA — paper-white sobre negro, negro sobre blanco
| Token | Clase | Light | Dark |
|---|---|---|---|
--primary | bg-primary | oklch(0.21 0 0) (casi-negro) | oklch(0.94 0 0) (paper-white) |
--primary-foreground | text-primary-foreground | oklch(0.98 0 0) | oklch(0 0 0) |
--accent | bg-accent | oklch(0 0 0 / 0.06) | oklch(1 0 0 / 0.06) |
--accent-foreground | text-accent-foreground | oklch(0.21 0 0) | oklch(0.94 0 0) |
--primary es el fill inverso al bg — la CTA canónica. --accent no es un color de marca; es el overlay de hover (transparente) que las primitivas shadcn usan para indicar estado activo/hover. Para color de marca usá --brand.
Marca e info — Vercel blue
| Token | Clase | Ambos temas |
|---|---|---|
--brand | bg-brand, text-brand | oklch(0.591 0.227 256) (~hsla(212, 100%, 48%, 1)) |
--brand-foreground | text-brand-foreground | oklch(0.98 0 0) |
--info | bg-info, text-info | alias de --brand |
--ring | ring-ring | --brand (los focus rings usan el color de marca) |
Usá --brand para links, badges informativos, highlights inline, y focus rings. No lo uses como bg de botón — los botones se quedan paper-white/black vía --primary. Esto refleja la separación de Vercel: el azul es "acento informativo", el contraste es "acción primaria".
Colores de estado
| Token | Clase | Light | Dark |
|---|---|---|---|
--success | bg-success, text-success | oklch(0.55 0.13 152) | oklch(0.62 0.135 152) |
--warning | bg-warning, text-warning | oklch(0.74 0.16 70) (ámbar) | igual |
--destructive | bg-destructive, text-destructive | oklch(0.59 0.21 22) (rojo) | igual |
Usá la primitiva <StatusBadge> (components/dashboard/status-badge.tsx) para renderizarlos consistentemente. Combiná siempre color + ícono/punto + texto — nunca uses sólo color (regla de a11y de Vercel, ver STYLE-GUIDE §3.5).
Colores de stage (entornos)
Reservados para indicadores de entorno cuando expongamos stages de deploy. No los usa nada todavía, pero documentados así no los repintamos inconsistentemente.
| Token | Clase | Hex | Uso |
|---|---|---|---|
--develop | text-develop | #0a72ef | Entorno de desarrollo |
--preview | text-preview | #de1d8d | Deployments de preview |
--ship | text-ship | #ff5b4f | Producción |
Bordes — box-shadow, no 1px solid
El patrón de Vercel: los bordes de card son shadows, no border: 1px solid. Evita la matemática de box-sizing (el border no consume espacio de layout) y compone bien con drop shadows.
--border-hairline: 0 0 0 1px <alpha hairline>;
--border-hairline-inset: inset 0 0 0 1px <alpha más sutil>;
--border-hairline-large: hairline + drop shadows para cards levantadas;Tres utilities expuestas en @layer utilities:
border-hairline— borde hairline outer, el defaultborder-hairline-inset— línea inner sutil (para headers de tabla, scroll containers)border-hairline-lg— borde + drop shadow compuesto para cards flotantes / popovers
Los componentes existentes que usan border-border (la versión 1px solid) siguen andando. Migralos gradualmente cuando se reescriban — no reescribas primitivas shadcn intactas.
Charts
--chart-1 a --chart-5 son monocromos (foreground puro descendiendo a muted), con --chart-5 aliasing a --brand para énfasis cuando una serie tiene que destacar.
Sidebar
Una sub-escala chica (--sidebar, --sidebar-foreground, --sidebar-border, --sidebar-accent, …) corre un par de pasos de luminosidad para que el rail de nav del dashboard se lea como una superficie distinta sin romper el monocromo.
Utilities custom
Definidas en globals.css @layer utilities:
.label-mono— label mono pequeño en uppercase con letter-spacing 0.18em, foreground al 55% alpha. Wrappealo en<MonoLabel>(ver primitives)..border-hairline,.border-hairline-inset,.border-hairline-lg— box-shadow-as-border (ver arriba)..shadow-soft/.shadow-soft-lg— drop shadows tuneadas para la base oscura..card-premium/.card-lift/.card-glow— efectos sólo de landing (UseDeploy hero cards). No los uses en el dashboard.
Escala de radius
--radius: 0.5rem (8px effective a base 15px)
--radius-sm: calc(var(--radius) - 2px)
--radius-md: var(--radius)
--radius-lg: calc(var(--radius) + 2px)
--radius-xl: calc(var(--radius) + 6px)Usá rounded-md para la mayoría de cards, rounded-sm para chips, rounded-lg para overlays flotantes.
Tipografía
Geist Sans + Geist Mono se cargan en app/layout.tsx. Usá font-sans (default) o font-mono. El body tiene font-feature-settings: "cv11", "ss01" activado para los stylistic alternates de Geist — no lo overrideés globalmente.
El font-size del body es 15px en <html> — entre los 14 de Vercel (más denso) y los 16 default (más accesible). Todas las utilities text-* derivan de esa base.