From be71b15afdca456270834c4d4808ea3a85c12537 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Fri, 3 Apr 2026 03:23:52 +0200 Subject: [PATCH] chore: regla frontend_theming y comandos claude MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nueva regla para usar componentes @fn_library y sistema de temas CSS variables en todos los frontends. Añade directorio de comandos claude. Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/commands/frontend.md | 569 ++++++++++++++++++++++++++++++ .claude/rules/INDEX.md | 1 + .claude/rules/frontend_theming.md | 3 + 3 files changed, 573 insertions(+) create mode 100644 .claude/commands/frontend.md create mode 100644 .claude/rules/frontend_theming.md diff --git a/.claude/commands/frontend.md b/.claude/commands/frontend.md new file mode 100644 index 00000000..2678b3a6 --- /dev/null +++ b/.claude/commands/frontend.md @@ -0,0 +1,569 @@ +# /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: `