Guía de productos · La ficha

La ficha: la página de detalle que se genera sola

Cada producto tiene su página de detalle (L4), y NO se edita una por una: una sola ruta dinámica sirve todas las fichas desde la colección. El layout es schema-driven —pocos campos base y bloques que aparecen solo si traen datos—.

Es la quinta pieza del flujo: la página que el visitante abre cuando ya eligió qué le interesa. Aquí espera todo lo que necesita para decidir —qué es, cómo es por dentro, qué cumple y cuál es el siguiente paso—. Y se genera sola: una ruta [...slug].astro recorre la colección y produce una ficha por producto, sin una página a mano por cada uno.

El layout (ProductLayout) sigue el principio de pocos campos base más bloques opcionales: el hero con galería y datos clave siempre; las especificaciones, aplicaciones, certificaciones y FAQ solo si el frontmatter las trae. Y un sidebar sticky de conversión que mantiene «cotizar por WhatsApp» a la vista. Informar y encauzar, en la misma página.

Definición

¿Qué es la ficha de un producto?

La página de detalle (nivel L4) de un producto: la que entrega todo para decidir. Se genera desde una ruta dinámica [...slug].astro que sirve todas las fichas, con la estructura en ProductLayout y el contenido en el .md.

La ficha es la página individual de un producto —/productos/<slug>—, el nivel más profundo del catálogo. Es donde el visitante llega cuando ya escogió qué mirar, así que su trabajo es entregar todo lo necesario para decidir: galería, descripción a fondo, especificaciones, cumplimiento y un camino claro a la cotización. No es un resumen como la card: es el detalle completo.

Lo que la hace especial técnicamente es que no se escribe una por producto. Una única ruta dinámica, [...slug].astro, recorre la colección con getStaticPaths y genera una página por cada .md. La estructura vive en ProductLayout (schema-driven, con bloques opcionales); el contenido —título, foto, precio, specs, FAQ— sale del frontmatter y del cuerpo Markdown. Crear una ficha es crear un archivo.

Función e importancia

¿Para qué sirve?

Entrega el detalle completo para decidir, sin mantener una página por producto: una ruta genera todas, los bloques aparecen solo si hay datos, y el sidebar sticky encauza la conversión.

Su función es cerrar la decisión. La card del catálogo engancha; la ficha convence. Aquí va la galería que muestra el producto desde varios ángulos, la descripción larga que responde el «¿esto es para mí?», las especificaciones que comparan, las certificaciones que dan confianza y las FAQ que matan la última objeción. Es la página que más pesa en la conversión.

Y lo hace de forma sostenible. Una sola ruta sirve todo el catálogo, así que diez o mil productos comparten exactamente el mismo layout —ninguna ficha queda distinta por accidente—. Los bloques opcionales mantienen cada ficha tan simple o completa como el producto requiera, sin huecos. Y el sidebar sticky asegura que, por largo que sea el scroll, cotizar esté siempre a un toque.

Cero páginas a mano

Una única ruta [...slug].astro genera todas las fichas del catálogo. Diez productos o mil: el mismo archivo. Crear una ficha es crear un .md; no hay una página .astro por producto que mantener, ni el riesgo de que una quede distinta de las demás.

Bloques que aparecen solos

La ficha es schema-driven con pocos campos base y bloques opcionales: specs, aplicaciones, certificaciones, FAQ. Cada uno se pinta solo si el frontmatter trae sus datos. Un producto simple muestra lo justo; uno completo se despliega —sin huecos ni secciones vacías—.

Conversión integrada

El sidebar sticky mantiene el siguiente paso siempre a la vista: cotizar por WhatsApp, llamar, ver relacionados. La ficha no solo informa: encauza. El CTA se arma con waUrl() y un mensaje que nombra el producto, así el lead llega con contexto.

Anatomía

¿Qué lleva la ficha?

Cuatro piezas: la ruta única [...slug] que la genera, el hero (galería + datos + acción), los bloques opcionales (specs, aplicaciones, certificaciones, FAQ) y el sidebar sticky de conversión.

Cada pieza cumple un papel. La ruta genera la página sin trabajo manual; el hero entrega la primera impresión completa (qué es + cómo se ve + cuánto cuesta); los bloques opcionales despliegan el detalle bajo demanda; y el sidebar mantiene la conversión a la vista. Juntas, convierten un .md en una página de venta.

Abajo, el ejemplo en vivo —el esqueleto de una ficha anotado—. Cada punto numerado se desglosa en su tarjeta: qué resuelve y qué parte de ProductLayout o de la ruta lo respalda.

1

La ruta única — [...slug].astro

Una sola ruta dinámica sirve TODAS las fichas: getStaticPaths recorre la colección y genera una página por producto. No editas una página por producto —el contenido sale del frontmatter—. Renombrar el .md cambia la URL; añadirlo crea la ficha.

Dato src/pages/productos/[...slug].astro · getStaticPaths

2

El hero — galería + datos clave

La cabecera de la ficha: foto principal (fetchpriority high) + miniaturas de la galería a un lado; del otro, badge, H1 con el nombre, descripción, lista de features y el precio. Es lo que el visitante ve sin hacer scroll: identidad + propuesta + acción.

Dato ProductLayout · prod-hero (image · gallery · features · price)

3

Los bloques opcionales

Especificaciones (tabla), aplicaciones, certificaciones/normas y FAQ: cada bloque SOLO se pinta si el frontmatter trae sus datos. Un producto sin specs no muestra esa sección —sin huecos ni placeholders—. Pocos campos base + bloques que aparecen bajo demanda.

Dato specs · applications · certifications · faqs (opcionales)

4

El sidebar sticky de conversión

Una columna que «sigue» el scroll en escritorio: precio (o «bajo cotización»), botón «Pedir cotización» (WhatsApp), «Llamar», y «También te puede servir» (relacionados). El siguiente paso siempre a la vista, sin volver arriba.

Dato prod-sidebar (sticky) · waUrl · telUrl · RelatedLinks

Variantes

De mínima a completa

La misma ficha va desde lo mínimo (hero + descripción + conversión) hasta una completa con galería, especificaciones, FAQ y certificaciones. Cada bloque aparece solo si el frontmatter trae sus datos —ninguna variante toca el layout—.

No hay fichas «cortas» y «largas» como plantillas distintas: hay un layout que se despliega según lo que trae cada producto. Un repuesto va mínimo; un equipo técnico suma specs; uno normado añade certificaciones y FAQ. El layout es el mismo; cambia qué campos opcionales llena el .md.

Abajo, cinco configuraciones —todas reales del layout actual, obtenidas poniendo o quitando campos opcionales—. Cada una con el tipo de producto donde encaja.

  • Ficha mínima

    Producto simple · Arranque

    Solo los campos base: hero (foto + título + descripción + precio), cuerpo Markdown y el sidebar de conversión. Sin specs, sin galería, sin FAQ. Válida y completa para un producto que no necesita más.

  • Con especificaciones

    Técnico · Industrial

    Añade specs[] {label, value}: la ficha pinta una tabla de especificaciones. Para productos donde los números deciden (medidas, materiales, capacidades). El bloque aparece solo porque hay datos.

  • Con galería

    Visual · Equipos

    gallery[] añade miniaturas junto a la foto principal en el hero. Para productos que se entienden mejor desde varios ángulos. La card del catálogo sigue usando una sola imagen.

  • Con FAQ y certificaciones

    Confianza · Normado

    faqs[] pinta el acordeón (y FAQPage en el schema); certifications[] lista normas. Resuelven objeciones y comunican cumplimiento en la propia ficha. Ambos opcionales, ambos bajo demanda.

  • Bajo cotización (sin precio)

    B2B · Proyecto

    Sin price, el hero y el sidebar muestran «Precio bajo cotización» y el CTA va directo a WhatsApp. La ficha completa funciona igual; solo cambia cómo se comunica el precio (ver la pieza «El precio»).

Responsive y móvil

La ficha, en el teléfono

La ficha es de dos columnas en escritorio (contenido + sidebar sticky) y se apila a una en móvil. El hero hace lo mismo (galería + datos lado a lado → apilados), y los botones de acción pasan a full-width.

La ficha se diseña en dos columnas en escritorio —el contenido a la izquierda, el sidebar de conversión a la derecha «siguiendo» el scroll—. En el teléfono, donde no caben dos columnas, todo se apila a una: primero el contenido, luego el bloque de conversión, a ancho completo. El sidebar deja de ser sticky y pasa a ser una sección más.

El hero hace el mismo movimiento: galería y datos van lado a lado en pantalla ancha y se apilan en móvil (foto arriba, datos y precio debajo), con los botones de acción a ancho completo en la zona del pulgar. Abajo, los dos patrones con su receta.

1 · Dos columnas → una (sidebar apilado)

En escritorio: contenido + sidebar sticky que sigue el scroll. En móvil se apila a una columna —contenido y luego conversión, a ancho completo— y el sidebar deja de ser sticky.

CSS · 2 columnas → 1 + sidebar sticky
/* EL SIDEBAR: STICKY EN ESCRITORIO, APILADO EN MÓVIL
   En escritorio la columna de conversión "sigue" el scroll (sticky);
   en el teléfono se apila bajo el contenido, a ancho completo. */
.prod-body { display: grid; grid-template-columns: 1fr; gap: var(--space-8); }

@media (min-width: 1024px) {
  .prod-body { grid-template-columns: minmax(0, 1fr) 22rem; }
  .prod-sidebar { position: sticky; top: calc(var(--header-height) + 1rem); }
}

2 · El hero se apila, los botones a full-width

El hero (galería + datos) va lado a lado en escritorio y se apila en móvil: foto arriba, datos y precio debajo. Los botones de acción pasan a full-width en la zona del pulgar.

CSS · hero de ficha responsive
/* EL HERO DE LA FICHA: 2 COLUMNAS → 1 EN MÓVIL
   La galería y los datos van lado a lado en escritorio; en el
   teléfono se apilan (foto arriba, datos debajo). Los botones de
   acción pasan a full-width en la zona del pulgar. */
.prod-hero__grid { display: grid; grid-template-columns: 1fr; gap: var(--space-6); }
@media (min-width: 1024px) { .prod-hero__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .prod-hero__actions .btn { width: 100%; } }

Posición

¿Dónde vive?

La ruta en src/pages/productos/[...slug].astro (genera todas las fichas); el layout en src/layouts/ProductLayout.astro (la estructura, una vez). El contenido sale de cada .md de la colección.

La ficha se reparte en dos archivos que no se tocan por producto. La ruta dinámica src/pages/productos/[...slug].astro contiene el getStaticPaths que recorre la colección y, por cada producto, pasa su data a ProductLayout. El layout src/layouts/ProductLayout.astro contiene toda la estructura —hero, bloques, sidebar, schema— una sola vez para las fichas del sitio entero.

El contenido, en cambio, vive distribuido: un .md por producto en src/content/productos/. Crear una ficha es escribir ese .md; la ruta y el layout ya existen y la generan. Esta pieza cierra el flujo de la guía: la colección define el dato, las categorías lo ordenan, las imágenes lo ilustran, el precio lo tarifa, y la ficha es la página donde todo eso se presenta al visitante.

Implementación

Cómo se construye

La ruta [...slug].astro con getStaticPaths (una página por producto), el paso de props a ProductLayout (frontmatter + cuerpo en el slot), y el patrón de bloques opcionales (solo se pintan con datos).

La ruta es el corazón: getStaticPaths recorre getCollection('productos'), y por cada producto devuelve { params: { slug }, props: { producto, related } }. Astro genera una página estática por cada uno en build. El cuerpo Markdown se obtiene con render(producto) y se inyecta en el slot de ProductLayout como <Content />.

ProductLayout recibe pocos campos base (title, description, image, price, faqs, related) y un puñado de bloques opcionales (specs, applications, certifications). Cada bloque se renderiza tras un guard (specs.length > 0 && …), así que un producto sin ese dato simplemente no genera la sección. Abajo, las tres recetas: la ruta, el paso a layout y los bloques.

[...slug].astro · una ruta genera todas las fichas
---
// src/pages/productos/[...slug].astro — UNA ruta sirve TODAS las fichas.
import { getCollection, render } from 'astro:content'
import ProductLayout from '@layouts/ProductLayout.astro'

export async function getStaticPaths() {
  const productos = await getCollection('productos', ({ data }) => !data.draft)
  return productos.map((producto) => {
    // Relacionados = hermanos de la colección (excluye el actual).
    const related = productos
      .filter((p) => p.id !== producto.id).slice(0, 3)
      .map((p) => ({ label: p.data.title, href: `/productos/${p.id}`, description: p.data.description }))
    return { params: { slug: producto.id }, props: { producto, related } }
  })
}

const { producto, related } = Astro.props
const { Content } = await render(producto)   // el cuerpo Markdown → HTML
const d = producto.data
---
Astro · pasar el frontmatter a ProductLayout
<!-- La ficha pasa el frontmatter a ProductLayout; el cuerpo va en el slot. -->
<ProductLayout
  slug={producto.id}
  title={d.title}
  description={d.description}
  category={d.category}
  image={d.image}
  badge={d.brand}
  price={d.price}
  faqs={d.faqs}
  related={related}
>
  <Content />   <!-- descripción larga en Markdown → sección "Acerca de" -->
</ProductLayout>
ProductLayout · bloques opcionales (solo con datos)
<!-- ProductLayout — los bloques OPCIONALES solo se pintan si traen datos. -->
{specs.length > 0 && (
  <section class="prod-section">
    <SectionHeading eyebrow="Detalle" title="Especificaciones" />
    <table class="prod-table">
      <tbody>{specs.map((s) => <tr><th>{s.label}</th><td>{s.value}</td></tr>)}</tbody>
    </table>
  </section>
)}

{faqs.length > 0 && (
  <section class="prod-section">
    <SectionHeading eyebrow="Dudas" title="Preguntas frecuentes" />
    <FAQAccordion items={faqs} />
  </section>
)}
<!-- Sin specs/faqs → el bloque NO existe en el HTML. Cero huecos. -->

En concreto: getStaticPaths() mapea la colección a rutas; cada una recibe props con el producto y sus relacionados. La página llama render(producto) para obtener Content (el cuerpo Markdown ya en HTML) y lo pasa al slot de ProductLayout. La estructura —hero, tabla, sidebar— vive en el layout, no en la ruta.

ProductLayout generaliza el patrón «pocos campos base + bloques opcionales»: evita el god-layout de 40 props. Los bloques (specs, applications, certifications, faqs) se pintan tras un length > 0, y el sidebar de conversión arma sus CTAs con waUrl() y telUrl() desde site.ts. El schema (Product + Offer) lo emite el propio layout vía buildSchema('product', …) — tema de la pieza «El schema».

Buenas prácticas

Qué hacer y qué evitar

Una ficha sana cabe en unos hábitos: dejar que [...slug] la genere, llenar solo los bloques con datos reales, usar el Markdown para la descripción larga y el sidebar para la conversión. El layout hace lo repetitivo.

Ninguno de estos hábitos es opcional si quieres un catálogo mantenible. La regla de oro es no crear páginas .astro por producto: la ruta dinámica las genera todas. Y no rellenar bloques con datos de relleno: los opcionales se ocultan solos, y una sección vacía honesta es mejor que una llena de placeholders.

La buena noticia es que el layout hace el trabajo pesado: la estructura, el schema y la conversión están resueltos una vez para todas las fichas. Lo que aportas tú es el contenido del .md, bien escrito y solo con lo que aplique. Abajo, lo que conviene y lo que conviene evitar.

Sí conviene

  • Deja que [...slug].astro genere las fichas: no creas una página por producto, solo el .md. La ruta y el layout hacen el resto.
  • Llena solo los bloques que aporten: un producto sin specs no necesita la tabla. Los bloques opcionales se ocultan solos si no hay datos.
  • Usa el cuerpo Markdown del .md para la descripción larga: se renderiza dentro de la ficha (sección «Acerca de»), con listas, tablas y formato.
  • Aprovecha el sidebar sticky para la conversión: precio + WhatsApp + llamar. El mensaje pre-armado de waUrl() ya nombra el producto.
  • Cura los relacionados con relatedProducts (reference()): el bloque «también te puede servir» enlaza fichas reales, validadas en build.

Mejor evita

  • NO crees una página .astro por producto: rompe la regla D1 y duplica el layout. Toda ficha sale de [...slug].astro + el .md.
  • NO rellenes bloques con datos de relleno «para que no se vean vacíos»: si no hay specs reales, omite el campo y el bloque no aparece. Vacío honesto > relleno.
  • NO metas el HTML del layout en el .md: el frontmatter son datos; la estructura (hero, sidebar, tabla) vive en ProductLayout, una vez para todas las fichas.
  • NO dupliques el CTA de WhatsApp con enlaces a mano: el layout ya lo arma con waUrl() y el número de site.ts (regla D4).
  • NO uses la ProductCard del catálogo para los «relacionados» de la ficha: ese papel es de RelatedLinks (tiles simples), para no anidar cards de catálogo dentro de la ficha.
¿Necesitas ayuda?