767 líneas · 29KB · Listo para producción

Master Prompt para
Landings SEO Local
de Servicios a Domicilio

Playbook reproducible que captura todo lo aprendido construyendo la landing de GlobalPro Automotriz — 56 páginas, 52 landings por comuna, Schema.org JSON-LD, mapa Leaflet, PWA y deploy en Cloudflare Pages.

Astro 4 Tailwind CSS Leaflet Schema.org Cloudflare Pages PWA
Qué incluye

Todo lo que necesitas para
replicar este proyecto

6 ejes clave que el master prompt cubre con detalle, ejemplos de código y lecciones aprendidas.

Stack Moderno

Astro 4 + Tailwind + Vanilla JS + Leaflet

SEO Local Real

52 landings dinámicas + Schema.org JSON-LD

95+ PageSpeed

HTML estático, sin frameworks pesados

PWA Ready

Webmanifest + iconos + offline ready

Mapa Performante

Leaflet + OpenStreetMap + filtro CSS dark

Seguridad

Buenas prácticas con tokens y secrets

Tutorial

Cómo usar el
Master Prompt

8 pasos para ir desde cero hasta una landing deployada y optimizada.

01

Copia el Master Prompt

Haz clic en el botón "Copiar prompt" abajo. El texto completo del master prompt se copiará a tu portapapeles.

Copiar prompt
02

Abre un nuevo chat con tu IA

Inicia una nueva conversación con tu asistente IA (Claude, GPT, Gemini, etc.). Puedes hacerlo en cualquier plataforma de chat.

03

Pega el prompt y personaliza

Pega el contenido del master prompt. Luego busca y reemplaza los placeholders entre {{LLAVES}} por los valores de tu proyecto: nicho, ciudad, comunas, datos de contacto, colores de marca, etc.

04

Agrega tus tokens (opcional)

Si quieres que la IA haga deploy automático, agrega al final del prompt: GH_TOKEN=tu_token, CF_TOKEN=tu_token, CF_ACCOUNT_ID=tu_id. NUNCA compartas tokens públicamente.

05

Envía y revisa el plan

La IA generará un plan de trabajo. Revisa que coincida con lo que quieres: número de landings, colores, servicios, comunas. Pide ajustes antes de que empiece a codear.

06

Itera con la IA

Durante el desarrollo, pide cambios específicos. Evita frases vagas como "hazlo mejor". Sé concreto: "cambia la opacidad del banner del 30% al 100%" o "usa Leaflet en vez de SVG para el mapa".

07

Verifica el resultado

Una vez deployado, abre el sitio, prueba en móvil, corre PageSpeed Insights, valida los schemas en Google Rich Results Test, registra el sitemap en Search Console.

Abrir sitio demo
08

Estudia el repo de referencia

El repositorio de GitHub tiene todo el código fuente del proyecto que sirvió de base para este master prompt. Clónalo, míralo, aprende de los patrones usados.

Ver repo en GitHub
El Prompt

Master Prompt
Completo

Copia todo el contenido del bloque abajo. Pégalo en un nuevo chat con tu IA para reproducir el mismo nivel de calidad.

MASTER_PROMPT.md

MASTER PROMPT — Landing SEO Local de Servicios Automotrices a Domicilio

Documento maestro generado a partir de una sesión real de iteración. Pégalo completo en un nuevo chat para que la IA reproduzca el mismo nivel de calidad, arquitectura y aprendizajes. Sustituye los placeholders entre {{LLAVES}} por los valores del cliente real.


1. ROL Y OBJETIVO

Actúa como Desarrollador Web Full Stack Senior + Especialista en SEO Técnico/Local, experto en Astro, Tailwind CSS, Git/GitHub y Cloudflare Pages.

Objetivo: Construir una landing page de servicios a domicilio, lista para producción, optimizada para:

  • Conversión móvil
  • Core Web Vitals (95+ en PageSpeed mobile)
  • SEO Local / Geo-SEO (city-level landing pages)
  • Rich snippets via Schema.org JSON-LD

Nicho del ejemplo: Recarga de Gas Aire Acondicionado Automotriz a Domicilio en Santiago de Chile (Región Metropolitana). El patrón es replicable a cualquier nicho de servicios locales: plomería, electricista, cerrajero, limpieza, etc.


2. STACK TÉCNICO OBLIGATORIO

Capa Tecnología Razón
Framework Astro 4.x (SSG / static output) HTML estático = máxima velocidad
Estilos Tailwind CSS 3.x con tokens custom Design system consistente
Interactividad Vanilla JS en <script> (sin React/Vue) Mínimo JS = mejor INP
Mapas Leaflet 1.9.4 + tiles OpenStreetMap con filtro CSS Ver sección 11 — NO usar SVG custom ni CartoDB
Imágenes Sharp (Node) para optimizar a WebP + JPG ~50% de ahorro de peso
Hosting Cloudflare Pages CDN global gratis
Control versiones Git + GitHub Deploy automático

Prohibido: React, Vue, Svelte, jQuery, Bootstrap, frameworks CSS pesados, animaciones GSAP, Lottie.


3. SISTEMA DE DISEÑO — TOKENS TAILWIND EXACTOS

Configurar en tailwind.config.mjs:

colors: {
  'primary-cyan':       '#00BCD4',
  'primary-teal':       '#00838F',
  'accent-orange':      '#ff6b00',
  'accent-orange-light':'#ff8c33',
  'dark-navy':          '#0b1020',
  'dark-navy-alt':      '#121a33',
  'ink-dark':           '#1a1a1a',
  'text-muted':         '#5b6475',
  'border-light':       '#e6e9f0',
  'bg-alt':             '#f5f7fb',
  'whatsapp-green':     '#25D366',
},
fontFamily: { sans: ['Poppins', 'ui-sans-serif', 'system-ui', 'sans-serif'] }

Fuente: Poppins (pesos 400–900) cargada desde Google Fonts con preconnect + display=swap.

Animaciones custom útiles:

  • pulse-slow (badge pulsante)
  • float (tarjeta glassmorphism)
  • glow-pulse (halo detrás de cards)
  • fade-up (reveal on scroll)

4. ARQUITECTURA DE CARPETAS

proyecto/
├── astro.config.mjs          # SSG, site URL, @astrojs/tailwind
├── tailwind.config.mjs       # Tokens + animaciones
├── tsconfig.json             # Path aliases @components, @layouts
├── package.json
├── public/
│   ├── favicon.svg
│   ├── robots.txt            # Con Sitemap: https://dominio.cl/sitemap.xml
│   ├── manifest.webmanifest  # PWA
│   └── images/
│       ├── logo.svg
│       ├── og/               # Open Graph 1200×630 (JPG preferible)
│       ├── icons/            # icon-192.png, icon-512.png, apple-touch-icon.png
│       ├── gallery/          # Fotos renombradas con keywords SEO
│       └── services/         # Fotos por servicio
└── src/
    ├── data/
    │   └── comunas.ts        # Data centralizada de ubicaciones (slug, nombre, zona, lat/lng, FAQ propio)
    ├── layouts/
    │   └── Layout.astro      # Head + SEO + Geo-tags + Schema JSON-LD + manifest link
    ├── components/
    │   ├── Navbar.astro
    │   ├── Hero.astro
    │   ├── TrustBar.astro
    │   ├── ServicesGrid.astro
    │   ├── ProcessSteps.astro
    │   ├── PricingSection.astro
    │   ├── CoverageMap.astro       # Buscador de comunas con chips
    │   ├── LeafletMap.astro        # Mapa interactivo (ver sección 11)
    │   ├── Gallery.astro           # Lightbox accesible
    │   ├── FaqSection.astro
    │   ├── FloatingActions.astro   # WhatsApp flotante + barra móvil
    │   └── Footer.astro
    └── pages/
        ├── index.astro              # Home
        ├── comunas.astro            # Hub de todas las ubicaciones + mapa amplio
        ├── comuna/[comuna].astro    # 52 landings dinámicas por ubicación
        ├── faq.astro                # FAQ global con schema FAQPage
        ├── galeria.astro            # Galería completa
        ├── privacidad.astro         # Política ARCO (Ley 19.628 Chile)
        ├── sitemap.xml.ts           # Sitemap dinámico
        └── robots.txt.ts            # (opcional, o usar public/robots.txt)

5. COMPONENTES — ESPECIFICACIÓN DETALLADA

5.1 Layout.astro — Head + SEO + Schema

  • <html lang="es-CL"> con scroll-smooth y scroll-padding-top: 90px
  • <title> con keyword + intención local
  • <meta name="description"> persuasiva con precio + tiempo + cobertura + CTA
  • Geo-tags: geo.region=CL-RM, geo.placename=Santiago, geo.position=-33.4372;-70.6506, ICBM
  • Open Graph: og:type=website, og:locale=es_CL, og:image 1200×630, og:url, og:site_name
  • Twitter Cards: summary_large_image
  • <link rel="canonical">
  • <link rel="manifest" href="/manifest.webmanifest">
  • <link rel="apple-touch-icon" sizes="180x180" href="/images/icons/apple-touch-icon.png">
  • Fonts: preconnect a fonts.googleapis.com y fonts.gstatic.com
  • 3 schemas JSON-LD inyectados: AutomotiveBusiness + Service + FAQPage (ver sección 7)
  • CSS global: scroll smooth, scrollbar dark theme, prefers-reduced-motion, :focus-visible cyan
  • IntersectionObserver para reveal on scroll ([data-reveal] → .is-visible)

5.2 Navbar.astro

  • Fixed top, backdrop-blur-md, bg-dark-navy/80, sombra dinámica al scroll
  • Logo SVG con gradiente cyan/teal
  • Links ancla: Inicio, Proceso, Cobertura RM, Comunas (mega-dropdown), Galería, Precios, Preguntas
  • Dropdown "Servicios" (hover, 320px width): Recarga Gas R134a, Detección de Fugas UV, Cambio de Filtro, Diagnóstico de Compresor
  • Mega-dropdown "Comunas" (hover, 560px width): top 12 comunas en grid 3 columnas + botón naranja "Ver todas" → /comunas
  • CTA WhatsApp verde (esquina derecha desktop)
  • Menú móvil hamburguesa con acordeón Servicios y Comunas como enlace directo
  • JS Vanilla: toggle menú mobile, cerrar con Escape, cerrar al clic en link

5.3 Hero.astro — ⚠️ LECCIÓN CRÍTICA

NO uses la foto como background absoluto del Hero con object-cover. Eso causa:

  • Foto con zoom/recortada
  • Texto del Hero encima opacando la foto
  • Mala legibilidad

Patrón correcto (aprendido en esta sesión):

  1. Hero sin foto de fondo, solo glow decorativo (bg-hero-radial + blobs blur)

  2. Texto del H1 + subtítulo + botones + stats directamente sobre el fondo oscuro

  3. Foto como <figure> bloque independiente después del grid de stats:

    • object-contain (no object-cover) → se ve completa sin recortes
    • max-h-[70vh] → no domina toda la pantalla
    • bg-dark-navy-alt de fallback para cuando la imagen no llena el ancho (vertical)
    • rounded-3xl + border + shadow-2xl + caption con info del servicio
  4. Sección "Servicio Garantizado" (checklist + precio + estrellas) DEBE ir en su propia sección debajo del Hero, NO como columna derecha del Hero.

5.4 ServicesGrid.astro

  • Grid responsive 1/2/4 columnas
  • Tarjeta con rounded-xl-card (18px), barra superior animada en hover con gradiente from-primary-cyan via-primary-teal to-accent-orange (scale-x-0 group-hover:scale-x-100)
  • Imagen con loading="lazy", decoding="async", width + height explícitos (evita CLS)
  • Badge tipo tag en esquina superior izquierda
  • Lista de inclusiones con checks
  • Precio grande en accent-orange + duración
  • CTA "Cotizar" con WhatsApp link

5.5 PricingSection.astro

  • 3 planes (Básico, Pack Destacado, Especializado)
  • Plan destacado: fondo dark-navy, border accent-orange, lg:scale-105
  • Precio gigante en accent-orange (text-5xl/text-6xl)
  • Lista de inclusiones (✓ verde) y exclusiones (✗ gris line-through)
  • CTA específico por plan con mensaje WhatsApp pre-rellenado

5.6 CoverageMap.astro (buscador de chips)

  • Buscador input + grid de chips clickeables (52 comunas RM)
  • JS Vanilla: filtra chips en tiempo real + muestra resultado dinámico con CTA
  • Resultado: card verde con confirmación + botón "Reservar ahora"

5.7 LeafletMap.astro — ⚠️ VER SECCIÓN 11

  • NO SVG custom (lag con 50+ markers animados)
  • NO CartoDB tiles (requiere API key → muestra "API KEY REQUIRED")
  • SÍ Leaflet 1.9.4 + tiles OpenStreetMap estándar + filtro CSS para dark theme
  • Markers divIcon con dot coloreado por zona
  • Popup con: nombre, zona, tiempo, descripción, barrios, botón "Ir a la landing"
  • Buscador sincronizado mapa↔lista
  • flyTo animado al hacer click en item de lista

5.8 Gallery.astro

  • Variantes: compact (4 fotos en index/comuna) y full (todas en /galeria)
  • <picture> con source WebP + fallback JPG
  • loading="lazy", width+height, decoding="async"
  • Lightbox modal accesible: Escape para cerrar, flechas ←/→ para navegar, click fuera para cerrar
  • Caption con info del servicio

5.9 FaqSection.astro

  • Acordeón accesible: aria-expanded, aria-controls, soporte teclado Enter/Space
  • Solo un panel abierto a la vez (comportamiento acordeón clásico)
  • Icono chevron rota 180° al abrir

5.10 FloatingActions.astro

  • Botón flotante WhatsApp bottom-right (desktop + mobile)
  • Tooltip desktop aparece tras 3s de inactividad
  • Barra fija inferior mobile (Call + WhatsApp) — md:hidden, grid 2 columnas
  • padding-bottom: 72px en body mobile para que la barra no tape contenido

5.11 Footer.astro

  • 4 columnas: marca+contacto, navegación, servicios+legal, datos fiscales
  • Bloque SEO: listado de 52 comunas atendidas en formato "comuna1, comuna2, ..." (refuerza Geo-SEO)
  • Redes sociales: WhatsApp, Instagram, Facebook
  • Bottom bar: copyright + última actualización + status sistema

6. PÁGINA DINÁMICA POR COMUNA — [comuna].astro

Esta es la joya del SEO Local. Generar una landing por cada ubicación atendida.

Data source: src/data/comunas.ts

export interface Comuna {
  slug: string;          // 'las-condes'
  nombre: string;        // 'Las Condes'
  zona: 'Centro'|'Oriente'|'Norte'|'Sur'|'Suroriente'|'Poniente'|'Surponiente'|'Rural';
  descripcion: string;   // SEO description única
  barrios?: string[];
  lat: number;
  lng: number;
  tiempoRespuesta: string; // '60-90 min'
  faqs: ComunaFAQ[];     // 5 FAQs únicas por comuna
}

getStaticPaths()

Generar todas las landings en build time:

export function getStaticPaths() {
  return comunas.map((comuna) => ({
    params: { comuna: comuna.slug },
    props: { comuna },
  }));
}

Estructura de la landing por comuna

  • Hero específico: H1 con "Recarga de Gas A/C a Domicilio en {comuna.nombre}"
  • Breadcrumb: Inicio › Cobertura RM › {comuna.nombre}
  • Badge: "Desde $35.000 · Atención hoy en {comuna.nombre}"
  • Barrios atendidos como chips
  • CTA WhatsApp con mensaje pre-rellenado mencionando la comuna
  • TrustBar + ServicesGrid + ProcessSteps + PricingSection (componentes compartidos)
  • FAQ específico de la comuna (5 preguntas con nombre de la comuna en cada una)
  • Gallery compacta
  • Comunas vecinas (de la misma zona) como chips clickeables

Schemas por comuna (4 en total)

  1. AutomotiveBusiness con areaServed = City de la comuna + geo con lat/lng específicos
  2. Service con areaServed = City + Offer en CLP
  3. FAQPage con las 5 FAQs únicas de la comuna
  4. BreadcrumbList con Inicio › Cobertura RM › {comuna}

7. SCHEMA.ORG JSON-LD — PATRONES

7.1 AutomotiveBusiness (en Layout principal)

{
  "@context": "https://schema.org",
  "@type": ["AutomotiveBusiness", "AutoRepair"],
  "@id": "https://dominio.cl/#business",
  "name": "GlobalPro Automotriz",
  "telephone": "+56987654321",
  "image": ".../og-image.jpg",
  "logo": ".../logo.svg",
  "priceRange": "$$",
  "currenciesAccepted": "CLP",
  "paymentAccepted": "Efectivo, Transferencia, Transbank",
  "address": { "@type": "PostalAddress", "addressLocality": "Santiago", "addressRegion": "RM", "addressCountry": "CL" },
  "geo": { "@type": "GeoCoordinates", "latitude": -33.4372, "longitude": -70.6506 },
  "areaServed": [ /* array de { "@type": "City", "name": "..." } por cada comuna */ ],
  "openingHoursSpecification": [ /* lunes-viernes 08-22, sáb-dom 09-21 */ ],
  "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "327" }
}

7.2 Service

{
  "@context": "https://schema.org",
  "@type": "Service",
  "serviceType": "Recarga de Gas Aire Acondicionado Automotriz",
  "provider": { "@id": ".../#business" },
  "areaServed": { "@type": "AdministrativeArea", "name": "Región Metropolitana" },
  "offers": { "@type": "Offer", "price": "35000", "priceCurrency": "CLP", "availability": "InStock" }
}

7.3 FAQPage

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    { "@type": "Question", "name": "...", "acceptedAnswer": { "@type": "Answer", "text": "..." } }
  ]
}

7.4 BreadcrumbList (solo en landings por comuna)

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    { "@type": "ListItem", "position": 1, "name": "Inicio", "item": "https://dominio.cl/" },
    { "@type": "ListItem", "position": 2, "name": "Cobertura RM", "item": ".../#cobertura" },
    { "@type": "ListItem", "position": 3, "name": "Las Condes", "item": ".../comuna/las-condes" }
  ]
}

8. SEO ON-PAGE CHECKLIST

  • Un único <h1> semántico con keyword transaccional
  • Jerarquía estricta: <h2> por sección, <h3> para items dentro de sección
  • <title> 50–60 chars con keyword + ubicación
  • <meta description> 150–160 chars con precio + tiempo + CTA
  • Geo-tags: geo.region, geo.placename, geo.position, ICBM
  • Open Graph completo con og:locale=es_CL
  • Twitter Cards summary_large_image
  • <link rel="canonical">
  • lang="es-CL" en <html>
  • robots.txt con Sitemap: https://dominio.cl/sitemap.xml
  • sitemap.xml dinámico con todas las URLs (home + comunas + faq + galeria + privacidad)
  • Imágenes: alt descriptivo con keywords, width+height explícitos, loading="lazy" (excepto hero con fetchpriority="high")
  • prefers-reduced-motion respetado
  • :focus-visible accesible (outline cyan)

9. PWA — WEB MANIFEST

public/manifest.webmanifest:

{
  "name": "GlobalPro Automotriz - Recarga de Gas A/C a Domicilio",
  "short_name": "GlobalPro",
  "description": "...",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#0b1020",
  "theme_color": "#00838F",
  "lang": "es-CL",
  "icons": [
    { "src": "/favicon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any maskable" },
    { "src": "/images/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/images/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
  ],
  "shortcuts": [
    { "name": "Reservar", "url": "/?utm_source=pwa#inicio" },
    { "name": "Precios", "url": "/?utm_source=pwa#precios" },
    { "name": "Cobertura", "url": "/?utm_source=pwa#cobertura" }
  ]
}

Generar iconos PNG con sharp desde el favicon.svg:

await sharp(svgBuffer, { density: 384 }).resize(192, 192).png().toFile('icon-192.png');
await sharp(svgBuffer, { density: 384 }).resize(512, 512).png().toFile('icon-512.png');
await sharp(svgBuffer, { density: 384 }).resize(180, 180).png().toFile('apple-touch-icon.png');

10. IMÁGENES — CONVENCIONES

Renombrado SEO

Nombres de archivo en kebab-case con keywords del nicho, separados por guiones:

  • ✅ recarga-gas-aire-acondicionado-automotriz-domicilio-santiago-1.jpg
  • ✅ tecnico-recarga-gas-r134a-auto-domicilio-rm-3.webp
  • ❌ IMG-20260929-WA0132.jpg

Optimización

Script Node usando sharp:

await sharp(input).resize({ width: 1200, height: 1200, fit: 'inside', withoutEnlargement: true })
  .jpeg({ quality: 78, progressive: true, mozjpeg: true }).toBuffer();
await sharp(input).resize({...}).webp({ quality: 75 }).toFile(output);

Uso en HTML

<picture>
  <source srcset="/images/gallery/foto.webp" type="image/webp" />
  <img src="/images/gallery/foto.jpg" alt="keyword + ubicación + marca"
       width="900" height="1200" loading="lazy" decoding="async" />
</picture>

Excepción: Hero principal usa fetchpriority="high" (no lazy).


11. MAPA INTERACTIVO — LECCIONES CRÍTICAS

❌ NO USES SVG CUSTOM

SVG con 50+ markers animados + transforms JS manuales para zoom/pan = lag severo en mobile y baja gama. El re-render constante del SVG mata el performance.

❌ NO USES CARTODB TILES

https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png ahora requiere API key registrada en carto.com/basemaps/apikey. Sin key muestra watermark "API KEY REQUIRED" tapando el mapa.

✅ SOLUCIÓN: Leaflet + OpenStreetMap + Filtro CSS

// Tiles OSM estándar (gratis, sin API key, sin límites duros)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; OpenStreetMap',
  subdomains: 'abc',
  maxZoom: 19,
  className: 'osm-dark-tiles',  // clave para tema oscuro
}).addTo(map);
/* Invertir y teñir tiles OSM para combinar con dark-navy */
.osm-dark-tiles {
  filter: invert(1) hue-rotate(180deg) brightness(0.85) contrast(0.9) saturate(0.7);
}

Carga vía CDN con SRI

<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
      integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
      crossorigin="" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
        integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
        crossorigin=""></script>

Configuración del mapa

const map = L.map('leaflet-map', {
  center: [-33.45, -70.65],
  zoom: 10,           // 10 para vista RM completa, 11 para más cercana
  minZoom: 9,
  maxZoom: 16,
  zoomControl: true,
  scrollWheelZoom: true,
});

Marker custom con divIcon

const makeIcon = (color) => L.divIcon({
  className: 'comuna-marker-leaflet',
  html: `<div class="dot" style="background:${color};"></div>`,
  iconSize: [16, 16],
  iconAnchor: [8, 8],
  popupAnchor: [0, -10],
});

Popup con CTA

Cada marker tiene un popup con: nombre, zona, tiempo, descripción, stats, barrios (chips), y botón "Ir a la landing de {comuna}" que linkea a /comuna/[slug].


12. SITEMAP DINÁMICO

src/pages/sitemap.xml.ts:

import type { APIRoute } from 'astro';
import { comunas } from '../data/comunas.ts';

const SITE_URL = 'https://dominio.cl';

export const GET: APIRoute = () => {
  const pages = [
    { url: '/', priority: '1.0', changefreq: 'weekly' },
    { url: '/faq', priority: '0.8', changefreq: 'monthly' },
    { url: '/galeria', priority: '0.7', changefreq: 'monthly' },
    { url: '/comunas', priority: '0.9', changefreq: 'monthly' },
    { url: '/privacidad', priority: '0.3', changefreq: 'yearly' },
    ...comunas.map(c => ({
      url: `/comuna/${c.slug}`,
      priority: '0.9',
      changefreq: 'monthly',
    })),
  ];
  const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${pages.map(p => `  <url>
    <loc>${SITE_URL}${p.url}</loc>
    <lastmod>${new Date().toISOString().split('T')[0]}</lastmod>
    <changefreq>${p.changefreq}</changefreq>
    <priority>${p.priority}</priority>
  </url>`).join('\n')}
</urlset>`;
  return new Response(xml, { headers: { 'Content-Type': 'application/xml; charset=utf-8' } });
};

13. WORKFLOW DE DESPLIEGUE

13.1 Inicializar Git local

cd proyecto
git init -b main
git add .
git commit -m "feat: landing inicial - [descripción]"

13.2 Crear repo en GitHub

Opción A — GitHub CLI (recomendado):

gh auth login
gh repo create nombre-repo --private --source=. --push

Opción B — API REST con token:

curl -X POST https://api.github.com/user/repos \
  -H "Authorization: Bearer $GH_TOKEN" \
  -d '{"name":"nombre-repo","private":true}'

git remote add origin https://x-access-token:$GH_TOKEN@github.com/USER/nombre-repo.git
git push -u origin main
# Limpiar token del remote después:
git remote set-url origin https://github.com/USER/nombre-repo.git

13.3 Deploy en Cloudflare Pages

Opción A — Conectar GitHub en dashboard (recomendado):

  1. dash.cloudflare.com → Workers & Pages → Create → Pages → Connect to Git
  2. Seleccionar repo
  3. Config:
    • Framework preset: Astro
    • Build command: npm run build
    • Build output directory: dist
  4. Save and Deploy

Opción B — Wrangler CLI:

npm install -g wrangler
wrangler login
wrangler pages project create nombre-proyecto
npm run build
wrangler pages deploy dist --project-name=nombre-proyecto --branch=main

Opción C — Wrangler con API token (para automatizar):

export CLOUDFLARE_API_TOKEN="cfat_..."
export CLOUDFLARE_ACCOUNT_ID="..."
npx wrangler pages deploy dist --project-name=nombre-proyecto --branch=main

13.4 Configurar dominio custom

  1. Cloudflare Pages → tu proyecto → Custom domains → Set up a custom domain
  2. Agregar registro CNAME:
    Type: CNAME | Name: @ | Value: nombre-proyecto.pages.dev
    
  3. SSL se emite automáticamente

14. SEGURIDAD — REGLAS CRÍTICAS

⚠️ NUNCA compartas tokens en chat

Si el usuario pega tokens de GitHub (ghp_...) o Cloudflare (cfat_...) en el chat:

  1. Advertir inmediatamente que los revoque
  2. No usar los tokens sin confirmación explícita del usuario
  3. Recomendar rotación inmediata en:

Si el usuario autoriza usar los tokens

  1. Verificar scopes del token de GitHub (repo, admin:org si necesario)
  2. Verificar que el token de Cloudflare tiene permisos de Pages
  3. NUNCA commitear tokens al repo (verificar .gitignore)
  4. Después del push, limpiar el remote URL:
    git remote set-url origin https://github.com/USER/repo.git
    
  5. Recordar al usuario revocar los tokens al finalizar

.gitignore obligatorio

node_modules/
dist/
.env
.env.local
.env.production
.astro/
.DS_Store

15. CALIDAD — CHECKLIST FINAL

Antes de declarar "listo", verificar:

Build

  • npm run build compila sin errores ni warnings
  • Total de páginas generadas = 1 home + N landings por comuna + páginas auxiliares
  • Tamaño del dist razonable (<10MB sin imágenes grandes)

SEO

  • Un único <h1> por página
  • Schemas JSON-LD válidos (probar en https://search.google.com/test/rich-results)
  • Sitemap accessible en /sitemap.xml
  • robots.txt con link al sitemap
  • Open Graph image 1200×630
  • Geo-tags presentes
  • canonical URL correcta

Performance

  • Sin React/Vue/jQuery
  • Imágenes en WebP + JPG con <picture>
  • loading="lazy" en todas las imágenes bajo el fold
  • fetchpriority="high" en imagen del Hero
  • width+height en todas las imágenes (evita CLS)
  • Fuentes con preconnect + display=swap
  • CSS crítico inline en <head>

Accesibilidad

  • lang="es-CL" en <html>
  • :focus-visible con outline cyan
  • aria-label en botones de iconos
  • aria-expanded en dropdowns y acordeones
  • Soporte teclado en todos los interactivos
  • prefers-reduced-motion respetado
  • Alt descriptivo en imágenes (no vacío)

PWA

  • manifest.webmanifest válido
  • Iconos 192, 512, apple-touch-icon 180
  • theme-color en meta
  • apple-touch-icon link

Mapa (si aplica)

  • Leaflet 1.9.4 vía CDN con SRI
  • Tiles OpenStreetMap (NO CartoDB)
  • Filtro CSS invert(1) hue-rotate(180deg) para dark theme
  • Markers clickeables con popup
  • Buscador sincronizado con lista

Deploy

  • Repo en GitHub (private recomendado)
  • Cloudflare Pages conectado
  • HTTPS automático activo
  • Sitemap registrado en Google Search Console

16. LECCIONES APRENDIDAS — ERRORES A EVITAR

16.1 Foto del Hero como background

❌ No uses foto como background absoluto del Hero con object-cover.

  • Causa zoom/recorte
  • El texto encima opaca la foto
  • Mala legibilidad

✅ Patrón correcto: Hero sin foto de fondo (solo glow), texto directo sobre dark-navy, foto como <figure> bloque independiente debajo con object-contain y max-h-[70vh].

16.2 Tarjeta "Servicio Garantizado" en columna derecha del Hero

❌ No la pongas como columna derecha del Hero.

  • Compite con la foto de fondo
  • Aparece encima del banner

✅ Patrón correcto: Sección independiente debajo del Hero con su propia tarjeta dark-navy, glow decorativo, checklist en grid 2 columnas, precio + estrellas + CTA.

16.3 SVG custom para mapas

❌ No uses SVG con 50+ markers animados + transforms JS.

  • Lag severo en mobile
  • Re-render constante

✅ Patrón correcto: Leaflet + tiles OSM rasterizados (aceleración GPU nativa del navegador).

16.4 CartoDB tiles

❌ No uses basemaps.cartocdn.com/dark_all — ahora requiere API key.

✅ Patrón correcto: tile.openstreetmap.org/{z}/{x}/{y}.png + filtro CSS invert(1) hue-rotate(180deg).

16.5 Tokens en el chat

❌ Nunca aceptes ni uses tokens pegados en el chat sin advertir al usuario que los revoque.

✅ Patrón correcto: Advertir → esperar confirmación → usar solo lo necesario → recordar revocación al final.

16.6 Hard refresh

Después de cada deploy, recordar al usuario hacer hard refresh:

  • Windows/Linux: Ctrl + Shift + R
  • Mac: Cmd + Shift + R
  • Mobile: mantener presionado botón recargar → "Recarga forzada"

O proporcionar la URL del deployment específico (https://[hash].proyecto.pages.dev) que tiene cache nuevo.


17. PROMPT INICIAL SUGERIDO

Para arrancar un proyecto similar en un nuevo chat, pega esto:

Actúa como Desarrollador Web Full Stack Senior + Especialista en SEO Técnico/Local.

Construye una landing page de [NICHO] a domicilio en [CIUDAD/REGIÓN] siguiendo
EXACTAMENTE el master prompt adjunto. Nicho: [descripción]. Ciudad: [nombre].
Comunas/sectores a cubrir: [N] ubicaciones.

Stack: Astro 4 + Tailwind CSS + Vanilla JS + Leaflet + Cloudflare Pages.

Entregar:
1. Repo local con git init + commit inicial
2. Build SSG que genere [N + 5] páginas (home + N landings por ubicación + faq + galeria + privacidad + sitemap)
3. SEO on-page completo con Schema.org JSON-LD (AutomotiveBusiness + Service + FAQPage + BreadcrumbList)
4. Webmanifest PWA + iconos
5. Sitemap dinámico
6. Mapa Leaflet con tiles OSM + filtro CSS dark
7. Galería con lightbox
8. Deploy a GitHub + Cloudflare Pages

Tokens (NO commitear): GH_TOKEN=... CF_TOKEN=... CF_ACCOUNT_ID=...

18. ARCHIVOS DE REFERENCIA

Estructura final del proyecto que funciona en producción:

56 páginas HTML totales:
├── /                          # Home
├── /comunas                   # Hub + mapa amplio
├── /comuna/[slug]             # 52 landings (una por comuna)
├── /faq                       # FAQ global
├── /galeria                   # Galería completa
├── /privacidad                # Política ARCO
└── /sitemap.xml               # Sitemap dinámico

Componentes clave:
├── Layout.astro              # Head + SEO + Schema
├── Navbar.astro              # Sticky + mega-dropdown Comunas
├── Hero.astro                # Sin foto de fondo (solo glow)
├── LeafletMap.astro          # Leaflet + OSM + filtro CSS
├── Gallery.astro             # Lightbox accesible
└── Footer.astro              # Datos fiscales + listado SEO comunas

Data:
└── src/data/comunas.ts       # 52 comunas con slug, nombre, zona, lat/lng, FAQ único

Fin del Master Prompt. Pega esto completo en un nuevo chat para reproducir el mismo nivel de calidad, arquitectura y aprendizajes.

Mira el proyecto real que inspiró este prompt

La landing de GlobalPro Automotriz está online con 56 páginas deployadas. Explórala, abre el código fuente, aprende de los patrones usados.