diff --git a/.atl/skill-registry.md b/.atl/skill-registry.md index 12adccb..5888a3f 100644 --- a/.atl/skill-registry.md +++ b/.atl/skill-registry.md @@ -59,3 +59,15 @@ Generated: 2026-04-13 - Frontend: Vitest + React Testing Library β€” comando: `vitest` - Coverage backend: `dotnet test --collect:"XPlat Code Coverage"` - Coverage frontend: `vitest --coverage` + +### Design System (frontend) +- Source of truth: `Obsidian/02-ARQUITECTURA-y-TECH-STACK/2.14 🎨 Design System.md`. Engram topic_key: `sig-cm2/design-system` +- Brand `#008fbe` (logo) β†’ escalado OKLCH `--brand-50..950`. Neutral cool slate (`--neutral-50..950`). NO usar `gray-*`/`slate-*`/`blue-*` genΓ©ricos +- Tokens semΓ‘nticos: `bg-background`, `text-foreground`, `bg-primary`, `bg-card`, `text-muted-foreground`, `border-border`, `ring-ring`. NUNCA hardcodear `bg-white`/`text-black`/hex inline +- Density compact: button 32-36px, input 36px, table row 40px. Spacing mΓΊltiplos de 4 +- Light + Dark con default = system preference (`useTheme()` hook). Smoke test en ambos antes de mergear +- Forms: ≀4 campos single col, β‰₯5 campos `grid grid-cols-1 md:grid-cols-2 gap-4` +- Tablas mobile: priority columns + tap-to-expand (NO cards-on-mobile, NO pure horizontal scroll) +- Toasts via `sonner` (`` ya montado en `App.tsx`). `toast.success()` / `toast.error()` +- Componentes shadcn: instalar via shadcn MCP server o `npx shadcn@latest add`. NUNCA copy-paste manual del website +- WCAG AA obligatorio: focus rings visibles (ya forzado en CSS base), contrast β‰₯ 4.5:1 texto normal, aria-label en botones icon-only diff --git a/src/web/src/index.css b/src/web/src/index.css index ba57b19..f7c4014 100644 --- a/src/web/src/index.css +++ b/src/web/src/index.css @@ -3,113 +3,216 @@ @import "@fontsource/inter/400.css"; @import "@fontsource/inter/500.css"; @import "@fontsource/inter/600.css"; +@import "@fontsource/inter/700.css"; @import "@fontsource/jetbrains-mono/400.css"; +/* ================================================================ + SIG-CM 2.0 Design System β€” Tokens + Source of truth: Obsidian/02-ARQUITECTURA-y-TECH-STACK/2.14 🎨 Design System.md + Brand color: #008fbe (logo El DΓ­a) β€” escalado OKLCH + ================================================================ */ + @layer base { :root { - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; - --card: 0 0% 100%; - --card-foreground: 240 10% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; - --accent: 240 4.8% 95.9%; - --accent-foreground: 240 5.9% 10%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 240 5.9% 90%; - --input: 240 5.9% 90%; - --ring: 240 5.9% 10%; - --radius: 0.5rem; - --font-sans: "Inter", system-ui, sans-serif; - --font-mono: "JetBrains Mono", monospace; + /* ── Brand (cyan-blue around logo #008fbe) ─────────────────── */ + --brand-50: oklch(0.972 0.013 220); + --brand-100: oklch(0.935 0.035 220); + --brand-200: oklch(0.870 0.072 220); + --brand-300: oklch(0.780 0.108 220); + --brand-400: oklch(0.690 0.128 220); + --brand-500: oklch(0.625 0.130 220); /* logo #008fbe */ + --brand-600: oklch(0.550 0.128 222); + --brand-700: oklch(0.460 0.110 224); + --brand-800: oklch(0.360 0.082 226); + --brand-900: oklch(0.280 0.058 228); + --brand-950: oklch(0.200 0.040 230); + + /* ── Neutral (cool slate, complementa el blue) ─────────────── */ + --neutral-50: oklch(0.985 0.003 240); + --neutral-100: oklch(0.965 0.005 240); + --neutral-200: oklch(0.918 0.008 240); + --neutral-300: oklch(0.850 0.012 240); + --neutral-400: oklch(0.690 0.015 240); + --neutral-500: oklch(0.550 0.018 240); + --neutral-600: oklch(0.450 0.018 240); + --neutral-700: oklch(0.360 0.015 240); + --neutral-800: oklch(0.270 0.013 240); + --neutral-900: oklch(0.210 0.010 240); + --neutral-950: oklch(0.150 0.008 240); + + /* ── Semantic ──────────────────────────────────────────────── */ + --success: oklch(0.640 0.155 145); + --success-foreground: oklch(0.990 0.000 0); + --warning: oklch(0.760 0.150 75); + --warning-foreground: oklch(0.220 0.050 75); + + /* ── shadcn semantic mapping (LIGHT) ───────────────────────── */ + --background: oklch(0.992 0.003 220); /* off-white tinted brand */ + --foreground: var(--neutral-900); + + --card: oklch(1 0 0); /* pure white over tinted bg */ + --card-foreground: var(--neutral-900); + + --popover: oklch(1 0 0); + --popover-foreground: var(--neutral-900); + + --primary: var(--brand-600); /* WCAG AA on white */ + --primary-foreground: oklch(0.990 0.000 0); + + --secondary: var(--neutral-100); + --secondary-foreground: var(--neutral-800); + + --muted: var(--neutral-100); + --muted-foreground: var(--neutral-500); + + --accent: var(--brand-50); /* hover navigation, subtle brand tint */ + --accent-foreground: var(--brand-700); + + --destructive: oklch(0.600 0.220 25); + --destructive-foreground: oklch(0.990 0.000 0); + + --border: var(--neutral-200); + --input: var(--neutral-200); + --ring: var(--brand-500); + + /* ── Spacing & shape ──────────────────────────────────────── */ + --radius: 0.5rem; /* 8px */ + + /* ── Typography ───────────────────────────────────────────── */ + --font-sans: "Inter", system-ui, -apple-system, sans-serif; + --font-mono: "JetBrains Mono", "Cascadia Code", Consolas, monospace; } .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 240 4.9% 83.9%; + --background: var(--neutral-950); + --foreground: var(--neutral-50); + + --card: var(--neutral-900); + --card-foreground: var(--neutral-50); + + --popover: var(--neutral-900); + --popover-foreground: var(--neutral-50); + + --primary: var(--brand-400); /* lighter on dark */ + --primary-foreground: var(--neutral-950); + + --secondary: var(--neutral-800); + --secondary-foreground: var(--neutral-100); + + --muted: var(--neutral-800); + --muted-foreground: var(--neutral-400); + + --accent: var(--neutral-800); + --accent-foreground: var(--brand-300); + + --destructive: oklch(0.580 0.190 25); + --destructive-foreground: oklch(0.990 0.000 0); + + --border: var(--neutral-800); + --input: var(--neutral-800); + --ring: var(--brand-400); } } @layer base { * { - border-color: hsl(var(--border)); + border-color: var(--border); + } + + html { + /* Smooth color transitions when toggling theme */ + color-scheme: light dark; } body { margin: 0; - background-color: hsl(var(--background)); - color: hsl(var(--foreground)); + background-color: var(--background); + color: var(--foreground); font-family: var(--font-sans); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } #root { min-height: 100svh; } - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: var(--font-sans); + /* Selection color uses brand */ + ::selection { + background-color: var(--brand-200); + color: var(--brand-900); + } + .dark ::selection { + background-color: var(--brand-800); + color: var(--brand-50); } - code, - pre, - kbd, - samp { + /* Focus visible β€” accessible ring usando brand */ + *:focus-visible { + outline: 2px solid var(--ring); + outline-offset: 2px; + } + + h1, h2, h3, h4, h5, h6 { + font-family: var(--font-sans); + font-weight: 600; + letter-spacing: -0.01em; + } + + code, pre, kbd, samp { font-family: var(--font-mono); } } +/* ================================================================ + Tailwind 4 inline theme β€” expone tokens como utility classes + Cada token aquΓ­ queda disponible como Tailwind utility + (bg-background, text-foreground, border-border, etc.) + ================================================================ */ @theme inline { - --color-background: hsl(var(--background)); - --color-foreground: hsl(var(--foreground)); - --color-card: hsl(var(--card)); - --color-card-foreground: hsl(var(--card-foreground)); - --color-popover: hsl(var(--popover)); - --color-popover-foreground: hsl(var(--popover-foreground)); - --color-primary: hsl(var(--primary)); - --color-primary-foreground: hsl(var(--primary-foreground)); - --color-secondary: hsl(var(--secondary)); - --color-secondary-foreground: hsl(var(--secondary-foreground)); - --color-muted: hsl(var(--muted)); - --color-muted-foreground: hsl(var(--muted-foreground)); - --color-accent: hsl(var(--accent)); - --color-accent-foreground: hsl(var(--accent-foreground)); - --color-destructive: hsl(var(--destructive)); - --color-destructive-foreground: hsl(var(--destructive-foreground)); - --color-border: hsl(var(--border)); - --color-input: hsl(var(--input)); - --color-ring: hsl(var(--ring)); + --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-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + + /* Brand scale (utilities tipo bg-brand-500, text-brand-700, etc.) */ + --color-brand-50: var(--brand-50); + --color-brand-100: var(--brand-100); + --color-brand-200: var(--brand-200); + --color-brand-300: var(--brand-300); + --color-brand-400: var(--brand-400); + --color-brand-500: var(--brand-500); + --color-brand-600: var(--brand-600); + --color-brand-700: var(--brand-700); + --color-brand-800: var(--brand-800); + --color-brand-900: var(--brand-900); + --color-brand-950: var(--brand-950); + + /* Semantic */ + --color-success: var(--success); + --color-success-foreground: var(--success-foreground); + --color-warning: var(--warning); + --color-warning-foreground: var(--warning-foreground); + --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); + --font-sans: var(--font-sans); --font-mono: var(--font-mono); }