En esta página

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-smrounded-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
EjemploFila

Vivos — ProjectCard (demo)

Cursor custom en desktop; hover scale sobre la imagen.

Demo ProjectCard

Design System Astro

Tipografía

Stack: Google Sanssystem-uisans-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-pageclamp(2.5rem, 5vw, 4.5rem)text-[length:var(--ds-text-title-page)]
--ds-text-title-sectionclamp(1.5rem, 2.5vw, 1.875rem).ds-heading-section
--ds-text-subtitleclamp(1.125rem, 1.5vw, 1.5rem)
--ds-text-body-lgclamp(1.125rem, 1.25vw, 1.375rem).ds-body-lg
--ds-text-body1remtext-base (default)
--ds-text-eyebrow0.75rem · uppercase · tracking 0.2em.ds-eyebrow

← Volver al trabajo