# /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.

## Stack

- **pnpm** — gestor de paquetes
- **React 19** — UI library
- **Vite 8** — build tool
- **Mantine v9** — component library + styling (props, no CSS manual)
- **Phosphor Icons** — `@phosphor-icons/react`
- **Recharts** — charts (via `@mantine/charts`)

**NO usar:** Tailwind, shadcn, CVA, clsx, cn(), lucide-react, styled-components, emotion, CSS-in-JS runtime.

---

## 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
    postcss.config.cjs
    index.html
    src/
      main.tsx              # Entry point con MantineProvider
      App.tsx               # Root con Router
      app.css               # Minimal (font-smoothing solo)
      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": {
    "@mantine/core": "^9.0.0",
    "@mantine/hooks": "^9.0.0",
    "@mantine/notifications": "^9.0.0",
    "@phosphor-icons/react": "^2.1.10",
    "react": "^19.2.4",
    "react-dom": "^19.2.4"
  },
  "devDependencies": {
    "@types/react": "^19.2.14",
    "@types/react-dom": "^19.2.3",
    "@vitejs/plugin-react": "^6.0.0",
    "postcss": "^8.5.8",
    "postcss-preset-mantine": "^1.18.0",
    "postcss-simple-vars": "^7.0.1",
    "typescript": "~5.9.3",
    "vite": "^8.0.0"
  }
}
```

Agregar dependencias extras segun necesidad:
- **Charts**: `@mantine/charts`, `recharts`
- **Tablas**: `@tanstack/react-table`
- **Forms**: `react-hook-form`, `@hookform/resolvers`, `zod`
- **Dates**: `@mantine/dates`, `dayjs`
- **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 { resolve } from 'path'

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'),
      '@fn_library': resolve(__dirname, '../../../frontend/functions/ui'),
    },
    dedupe: ['react', 'react-dom'],
  },
  css: {
    postcss: resolve(__dirname, './postcss.config.cjs'),
  },
  build: {
    target: 'es2022',
    rollupOptions: {
      output: {
        manualChunks: {
          'react-vendor': ['react', 'react-dom'],
        },
      },
    },
  },
})
```

### postcss.config.cjs base

```js
module.exports = {
  plugins: {
    'postcss-preset-mantine': {},
    'postcss-simple-vars': {
      variables: {
        'mantine-breakpoint-xs': '36em',
        'mantine-breakpoint-sm': '48em',
        'mantine-breakpoint-md': '62em',
        'mantine-breakpoint-lg': '75em',
        'mantine-breakpoint-xl': '88em',
      },
    },
  },
};
```

### app.css base

```css
/* Minimal — Mantine handles all theming via MantineProvider */
html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
```

### main.tsx base

```tsx
import '@mantine/core/styles.css'
import '@mantine/notifications/styles.css'
import './app.css'

import React from 'react'
import ReactDOM from 'react-dom/client'
import { MantineProvider, createTheme } from '@mantine/core'
import { Notifications } from '@mantine/notifications'
import App from './App'

const theme = createTheme({
  primaryColor: 'blue',
  defaultRadius: 'md',
  // Customize colors, fonts, etc. here
})

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <MantineProvider theme={theme} defaultColorScheme="dark">
      <Notifications />
      <App />
    </MantineProvider>
  </React.StrictMode>,
)
```

### 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. **Mantine first**: wrappear componentes de Mantine. Solo crear desde cero si Mantine no tiene equivalente.
2. **Styling via props**: usar props de Mantine (`size`, `color`, `variant`, `p`, `m`, `fw`, `gap`, etc.) y el style system. NUNCA clases CSS manuales ni Tailwind.
3. **CSS variables de Mantine**: si necesitas styles inline, usar `var(--mantine-color-*)`, `var(--mantine-spacing-*)`, etc.
4. **Iconos**: usar `@phosphor-icons/react`, no lucide-react ni @tabler/icons-react.
5. **Props tipadas**: usar `React.ComponentPropsWithoutRef<"element">` para HTML props spreading.
6. **Accesibilidad**:
   - Elementos semanticos: `<button>` para acciones, `<a>` para navegacion
   - NUNCA `<div onClick>` para elementos interactivos
   - `aria-label` en botones de solo icono
   - `aria-invalid` + `aria-describedby` en inputs con error
   - Focus management en modales/popovers
7. **Discriminated unions** cuando las props cambian segun variante:

```tsx
type Props = { size?: 'sm' | 'md' | 'lg'; children: React.ReactNode } & (
  | { variant: 'link'; href: string; onClick?: never }
  | { variant: 'button'; onClick: () => void; href?: never }
)
```

### Patron de archivo .tsx

```tsx
import { Select, type SelectProps } from '@mantine/core'

// Re-export con defaults o logica adicional si necesario
interface MySelectProps extends Omit<SelectProps, 'xxx'> {
  customProp?: string
}

function MySelect({ customProp, ...props }: MySelectProps) {
  return <Select {...props} />
}

export { MySelect }
export type { MySelectProps }
```

### Patron de archivo .md

**IMPORTANTE:** El campo `lang` debe ser `ts` (no `typescript`). El indexer solo reconoce `ts`. Los IDs siguen el formato `{name}_ts_{domain}`.

```yaml
---
name: component_name
kind: component
lang: ts
domain: ui
version: "1.0.0"
purity: impure
signature: "ComponentName(props: ComponentProps): JSX.Element"
description: "Descripcion concisa de que hace el componente"
tags: [component, ui, ...]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: ["@mantine/core"]
tested: false
tests: []
test_file_path: ""
file_path: "frontend/functions/ui/component_name.tsx"
props:
  - name: variant
    type: "'default' | 'secondary'"
    required: false
    description: "Estilo visual"
emits: []
has_state: false
framework: react
variant: [default]
---

## Ejemplo

...codigo de ejemplo...

## Notas

...notas relevantes...
```

### Despues de crear

```bash
./fn index && ./fn show {id}
```

---

## CREAR FEATURE

Para features dentro de una app existente. Co-location obligatoria.

### Estructura

```
src/features/{feature_name}/
  components/
    FeatureMain.tsx        # Componente principal
    FeatureDetail.tsx      # Sub-componentes
  hooks/
    useFeatureData.ts      # Hooks del feature
  types.ts                 # Tipos locales
  index.ts                 # Barrel export
```

### Barrel export (index.ts)

```ts
// Solo exportar la API publica del feature
export { FeatureMain } from './components/FeatureMain'
export { useFeatureData } from './hooks/useFeatureData'
export type { FeatureItem, FeatureConfig } from './types'
```

### Patrones de estado obligatorios

**Server state** (datos de API/backend):
```tsx
// Con @tanstack/react-query
const queryKeys = {
  all: ['feature'] as const,
  list: (filters: Filters) => [...queryKeys.all, 'list', filters] as const,
  detail: (id: string) => [...queryKeys.all, 'detail', id] as const,
}

function useFeatureList(filters: Filters) {
  return useQuery({
    queryKey: queryKeys.list(filters),
    queryFn: () => fetchFeatureList(filters),
  })
}
```

**Client state** (UI state compartido):
```tsx
// Con Zustand
import { create } from 'zustand'

interface FeatureStore {
  selectedId: string | null
  setSelected: (id: string | null) => void
}

const useFeatureStore = create<FeatureStore>((set) => ({
  selectedId: null,
  setSelected: (id) => set({ selectedId: id }),
}))
```

**Wails** (apps de escritorio):
```tsx
// Usar hooks del registry
import { useWailsQuery, useWailsMutation } from '@fn_library'

function useFeatureData() {
  return useWailsQuery('GetFeatureData', [], { staleTime: 60_000 })
}
```

### Code splitting por ruta

```tsx
import { lazy, Suspense } from 'react'
import { Skeleton } from '@mantine/core'

const FeaturePage = lazy(() => import('./features/feature/components/FeaturePage'))

function AppRoutes() {
  return (
    <Routes>
      <Route path="/feature" element={
        <Suspense fallback={<Skeleton height="100vh" />}>
          <FeaturePage />
        </Suspense>
      } />
    </Routes>
  )
}
```

---

## CHECKLIST DE VALIDACION (ejecutar siempre al final)

Antes de dar por terminado cualquier trabajo frontend, verificar:

### Colores y estilos
- [ ] CERO colores hardcodeados en componentes (no hex, no rgb inline)
- [ ] Styling via props de Mantine (`size`, `color`, `variant`, `p`, `m`, `fw`, `gap`, etc.)
- [ ] Si se necesitan styles inline, usar CSS variables de Mantine (`var(--mantine-color-*)`)
- [ ] NO clases CSS manuales, NO Tailwind, NO cn(), NO CVA

### Componentes del registry
- [ ] Verificado que no se esta recreando algo que ya existe en `@fn_library` (`frontend/functions/ui/`)
- [ ] Componentes de `@fn_library` usados donde aplica: Card, Select, SimpleSelect, KPICard, Sparkline, DashboardLayout, DataTable, charts, hooks Wails
- [ ] Componentes de Mantine usados directamente donde `@fn_library` no tiene wrapper: Button, TextInput, Table, Alert, Badge, Skeleton, Tabs, Tooltip, Group, Stack, Grid, Box, Paper, AppShell, Container

### Iconos
- [ ] Usando `@phosphor-icons/react` para iconos
- [ ] NO lucide-react, NO @tabler/icons-react

### TypeScript
- [ ] Props interfaces con `React.ComponentPropsWithoutRef` para HTML spreading
- [ ] Discriminated unions donde las props varian segun tipo/variante
- [ ] `as const` para arrays literales y config objects
- [ ] No `any` — usar `unknown` + type guards si es necesario

### Accesibilidad
- [ ] Elementos semanticos (button, a — no div onClick)
- [ ] `aria-label` en botones de solo icono
- [ ] `aria-invalid` + `aria-describedby` en inputs con validacion
- [ ] Focus trap en modales y popovers
- [ ] `prefers-reduced-motion` respetado (ya en app.css base)

### Performance
- [ ] Lazy loading en rutas (`React.lazy` + `Suspense`)
- [ ] `manualChunks` en vite.config para vendor splitting
- [ ] Sin barrel exports profundos que maten tree-shaking
- [ ] Listas largas virtualizadas si >100 items

### Estructura
- [ ] Features co-located: componente + hook + tipos + barrel en el mismo directorio
- [ ] Un `index.ts` por feature con API publica explicita
- [ ] Componentes reutilizables de la app en `src/components/`
- [ ] Tipos compartidos en `src/types/`

---

## ANTI-PATRONES (nunca hacer)

1. **`<div onClick={...}>`** → usar `<button>` o componente Mantine
2. **`style={{ color: '#3b82f6' }}`** → usar prop `c="blue"` o `var(--mantine-color-blue-6)`
3. **`import Button from './MyButton'`** cuando existe en Mantine → usar `import { Button } from '@mantine/core'`
4. **Estado global para todo** → segmentar: server state (React Query), client state (Zustand), form state (React Hook Form), URL state (search params)
5. **`index.ts` en la raiz de `src/`** que re-exporta todo → mata tree-shaking
6. **`// @ts-ignore`** → arreglar el tipo
7. **CSS-in-JS runtime** (styled-components, emotion) → usar props de Mantine
8. **Tailwind, CVA, cn(), clsx** → usar props de Mantine y su style system
9. **Crear utilidades que ya existen**: `getSeriesColor()`, `ChartContainer`, `DashboardLayout`, `DataTable` ya estan en `@fn_library`
10. **Colores de chart hardcodeados** → usar `@mantine/charts` color system o `getSeriesColor()`

$ARGUMENTS
