Craft & Arquitectura
Design System
Este portfolio funciona sobre un sistema de diseño construido desde cero. No es una guía de estilo generada automáticamente — es una serie de decisiones explícitas: qué abstrae un token, cuándo merece existir un componente y cómo escala el layout sin deuda técnica.
La tipografía (Google Sans) define una jerarquía de 3 niveles con pesos 400 y 700 — sin intermedios
que rompan la coherencia. Los radios usan escala semántica
(rounded-ds-sm →
rounded-ds-xl)
para que los componentes se "sientan" parte del mismo sistema sin coordinar valores a mano.
El color dark mode usa la clase dark en
<html>,
lo que permite overrides quirúrgicos sin duplicar reglas.
Disponible como referencia técnica para equipos y reclutadores que quieran ver cómo pienso en sistemas, no solo en pantallas.
Componentes UI
Los primitivos en src/components/ui/
centralizan bordes, radios y CTAs. Los bloques de proyecto viven en components/project/ y los de plugin en components/plugin/.
| Componente | Ruta | Uso |
|---|---|---|
| DsSurface | ui/DsSurface.astro | Secciones con borde y fondo: section, inset, compact |
| DsTableShell | ui/DsTableShell.astro | Envolver <table> con scroll y borde |
| DsButtonLink | ui/DsButtonLink.astro | CTA principal (primary) o compacto en tablas (compact) |
| DsBadge | ui/DsBadge.astro | Tags en ProjectCard y similares |
| ProjectCard | ProjectCard.astro | Grid de proyectos en home (imagen 1:1, cursor custom desktop) |
| CaseStudyBlock | project/CaseStudyBlock.astro | Primitivo de bloque narrativo. El patrón vigente en wizzapp.astro usa grid inline de 12 cols — ver sección Páginas de proyecto |
| CaseStudyBanner | project/CaseStudyBanner.astro | Banner 16:9 full-width en casos de estudio. Props: imageSrc?, mobileBleed, ratio, caption. Sin hover/lightbox |
| Carousel | Carousel.astro | Galería horizontal full-width. images: string[] — slides cuadrados 1:1. Arrastre con Pointer Events |
| PluginCarousel | PluginCarousel.astro | Carrusel de tarjetas de plugins en PortfolioHome (#plugins). Arrastre con translateX |
| PluginDocNav | plugin/PluginDocNav.astro | Sidebar sticky (desktop) + collapsible (mobile) para docs de plugins. Props: currentPath, navItems? (default: PLUGIN_CSG_NAV), toc?. Scroll spy integrado |
| ColorScaleLegalShell | plugin/ColorScaleLegalShell.astro | Shell de páginas legales de Color Scale Generator (Layout + PluginDocNav + prose) |
| CopykitLegalShell | plugin/CopykitLegalShell.astro | Shell equivalente para páginas legales de CopyKit. Mismo patrón, datos de PLUGIN_CK |
| Lightbox | Lightbox.astro | Modal global de imagen (<dialog> nativo). Activado con data-lightbox-src en cualquier elemento del DOM |
| PortfolioHome | PortfolioHome.astro | Home completo (PluginCarousel + banner desktop + ProjectCard grid). En / (SITE_LIVE) o /home |
| ComingSoonPage | ComingSoonPage.astro | Página Coming Soon pública. Redirige a Dribbble. Activa cuando SITE_LIVE = false |
| Footer | Footer.astro | Pie global (Dribbble, LinkedIn, enlace a Design System) |
| AnimatedLogo | AnimatedLogo.astro | Marca animada en nav |
Vivos — primitivos UI
DsSurface variant="section"
Contenido dentro de un bloque de sección (ej. Centro de Ayuda del plugin).
DsTableShell + tabla
| Col A | Col B |
|---|---|
| Ejemplo | Fila |
Vivos — ProjectCard (demo)
Cursor custom en desktop; hover scale sobre la imagen.
Demo ProjectCard
Tipografía
Stack: Google Sans → system-ui → sans-serif.
Cargada en Layout.astro; reglas base en global.css; variables en design-tokens.css.
Pesos disponibles: 400 (cuerpo) y 700 (títulos). No asumir 300/500/600.
| Rol | Implementación | Tamaño / interlineado | Peso |
|---|---|---|---|
| Cuerpo (default) | body, p, font-sans | 1rem (16px) · line-height 1.6 | 400 |
| H1 global | h1, .heading-1 | 3rem desktop · 2rem ≤768px · lh 1.1 · tracking -0.02em | 700 |
| H2 global | h2, .heading-2 | 2rem · 1.5rem ≤768px · lh 1.2 | 700 |
| H3 global | h3, .heading-3 | 1.5rem · lh 1.3 | 700 |
| Eyebrow (sección) | .ds-eyebrow | 0.75rem · uppercase · tracking 0.2em · text-gray-500 | 400 |
| Título de bloque | .ds-heading-section | clamp(1.5rem, 2.5vw, 1.875rem) · font-black · lh 1.2 | 700 |
| Cuerpo grande | .ds-body-lg | clamp(1.125rem, 1.25vw, 1.375rem) · leading-relaxed | 400 |
Muestra .ds-eyebrow
Título de sección (.ds-heading-section)
Cuerpo grande (.ds-body-lg) — párrafo introductorio en casos de estudio.
Texto secundario (text-sm text-gray-500)
Layout & anchuras
| Contenedor | Clase | Uso |
|---|---|---|
| Home / proyectos | max-w-screen-2xl mx-auto px-8 | 1536px máximo; ancho útil 1472px en 1536px viewport |
| Plugin docs / Design System | max-w-5xl mx-auto px-8 | 1024px máximo; dos columnas (sidebar + article) |
| Artículos legales / guía | max-w-3xl mx-auto px-8 | 768px máximo; lectura cómoda para prose |
| Padding lateral estándar | px-8 | 32px a cada lado; constante en todos los contenedores |
Imágenes del home — covers de proyectos
Las tarjetas ProjectCard usan imagen cuadrada (1:1), recorte object-cover.
| Contexto | Tamaño mostrado | Export recomendado |
|---|---|---|
| Desktop (2 cols, ≥ md) | ~696 × 696px | 1200 × 1200px o 1400 × 1400px (retina) |
| Móvil (1 col) | ~326px en iPhone 390 | 1200 × 1200px (mismo archivo, se recorta) |
Imágenes del home — banner de plugin (PluginCarousel)
| Contexto | Tamaño mostrado | Export recomendado |
|---|---|---|
| Móvil | Cuadrado, ancho útil (~326–400px) | 800 × 800px mín; 1200 × 1200px cómodo |
| Desktop | 240 × 240px (w-60) | 480 × 480px @2x |
Páginas de proyecto
La referencia canónica es src/pages/projects/wizzapp.astro.
Para un proyecto nuevo: duplica ese archivo, actualiza wizzImages, blueprintSteps y el contenido de cada sección. Añade un ProjectCard en PortfolioHome.astro.
Grid 12 columnas — patrón de sección
Cada sección de contenido usa grid grid-cols-12.
El ritmo es siempre: texto → banner.
| Elemento | Clases (desktop) | Rol |
|---|---|---|
| Encabezado de sección | col-span-12 lg:col-span-10 | eyebrow + h2 (10 cols). Technical chip (lg:hidden) para el side-note en mobile |
| Cuerpo narrativo | col-span-12 lg:col-span-8 | Párrafos, listas, blockquotes. 8 cols = anchura de lectura cómoda |
| Side-note técnico | hidden lg:flex lg:col-start-10 lg:col-span-3 | Contexto técnico en margen derecho. Oculto en mobile (substituido por chip) |
| Banner / imagen | col-span-12 | CaseStudyBanner mobileBleed={true} — imagen 16:9 full-bleed en mobile, redondeada en desktop |
| Banner rotativo (2+ slides) | col-span-12 | Carousel inline con flechas + dots superpuestos. Sin auto-rotate. Arrastre libre |
| Métricas / tablas | col-span-12 | Grid interno 2–3 cols para KPIs; tablas con overflow-x-auto rounded-ds-xl |
Gestión de imágenes en wizzapp.astro
Las rutas de imagen se centralizan en el frontmatter como único punto de edición:
// Un solo archivo como objeto
const wizzImages = {
hero: '/images/wizzapp/cover.png',
banner01: '/images/wizzapp/asset_01.png',
banner02: null, // pendiente
}
// Array para carousel de 2+ imágenes
const banner03Slides = ['/images/wizzapp/asset_03_01.png', ...]
Flag SHOW_GALLERY controla la galería final. Ponerlo en true cuando los assets estén listos.
Portada (hero) — medidas de export
Uso CSS / proporción Export master Hero de proyecto h-[clamp(160px,32vw,320px)] · object-cover 1920 × 640px (~3:1; recorte automático) Banner 16:9 (CaseStudyBanner) aspect-ratio: 16/9 · full-width 1920 × 1080px
Caso de estudio — bloques y galería
El patrón vigente en wizzapp.astro usa grid inline de 12 columnas
(ver sección anterior). CaseStudyBlock existe como primitivo de
respaldo pero las páginas de proyecto nuevas siguen el patrón de grid directo.
Medidas de imagen y Carousel
| Componente | Formato | Tamaño / notas |
|---|---|---|
| CaseStudyBanner | 16:9 (default) o ratio custom | Full-width del contenedor (hasta 1536px). Export 1920 × 1080px |
| Carousel (galería) | Cuadrado 1:1 por slide | images: string[]. Ancho slide ≈ min(85vw, 28rem). Export 1200 × 1200px |
| Banner carousel (b03-style) | 16:9 | Flechas + dots superpuestos. Drag libre. Sin auto-rotate. Export 1920 × 1080px por slide |
Service Blueprint — patrón de diagrama
Para visualizar flujos de servicio (ej. Wizz Life sección 02), se usa un grid inline de 4 columnas con "Línea de Visibilidad" interior. Datos en el frontmatter como array:
const blueprintSteps = [
{ num: "01", title: "Onboarding", frontstage: "...", backstage: "...", kpi: "..." },
...
];
Documentación de plugins
Cada plugin tiene su propio módulo de rutas y nav en src/data/plugin-[nombre]-urls.ts.
Este módulo es la fuente única de verdad para sidebar, breadcrumbs y tabla del Centro de Ayuda.
Estructura de datos de un plugin
// src/data/plugin-[nombre]-urls.ts
export const PLUGIN_XX_BASE = '/plugins/nombre';
export const PLUGIN_XX = { docs, guia, terminos, ... } as const;
export const PLUGIN_XX_FIGMA_URL = 'https://...';
export const PLUGIN_XX_NAV = [
{ label: "Documentación", href: PLUGIN_XX.docs },
// más ítems...
] as const;
Layout de página de docs
Dos columnas: PluginDocNav + article.
El componente acepta navItems para reutilizarse en cualquier plugin
sin duplicar el componente.
| Prop | Tipo | Descripción |
|---|---|---|
| currentPath | string | Astro.url.pathname.replace(/\/$/, '') — marca el ítem activo |
| navItems? | readonly NavItem[] | Lista de documentos del sidebar. Default: PLUGIN_CSG_NAV |
| toc? | TocItem[] | TOC en-página con scroll spy. Solo para guías (h2 en el artículo) |
Plugins activos
| Plugin | Data file | Shell legal | Páginas |
|---|---|---|---|
| Color Scale Generator | plugin-color-scale-urls.ts | ColorScaleLegalShell | index, guia, terminos, privacidad, aviso-legal, cookies, como-activar |
| CopyKit | plugin-copykit-urls.ts | CopykitLegalShell | index, guia, terminos, privacidad, aviso-legal, cookies |
Tokens CSS
Escala de grises
Tailwind gray — única paleta del sistema. Negro y blanco son los extremos semánticos.
white
#ffffff
50
#f9fafb
100
#f3f4f6
200
#e5e7eb
300
#d1d5db
400
#9ca3af
500
#6b7280
600
#4b5563
700
#374151
800
#1f2937
900
#111827
950
#030712
black
#000000
Semánticos — uso por rol
| Rol | Light | Clase | Dark | Clase dark: |
|---|---|---|---|---|
| Texto principal | #000000 | text-black | #ffffff | text-white |
| Texto cuerpo | #374151 | text-gray-700 | #d1d5db | text-gray-300 |
| Texto secundario | #6b7280 | text-gray-500 | #9ca3af | text-gray-400 |
| Texto eyebrow | #6b7280 | text-gray-500 | #9ca3af | text-gray-400 |
| Borde | #e5e7eb | border-gray-200 | #1f2937 | border-gray-800 |
| Fondo de sección | #f9fafb | bg-gray-50 | #111827 | bg-gray-900/40 |
| Fondo de surface | #f3f4f6 | bg-gray-100 | #1f2937 | bg-gray-800/80 |
Radios
ds-sm
8px
ds-md
12px
ds-lg
16px
ds-xl
24px
Variables de tipografía
| Variable | Valor | Clase Tailwind |
|---|---|---|
| --ds-text-title-page | clamp(2.5rem, 5vw, 4.5rem) | text-[length:var(--ds-text-title-page)] |
| --ds-text-title-section | clamp(1.5rem, 2.5vw, 1.875rem) | .ds-heading-section |
| --ds-text-subtitle | clamp(1.125rem, 1.5vw, 1.5rem) | — |
| --ds-text-body-lg | clamp(1.125rem, 1.25vw, 1.375rem) | .ds-body-lg |
| --ds-text-body | 1rem | text-base (default) |
| --ds-text-eyebrow | 0.75rem · uppercase · tracking 0.2em | .ds-eyebrow |