Design system
Pendiente de definir. Por ahora usamos los tokens de shadcn-svelte (color base neutral, radius 0.5rem) y un verde esmeralda como primary.
Decisiones tomadas
- Tailwind CSS 4 con config CSS-based (
@theme inlineensrc/app.css). - shadcn-svelte (bits-ui + tailwind-variants) como base de componentes.
- Color primary: verde esmeralda (
hsl(142 71% 45%)) — alineado con marca de salud/nutrición. - Color base: neutral (gris).
- Radius:
0.5rem(default shadcn). - Tipografía: system stack (no custom font cargada todavía).
- Iconos:
@lucide/svelte(tree-shakeable, mismo set que lucide-react).
Tokens (CSS variables)
Definidos en src/app.css:
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--primary: 142 71% 45%; /* esmeralda */
--primary-foreground: 0 0% 98%;
--destructive: 0 84.2% 60.2%;
/* ... */
}
Usar siempre las utility classes de Tailwind, no los valores hardcodeados:
<div class="bg-background text-foreground">...</div>
<button class="bg-primary text-primary-foreground">...</button>
Dark mode
mode-watcher está instalado. Para activarlo, agregar un toggle en el header. Por ahora está en defaultMode="light".
Componentes disponibles
Ver src/lib/components/ui/:
| Componente | Uso | Para agregar más |
|---|---|---|
Button |
CTAs, acciones | pnpm dlx shadcn-svelte@latest add button |
Card (+ Header, Title, Description, Content, Footer) |
Contenedores de información | idem |
Input |
Campos de texto | idem |
Label |
Labels de forms | idem |
Separator |
Divisores | idem |
Badge |
Etiquetas pequeñas | idem |
Avatar (+ Image, Fallback) |
Fotos de perfil | idem |
Componentes a agregar pronto
Dialog— modalesDropdownMenu— menúsSelect— dropdowns nativos mejoradosTextarea— campos de texto largosToast(Sonner) — notificacionesTabs— navegación secundariaTable— listadosCalendar— date pickerPopover— popoversSheet— drawers laterales
Convenciones de uso
Botones
- Acción primaria (CTAs):
Button(default = primary verde) - Acción secundaria:
Button variant="outline"ovariant="secondary" - Acción destructiva:
Button variant="destructive" - Acción terciaria (link-like):
Button variant="ghost" - Loading state: agregar
<Loader2 class="animate-spin" />ydisabled={loading}
Forms
sveltekit-superformspara validación client+server- Schema Zod en
src/lib/modules/<x>/schema.ts - Default values en el
+page.svelteconsuperForm(...) - Errores del server en
form?.error(string) oform?.errors(Zod field errors) - Mostrar error del server con un
div role="alert"arriba del form
Layouts
- Marketing: header con logo + nav + CTA, footer con links legales
- App (nutricionista): sidebar desktop + topbar, mobile-first via sheet/drawer
- Patient: topbar minimal + bottom nav (mobile-first)
Estados vacíos
Para cualquier lista vacía:
{#if data.items.length === 0}
<div class="text-muted-foreground py-12 text-center">
<Icon class="mx-auto mb-2 h-8 w-8" />
<p>No hay nada todavía</p>
<Button href="..." class="mt-4">Crear el primero</Button>
</div>
{:else}
...
{/if}
Loading
- Initial page load: usar
+page.server.tsload (SSR-first, sin loading state) - Mutations: usar
use:enhancecon unloadingstate local - Lista de datos en background: dejar para post-MVP
Por hacer (post-MVP)
- Tipografía custom (probablemente Inter o Geist)
- Logo y brand assets
- Ilustraciones
- Tokens semánticos (
--color-success,--color-warning,--color-info) - Componentes de feedback (rating, empty states con ilustraciones)
- Templates de email branded (Resend)
- Favicon completo (múltiples tamaños + apple-touch-icon)
- OpenGraph image
Decisiones que NO tomar todavía
- No definir un design language completo antes de tener feedback de usuarios reales. shadcn-svelte neutral + verde primario es suficiente para validar el MVP.
- No invertir en animaciones hasta tener flujos validados.
- No hacer un logo elaborado — el favicon SVG simple alcanza.