DocHubs
prdstacktecnologiasarquitecturanextjstypescriptthreejs

🧱 Stack Tecnológico – ALENOR

Documento: Product Requirements Document (PRD) – Tecnologías
Proyecto: ALENOR – Tienda de Jabones Artesanales Naturales
URL producción: alenor.store
Fecha: Marzo 2026


📋 Resumen ejecutivo

ALENOR es una tienda e-commerce de jabones artesanales 100% naturales dirigida al mercado peruano. El stack fue elegido priorizando rendimiento, experiencia visual inmersiva y SEO técnico avanzado, sin requerir base de datos ni backend complejo.


🗂️ Tabla de dependencias

Producción (dependencies)

PaqueteVersiónRol
next16.1.6Framework principal – App Router
react19.2.3UI declarativa
react-dom19.2.3Renderizado DOM
three^0.182.0Renderizado 3D WebGL
@types/three^0.182.0Tipos TypeScript para Three.js
gsap^3.14.2Animaciones de alta performance
framer-motion^12.33.0Animaciones de UI / transiciones
zustand^5.0.11Estado global del carrito
react-hook-form^7.71.1Gestión de formularios
@hookform/resolvers^5.2.2Bridge RHF ↔ Zod
zod^4.3.6Validación de esquemas (client + server)
lucide-react^0.563.0Sistema de íconos SVG
@vercel/analytics^1.6.1Analytics de visitas
@vercel/speed-insights^1.3.1Métricas de performance

Desarrollo (devDependencies)

PaqueteVersiónRol
typescript^5Tipado estático estricto
tailwindcss^4Utility-first CSS framework
@tailwindcss/postcss^4Integración PostCSS para Tailwind v4
eslint^9Linting (flat config)
eslint-config-next16.1.6Reglas Next.js + core-web-vitals
@types/node^20Tipos Node.js
@types/react^19Tipos React 19
@types/react-dom^19Tipos ReactDOM

⚙️ Core Framework

Next.js 16 – App Router

bash
# Comandos del proyecto
npm run dev      # Servidor de desarrollo → http://localhost:3000
npm run build    # Build de producción (SWC compiler)
npm run start    # Servidor de producción
npm run lint     # ESLint con flat config

Características usadas:

  • App Router (app/) con layouts anidados por ruta
  • Route Handlers (app/api/orders/route.ts) — API REST nativa
  • File-based metadata (sitemap.ts, robots.ts, opengraph-image.tsx)
  • Next Image — optimización automática con AVIF/WebP, caché de 1 año
  • Dynamic imports con ssr: false para componentes Three.js
  • next/font/google — fuentes con zero layout shift
ts
// next.config.ts — configuración de imágenes optimizada
images: {
  formats: ['image/avif', 'image/webp'],
  minimumCacheTTL: 60 * 60 * 24 * 365, // 1 año
  deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
}

🎨 Estilos – Tailwind CSS v4

La versión 4 de Tailwind introduce cambios fundamentales respecto a v3:

  • Sin tailwind.config.js — configuración enteramente en CSS
  • Tokens del tema definidos con @theme en globals.css
  • Integración vía PostCSS (@tailwindcss/postcss)
css
/* globals.css — tema custom con @theme */
@theme {
  --color-primary: ...;
  --font-sans: var(--font-geist-sans);
}

🔤 Tipografía – Geist (Vercel)

ts
// app/layout.tsx
import { Geist, Geist_Mono } from "next/font/google";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
  display: "swap",
});
Variable CSSFuenteUso
--font-geist-sansGeist SansTexto general, UI
--font-geist-monoGeist MonoCódigo, etiquetas técnicas

📦 Visualizador 3D – packaging-3d/

El componente más complejo del proyecto: una simulación interactiva de packaging con PBR rendering.

Módulos del sistema 3D

ArchivoResponsabilidad
PackagingViewer.tsxWrapper con dynamic import, guard SSR, fallback WebGL
PackagingScene.tsxComponente cliente principal — inicializa escena, parallax mouse
SceneManager.tsRenderer, cámara, loop de animación, resize handler
Lights.tsIluminación multi-capa: key/fill/accent/hemisphere/rim + presets
PackagingBox.tsGeometría dieline con pivot groups por panel + sistema de flaps
Soap.tsJabón glicerina translúcido (MeshPhysicalMaterial con transmission)
FoldAnimation.tsTimeline GSAP: plano → caja ensamblada con movimientos de cámara
PostProcessing.tsEffectComposer: bloom + viñeta
ProceduralHDRI.tsGenera environment map proceduralmente (sin archivos HDRI externos)
ControlsOverlay.tsxOverlay Framer Motion: tarjetas skin, presets de luz, botón animar

Decisiones de diseño clave

code
Box dieline → nested THREE.Group pivots
  └─ Cada panel tiene su group con origen en el borde de doblez (hinge)
  └─ Side flaps anidados dentro de pivots front/back (se mueven con su padre)
  └─ Top panel anidado dentro del pivot back (se dobla desde el borde superior)

Texturas → Canvas procedural (sin assets externos)
Environment → PMREMGenerator procedural
Rendering → Solo client-side (dynamic import ssr:false)

Stack 3D

TecnologíaVersiónUso
three^0.182.0Escena, geometrías, materiales PBR
gsap^3.14.2Timeline de animación de plegado
framer-motion^12.33.0Overlay de controles UI

🛒 Estado Global – Zustand

Carrito persistido en localStorage mediante middleware persist:

ts
// app/lib/cart-store.ts
export const useCartStore = create<CartStore>()(
  persist(
    (set, get) => ({ ... }),
    {
      name: CART.storageKey,
      partialize: (state) => ({ items: state.items }), // solo persiste items
    }
  )
);

Acciones del store:

AcciónDescripción
addItemAgrega producto (respeta maxQuantity)
removeItemElimina item por ID
updateQuantityActualiza cantidad o elimina si ≤ 0
clearCartVacía el carrito
openDrawer / closeDrawerControla visibilidad del CartDrawer
getSubtotal / getShipping / getTotalComputed values

📝 Formularios y Validación – React Hook Form + Zod

ts
// Flujo de validación dual (client + server)
// Client: RHF + zodResolver en CheckoutForm.tsx
// Server: zod.safeParse en app/api/orders/route.ts

const customerInfoSchema = z.object({ ... });
const result = customerInfoSchema.safeParse(body.customer);

Ventajas del stack:

  • Zero re-renders en escritura gracias a RHF con register uncontrolled
  • Mismo schema Zod reutilizado en frontend y API Route
  • Mensajes de error tipados con zodResolver

🔌 API – Route Handlers (Next.js)

code
POST /api/orders
  ├─ Valida datos del cliente (Zod)
  ├─ Lee productos desde /public/data/products.json (fs/promises)
  ├─ Recalcula precios server-side (previene manipulación)
  ├─ Valida stock y cantidad (1–20 por producto)
  ├─ Calcula shipping (gratis sobre umbral)
  └─ Retorna Order con UUID generado por crypto.randomUUID()

No hay base de datos — productos almacenados en JSON estático, órdenes retornadas al cliente para flujo WhatsApp.


🔍 SEO Técnico

Metadata API (Next.js)

ts
// app/layout.tsx — metadata completa
export const metadata: Metadata = {
  title: { default: "...", template: "%s | ALENOR" },
  openGraph: { type: "website", locale: "es_PE", ... },
  twitter: { card: "summary_large_image", ... },
  robots: { index: true, follow: true, googleBot: { ... } },
  alternates: { canonical: SITE_URL, languages: { "es-PE": SITE_URL } },
};

JSON-LD Structured Data

Tres schemas implementados en <script type="application/ld+json">:

SchemaPropósito
OrganizationIdentidad de marca
WebSiteHabilita Google Sitelinks Search Box
LocalBusiness + StoreRich results para búsquedas locales

Archivos SEO generados dinámicamente

ArchivoRutaPropósito
sitemap.ts/sitemap.xmlIndexación automática
robots.ts/robots.txtDirectivas de crawlers
opengraph-image.tsx/opengraph-imageOG image dinámica

📊 Observabilidad – Vercel

ServicioPaqueteQué mide
Analytics@vercel/analyticsPageviews, unique visitors, referrers
Speed Insights@vercel/speed-insightsCore Web Vitals (LCP, FID, CLS) en producción
tsx
// app/layout.tsx — montados en el RootLayout
<Analytics />
<SpeedInsights />

🏗️ Arquitectura de archivos

code
alenor/
├── app/
│   ├── api/orders/route.ts       # API Route – procesamiento de órdenes
│   ├── components/
│   │   ├── packaging-3d/         # Sistema 3D completo (Three.js + GSAP)
│   │   ├── cart/                 # CartDrawer, CartIcon, CartItem, CartSummary
│   │   ├── checkout/             # CheckoutForm, CheckoutPage, OrderSummary
│   │   ├── catalogo/             # CatalogPage, CatalogProductCard, FilterSidebar
│   │   ├── sections/             # Hero, Products, Benefits, FAQ, Aromas, etc.
│   │   ├── layout/               # Header/Navbar, Footer
│   │   ├── ui/                   # Badge, Button, Card, FilterChip, etc.
│   │   └── widgets/              # ContactWidget, WhatsAppCTA
│   ├── lib/
│   │   ├── cart-store.ts         # Zustand store (persistido)
│   │   ├── animations.ts         # Variantes Framer Motion reutilizables
│   │   ├── constants.ts          # SHIPPING, CART, etc.
│   │   ├── validators.ts         # Schemas Zod compartidos
│   │   ├── format.ts             # Formateo de precios/fechas
│   │   └── products.ts           # Helpers de productos
│   ├── types/
│   │   ├── products.ts           # Product, CartItem, ProductsData
│   │   └── orders.ts             # Order, OrderItem, CustomerInfo
│   ├── catalogo/page.tsx         # /catalogo — catálogo completo
│   ├── checkout/page.tsx         # /checkout — formulario de compra
│   ├── pedido/[orderId]/page.tsx # /pedido/:id — confirmación
│   ├── layout.tsx                # RootLayout + metadata + JSON-LD
│   ├── page.tsx                  # / — landing page
│   ├── sitemap.ts                # /sitemap.xml dinámico
│   ├── robots.ts                 # /robots.txt
│   └── opengraph-image.tsx       # OG image generada por Next.js
├── public/data/products.json     # Catálogo de productos (fuente de verdad)
├── next.config.ts                # Config Next.js (imágenes, compresión, etags)
├── tsconfig.json                 # TypeScript strict + alias @/*
├── postcss.config.mjs            # PostCSS con @tailwindcss/postcss
└── eslint.config.mjs             # ESLint flat config

🔧 TypeScript – Configuración

json
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2017",
    "strict": true,               // modo estricto completo
    "moduleResolution": "bundler", // resolución moderna
    "incremental": true,           // builds incrementales
    "paths": { "@/*": ["./*"] }    // alias para imports limpios
  }
}

🚀 Deploy – Vercel

AspectoConfiguración
PlatformVercel (integración nativa Next.js)
Compresióncompress: true en next.config.ts
HeaderspoweredByHeader: false (no expone X-Powered-By)
ETagsgenerateEtags: true (caché HTTP eficiente)
Dominioalenor.store (canonical, sin www)
ImágenesOptimización automática Vercel Image Optimization

📌 Decisiones de arquitectura destacadas

  1. Sin base de datos — Productos en JSON estático (public/data/products.json), órdenes procesadas en memory y enviadas por WhatsApp. Reduce costos y complejidad operacional.

  2. Precios recalculados server-side — El API Route lee precios desde el JSON, nunca confía en los precios enviados por el cliente, previniendo manipulación.

  3. Three.js solo client-sidedynamic(() => import(...), { ssr: false }) garantiza que WebGL nunca se intente en servidor.

  4. Schema Zod compartido — El mismo customerInfoSchema valida en el formulario (RHF) y en la API Route, eliminando duplicación de reglas.

  5. Tailwind v4 sin config file — Tokens definidos en CSS con @theme, más cercano al estándar web y sin overhead de configuración JS.

  6. Zustand con partialize — Solo items se persiste en localStorage, el estado de UI (drawer abierto) se resetea en cada sesión.