# /frontend — Skill para proyectos frontend Eres un arquitecto frontend experto. Esta skill se activa cuando el usuario pide crear un proyecto frontend, una app con UI, un componente nuevo, o una feature frontend. Tu trabajo es garantizar que TODO el frontend se construya usando el sistema de funciones reutilizables del registry y las mejores practicas actuales. --- ## PASO 1: Consultar el registry (OBLIGATORIO) Antes de escribir una sola linea de codigo, consulta registry.db para saber que componentes, funciones y tipos frontend ya existen: ```bash # Componentes y funciones frontend disponibles sqlite3 registry.db "SELECT id, kind, description FROM functions WHERE lang IN ('ts','typescript') ORDER BY domain, name;" # Tipos frontend disponibles sqlite3 registry.db "SELECT id, algebraic, description FROM types WHERE lang IN ('ts','typescript') ORDER BY domain, name;" # Busqueda FTS5 si buscas algo especifico sqlite3 registry.db "SELECT id, kind, description FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'name:chart* OR description:chart*') ORDER BY name;" ``` Tambien lista los archivos reales en disco ya que no todos estan indexados aun: ```bash ls frontend/functions/ui/ # Componentes React ls frontend/functions/core/ # Utilidades TS puras ls frontend/types/ # Tipos ``` **REGLA:** Si un componente ya existe en `frontend/functions/ui/` (alias `@fn_library`), USALO. Nunca recrear lo que ya existe. --- ## PASO 2: Determinar el tipo de trabajo ### A) App nueva en `apps/` Ir a → Seccion SCAFFOLD APP ### B) Componente nuevo para el registry Ir a → Seccion CREAR COMPONENTE ### C) Feature en app existente Ir a → Seccion CREAR FEATURE --- ## SCAFFOLD APP Crear la estructura completa de una app frontend nueva en `apps/{nombre}/frontend/`. ### Estructura obligatoria ``` apps/{nombre}/ frontend/ package.json vite.config.ts tsconfig.json index.html src/ main.tsx # Entry point App.tsx # Root con ThemeProvider + Router app.css # Tokens CSS — NUNCA hardcodear colores features/ # Feature-based co-location {feature}/ components/ # Componentes del feature hooks/ # Hooks del feature types.ts # Tipos del feature index.ts # Barrel export publico components/ # Componentes compartidos de esta app (no reutilizables) hooks/ # Hooks compartidos lib/ # Utilidades, API client types/ # Tipos globales de la app ``` ### package.json base ```json { "name": "{nombre}", "private": true, "version": "0.0.1", "type": "module", "scripts": { "dev": "vite --host", "build": "tsc -b && vite build", "preview": "vite preview --host" }, "dependencies": { "@base-ui/react": "^1.3.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.577.0", "react": "^19.2.4", "react-dom": "^19.2.4", "recharts": "^2.15.0", "tailwind-merge": "^3.5.0" }, "devDependencies": { "@tailwindcss/vite": "^4.2.2", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.0", "tailwindcss": "^4.2.2", "typescript": "~5.9.3", "vite": "^8.0.0" } } ``` Agregar dependencias extras segun necesidad: - **Tablas**: `@tanstack/react-table` - **Charts**: `recharts` - **Iconos extra**: `@phosphor-icons/react` - **Forms**: `react-hook-form`, `@hookform/resolvers`, `zod` - **Router**: `react-router` o `@tanstack/react-router` - **State**: `zustand` (client state), `@tanstack/react-query` (server state) - **Wails**: los hooks de Wails ya estan en `@fn_library` (useWailsQuery, useWailsMutation, useWailsStream, useWailsEvent, WailsProvider) ### vite.config.ts base ```ts import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' import { resolve } from 'path' export default defineConfig({ plugins: [react(), tailwindcss()], resolve: { alias: { '@': resolve(__dirname, './src'), '@fn_library': resolve(__dirname, '../../../frontend/functions/ui'), }, dedupe: ['react', 'react-dom'], }, build: { target: 'es2022', rollupOptions: { output: { manualChunks: { 'react-vendor': ['react', 'react-dom'], }, }, }, }, }) ``` ### app.css base ```css @import "tailwindcss"; @theme inline { --font-sans: 'Geist Variable', ui-sans-serif, system-ui, sans-serif; --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-secondary: var(--secondary); --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2); --color-chart-3: var(--chart-3); --color-chart-4: var(--chart-4); --color-chart-5: var(--chart-5); --radius-sm: calc(var(--radius) * 0.6); --radius-md: calc(var(--radius) * 0.8); --radius-lg: var(--radius); --radius-xl: calc(var(--radius) * 1.4); } /* Dark theme (default) */ :root, [data-theme="dark"] { --background: oklch(8% 0.015 260); --foreground: oklch(95% 0.01 260); --muted: oklch(18% 0.02 260); --muted-foreground: oklch(60% 0.02 260); --border: oklch(15% 0.01 260); --primary: oklch(65% 0.22 260); --primary-foreground: oklch(98% 0.01 260); --secondary: oklch(20% 0.02 260); --secondary-foreground: oklch(95% 0.01 260); --accent: oklch(18% 0.03 260); --accent-foreground: oklch(95% 0.01 260); --destructive: oklch(55% 0.22 25); --destructive-foreground: oklch(98% 0.01 260); --card: oklch(11% 0.015 260); --card-foreground: oklch(95% 0.01 260); --popover: oklch(12% 0.015 260); --popover-foreground: oklch(95% 0.01 260); --ring: oklch(65% 0.22 260); --input: oklch(22% 0.02 260); --radius: 0.5rem; --chart-1: oklch(62% 0.19 260); --chart-2: oklch(65% 0.2 155); --chart-3: oklch(75% 0.18 85); --chart-4: oklch(60% 0.22 25); --chart-5: oklch(60% 0.2 300); } /* Light theme */ [data-theme="light"] { --background: oklch(99% 0.005 260); --foreground: oklch(15% 0.01 260); --muted: oklch(95% 0.01 260); --muted-foreground: oklch(45% 0.02 260); --border: oklch(90% 0.01 260); --primary: oklch(50% 0.22 260); --primary-foreground: oklch(98% 0.01 260); --secondary: oklch(95% 0.01 260); --secondary-foreground: oklch(20% 0.01 260); --accent: oklch(95% 0.02 260); --accent-foreground: oklch(20% 0.01 260); --destructive: oklch(55% 0.22 25); --destructive-foreground: oklch(98% 0.01 260); --card: oklch(100% 0 0); --card-foreground: oklch(15% 0.01 260); --popover: oklch(100% 0 0); --popover-foreground: oklch(15% 0.01 260); --ring: oklch(50% 0.22 260); --input: oklch(90% 0.01 260); --radius: 0.5rem; --chart-1: oklch(55% 0.22 260); --chart-2: oklch(55% 0.2 155); --chart-3: oklch(65% 0.18 85); --chart-4: oklch(55% 0.22 25); --chart-5: oklch(55% 0.2 300); } @layer base { * { @apply border-border outline-ring/50; } body { @apply bg-background text-foreground; } } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } ``` ### App.tsx base ```tsx import { ThemeProvider } from '@fn_library' export default function App() { return ( {/* Router y contenido aqui */} ) } ``` ### Despues del scaffold ```bash cd apps/{nombre}/frontend && pnpm install ``` --- ## CREAR COMPONENTE Para componentes nuevos que van al registry en `frontend/functions/`. ### Reglas de implementacion 1. **Headless first**: usar `@base-ui/react` como primitivo si el componente es interactivo (dialog, select, tooltip, etc.) 2. **CVA para variantes**: SIEMPRE usar `class-variance-authority` para definir variantes 3. **cn() para clases**: SIEMPRE usar `cn()` de `frontend/functions/core/cn.ts` para componer classNames 4. **CSS variables**: NUNCA hex/rgb/oklch inline en el componente — solo clases Tailwind que mapean a CSS variables (`bg-primary`, `text-muted-foreground`, `border-border`) 5. **Props tipadas**: usar `React.ComponentPropsWithoutRef<"element">` para HTML props spreading 6. **Accesibilidad**: - Elementos semanticos: `