Saltar al contenido
pcreative Commerce

Crear un tema

Un tema es una aplicación web independiente. No corre dentro de la tienda: habla con ella por HTTP, como lo haría cualquier cliente.

Un tema es una aplicación web independiente. No corre dentro de la tienda: habla con ella por HTTP, como lo haría cualquier cliente.

Por eso puedes usar el stack que quieras. Hay esqueleto para seis —Next, Astro, SvelteKit, Vue (Nuxt), React Router y Vite + React— y el contrato es el mismo en todos.


1. Empezar

npx @pcreative/theme-contract init mi-tema --name "Mi Tema" --stack astro
cd mi-tema

--stack acepta next, astro, sveltekit, nuxt, react-router y vite-react. No hace falta instalar nada antes: npx se baja la herramienta, crea el tema y el propio tema la trae después como dependencia, así que a partir de ahí basta con npx pcc-theme.

Eso no crea una carpeta vacía: crea un tema que ya habla con la tienda. Portada componible, catálogo paginado en servidor, ficha de producto, categoría, carrito, blog y páginas sueltas, todo contra el comercio de verdad.

Lo que no trae, para que no te pille por sorpresa:

  • La página de pago. El carrito lo dice donde antes el botón apuntaba a una página que no existía: ahora el botón vuelve al catálogo, y la nota de al lado nombra las llamadas del contrato que resuelven una caja (setEmail, setAddresses, listShippingOptions, setShippingMethod, listPaymentMethods, selectPaymentMethod, complete). Cuando la montes, decláralas en capabilities.pages junto con order-confirmation: el esqueleto deja las dos fuera, porque declarar una página que no existe manda al instalador a un 404 y al editor a componer algo que nadie pinta.
  • Más de un idioma. El esqueleto sale en un solo idioma: inglés, salvo que tu terminal esté en español (LANG=es…), y entonces sale en español. Trae una pareja de ficheros de textos y ninguna ruta /en o /es; añadir un segundo idioma es trabajo tuyo.

Cuál elegir. Los cinco primeros renderizan en el servidor. vite-react es estático: el HTML llega vacío hasta que se ejecuta JavaScript, así que un buscador no ve ni una ficha de producto. Para un escaparate público es la elección equivocada; para una tienda dentro de una aplicación, tras una contraseña o en un quiosco, es perfecta.

2. Verlo funcionando

Necesitas una tienda con la que hablar, y la de verdad cabe en tu ordenador. Descarga pcreative Commerce desde pcreativecommerce.dev y arráncalo con el instalador que viene al lado del paquete:

sh instalar.sh pcreative-commerce-*.zip

Lo descomprime, crea .env con una contraseña aleatoria para la base de datos y lo arranca. Si prefieres hacerlo a mano, la contraseña no es opcional: sin POSTGRES_PASSWORD en .env la base de datos se niega a arrancar.

unzip pcreative-commerce-*.zip && cd pcreative-commerce-*/
cp .env.docker.example .env      # y rellena POSTGRES_PASSWORD
docker compose up -d

Viene construida: no compila nada. Abre el panel en http://localhost:7001/admin, completa el asistente y deja que meta el catálogo de ejemplo. La clave publicable que necesita tu tema está aquí:

docker compose exec backend cat /app/data/clave-publicable.txt

Luego, en tu tema:

cp .env.example .env.local     # y pon la URL y la clave publicable
npm install
npm run dev

Sin la clave publicable tu tema arranca y no ve ni un producto, y el error no dice que falte una clave. Es el fallo número uno al empezar.

3. Lo que ya viene hecho

mi-tema/
  theme.json              quién eres y qué traes
  tokens.json             colores, tipos, espacios — en formato estándar (W3C DTCG)
  settings.schema.json    qué se puede personalizar desde el panel
  settings.json           los valores de fábrica
  sections.schema.json    qué secciones sabes pintar
  templates/*.json        cómo vienen compuestas tus páginas
  locales/                los textos, en dos ficheros (ver abajo)
  demo/contenido.json     productos de ejemplo
  src/                    tu código

Los idiomas van en dos ficheros y no en uno. en.json son los textos del escaparate —los ve quien compra— y en.schema.json los del personalizador —los ve quien lleva la tienda— (es.json y es.schema.json si el esqueleto salió en español). Casi todo el mundo hace solo el primero, y por eso una agencia entrega una web perfecta en francés con el panel en español.

4. Diseñar

Los colores no se escriben en el código. Van en tokens.json y llegan al navegador como variables CSS. Un componente que escriba un #fff a pelo se queda fuera del personalizador para siempre:

/* sí */    color: var(--color-brand-primary);
/* no */    color: #1f5f57;

Lo que se puede tocar sin programar se declara en settings.schema.json. El panel genera la pantalla solo a partir del esquema: no escribes interfaz.

Las secciones son los bloques que se recomponen desde el editor. Tu tema declara cuáles sabe pintar en sections.schema.json, y las tres reglas de una sección son:

  1. Se pinta en cualquier posición y repetida.
  2. Sobrevive a que le falte todo — un ajuste vacío no puede tumbar la página.
  3. Lleva data-pcc-seccion con su identificador. El esqueleto lo pone en un solo sitio a propósito, para que no se pueda olvidar en la sección número nueve. Con cualquier otro nombre de atributo se pinta perfecta y el editor no la ve.

5. Secciones con JavaScript

Cuando alguien cambia un ajuste, el editor sustituye el HTML de esa sección y deja el resto de la página en paz. Eso tiene dos consecuencias que no dan ningún error.

Los ciclos de vida de tu framework no se disparan. onMount, useEffect, onMounted: ninguno. El HTML entra ya hecho. Hay dos eventos para eso, que salen sobre el nodo de la sección y burbujean:

eventocuándopara qué
pcc:seccion:descargadaantes de quitar el nodo viejoparar bucles, quitar observadores, liberar WebGL
pcc:seccion:cargadadespués de poner el nuevovolver a montar carruseles y animaciones
seccion.addEventListener("pcc:seccion:cargada", (e) => montar(e.detail.el))
seccion.addEventListener("pcc:seccion:descargada", (e) => limpiar(e.detail.el))

🔴 Sin escuchar el de descarga, cada edición apila otro juego de escuchadores y otro bucle de animación sobre el anterior. A las diez ediciones la página se arrastra.

Y hay secciones que no se pueden pintar solas. Un héroe que fija el scroll de toda la página, un lienzo WebGL compartido o una línea de tiempo que abarca tres secciones no existen fuera de su página: sustituir su nodo no falla, deja algo pintado y roto. Se declara y el editor recarga la página para esa sección:

{ "type": "hero-cinematografico", "name": "Héroe cinematográfico", "aislable": false }

Por defecto es true, que es lo que quieres en el 95 % de las secciones.

6. Hablar con la tienda

import { createCommerce } from "@pcreative/commerce-contract/api"

const commerce = createCommerce({
  baseUrl: process.env.COMMERCE_URL,
  publishableKey: process.env.COMMERCE_KEY,
  countryCode: "es",
})

El adaptador usa fetch a secas, sin ningún SDK: un SDK presupone navegador, y eso es justo lo que impediría que un tema Astro o un tiempo de ejecución en el borde usaran el mismo código.

🔴 El dinero no se calcula en el tema. El importe y la moneda vienen dados por el comercio; tú los formateas. Un tema que multiplica precio por cantidad acaba enseñando un número creíble y equivocado en cuanto hay impuestos, descuentos o promociones por cantidad.

🔴 Ningún secreto. El tema habla con la API pública y la clave publicable, que es pública. Pedir la base de datos, la Admin API o una pasarela hace que validate falle.

7. Blog y páginas

Vienen puestas: templates/blog.json, blog-post.json y page.json, con sus rutas. Las páginas sueltas —el aviso legal, los envíos, quiénes somos— las escribe quien lleva la tienda desde su panel; tú solo las pintas.

Su contenido se pinta como HTML sin escapar, porque viene del panel de la propia tienda: quien puede escribir ahí ya puede cambiar el escaparate entero, así que no gana ningún permiso por esto. Si algún día pintas HTML de un cliente —una reseña, una pregunta— por esa misma puerta, sanéalo antes.

8. Comprobar

npx pcc-theme validate              # forma y coherencia — tiene que salir SIN AVISOS
npx pcc-theme audit                 # qué se ejecutaría y a qué dominios habla
npx pcc-theme info

Si audit dice «no hay fichero de bloqueo», ejecuta npm install antes. Tiene razón en pedirlo: sin él no se puede saber qué se va a descargar quien instale tu tema.

Sin avisos, no «con pocos». Un tema que se entrega con avisos enseña a ignorar los avisos, y a partir de ahí el validador no sirve para nada.

🔴 Y validar no prueba que compile. De los esqueletos de esta casa, tres pasaban validate sin un solo aviso y no construían — y uno construía y no arrancaba. Ejecuta npm run build antes de entregar nada.

9. Entregar

npx pcc-theme pack
npx pcc-theme pack --key clave.pem --publisher "Tu Estudio"   # firmado

Un comando, y hace las siete cosas que el .zip necesita para que el panel lo acepte: valida, audita, busca secretos en lo que iba a empaquetar, copia a un montaje aparte —tu carpeta no se toca—, mete los contratos dentro, instala y construye de verdad, y escribe el archivo.

🔴 No hagas el .zip a mano. Si tus dependencias apuntan a una carpeta de tu ordenador, el paquete instala en tu máquina y en ninguna otra — y no te enteras hasta que alguien lo sube. pack es justo lo que arregla eso.

Solo entra lo que es parte de un tema: si tienes una carpeta propia, pack te dice por su nombre lo que se queda fuera.

10. Si lo vendes fuera

Declara en theme.json que se vende bajo licencia:

"license_check": { "product": "mi-tema", "gracia": 7 }

Quien lo compre pega su clave en el panel, en la ficha del tema. A partir de ahí:

  • La comprobación va sin red. El permiso viaja firmado y se verifica con la clave pública; solo renovarlo habla con el servidor.
  • La clave se usa una vez y se tira. Lo que queda guardado en la tienda es un permiso que caduca y está atado a ese dominio, no la clave de tu comprador.
  • El escaparate no se apaga NUNCA por una licencia. Lo único que se niega es publicar, con el dueño delante y la tienda anterior intacta. Un servidor de licencias caído no puede dejar a oscuras a quien ya te compró.
  • Comprado en el mercado de pcreative, no se pide clave: la compra ya se comprobó al descargar.

Los errores que se repiten

«0 productos» → falta la clave publicable, o apunta a otro canal de venta.

El editor no ve una sección → el atributo se llama data-pcc-seccion. Con data-seccion se pinta perfecta y el editor no la encuentra.

La sección se pinta y está muerta → el JavaScript no se reejecuta al sustituirla. Escucha pcc:seccion:cargada.

La página se arrastra a las diez ediciones → falta escuchar pcc:seccion:descargada y se están apilando escuchadores.

El .zip no instala en el servidor de quien lo compró → se hizo a mano. Usa pack.

Los colores del panel no cambian nada → hay un color escrito en el código en vez de un token.

Construye en tu portátil y muere en el servidor → next/font/google descarga las fuentes mientras construye, y el panel construye los temas sin red. En tu portátil ya están en caché, así que no lo ves nunca. Mete los .woff2 dentro del tema y cárgalos con next/font/local, con la licencia al lado. audit lo bloquea.

Y cuando esté terminado

Todo lo que viene después de «funciona» —empaquetarlo y firmarlo como es debido, publicarlo en el mercado, venderlo fuera, qué hace que te tumben la ficha y qué hacen de verdad las licencias— está en Vender el tema que has hecho.

Ver también