# Páginas compuestas

> El Markdown de las páginas compuestas: la valla de página de cuatro dos puntos que nombra una plantilla de página, los marcos y el atributo slot que llevan el contenido a los marcos de la plantilla, las figuras con rótulos en las anclas de una imagen y los avisos que comprueban el contenido frente a la plantilla

- Versión HTML: https://postext.dev/es/docs/composed-pages
- Última actualización: 2026-10-16
- Tiempo de lectura: 9 min
- Otros idiomas: [en](https://postext.dev/en/docs/composed-pages.md), [ca](https://postext.dev/ca/docs/composed-pages.md), [de](https://postext.dev/de/docs/composed-pages.md), [fr](https://postext.dev/fr/docs/composed-pages.md), [it](https://postext.dev/it/docs/composed-pages.md), [pt](https://postext.dev/pt/docs/composed-pages.md), [zh](https://postext.dev/zh/docs/composed-pages.md), [ja](https://postext.dev/ja/docs/composed-pages.md), [ar](https://postext.dev/ar/docs/composed-pages.md)

## En pocas palabras

Algunas páginas no son una columna de texto corrido, sino un conjunto de cajas, como la página de un libro de texto escolar con una banda para el título, un texto y tres cajas de colores con fotos. Postext las llama páginas compuestas. La forma de la página es una plantilla de la configuración, y el Markdown solo dice qué trozo de texto va en cada caja. Esta página enseña a escribir ese Markdown: la valla de la página, los marcos, los nombres de slot y las imágenes con rótulos, como los nombres de un mapa. También recoge los avisos que da Postext cuando el texto y la plantilla no casan.

**La plantilla guarda la forma de la página; el Markdown guarda su contenido y dice a qué marco va cada trozo.**

Una página compuesta se maqueta a partir de una plantilla: marcos con nombre (una banda de título, un texto, una caja con su foto), grupos que se mueven juntos y una decoración, todo ello definido en la configuración, en `pageTemplates`. El Markdown de la página es un bloque con valla que nombra su plantilla y vierte el contenido en los marcos por su nombre. En el Markdown nunca se escribe una posición ni un tamaño, y en la configuración nunca se escribe texto del contenido, así que una traducción es la misma plantilla con otras palabras.

Esta página trata del Markdown y de cómo se compone la página a partir de él. La plantilla en sí (sus marcos, tamaños, estilos y decoración) se describe en [Configuración › Páginas compuestas](https://postext.dev/es/docs/configuration-composed-pages.md). Un documento sin páginas compuestas se maqueta exactamente igual que antes.

## La valla de página

```md
::::page{template="tema-cajas" fondo="banda-retos"}
# La actividad económica y el dinero

Las personas utilizan :chip[recursos]{style="clave"} que se obtienen de la naturaleza…
::::
```

Una página compuesta se abre con una línea de **cuatro dos puntos** seguida de `page` y sus atributos, y se cierra con una línea que solo lleva el **mismo número de dos puntos**. Dentro, los contenedores de tres dos puntos (`:::frame`, `:::callout`, `:::figure`, `:::columns`, `:::paper`) se abren y se cierran como siempre, así que un `:::` suelto cierra el más interior de ellos y nunca la página. Una página escrita con cinco dos puntos (`:::::page`) se cierra con cinco, y una línea de cuatro dos puntos dentro de ella pasa a ser texto normal.

| Atributo | Efecto |
| --- | --- |
| `template="…"` | Obligatorio. El id de una entrada de `pageTemplates`. |
| `spread` | La página es una doble página que ocupa dos páginas enfrentadas; la plantilla también tiene que ser de doble página. |
| `id="…"`, `#id` | Un ancla: un `:ref` que apunta a ella imprime la página en la que cae la página compuesta. |
| cualquier otro | Un valor que la plantilla lee como `{attr.name}`, por ejemplo una imagen de fondo elegida página a página: `fondo="banda-retos"` con `resourceId: "{attr.fondo}"` en la decoración. |

Una página compuesta empieza siempre una página nueva (o una doble página), y el texto que sigue a su valla de cierre empieza en la siguiente.

El `:::page{split=…}` de tres dos puntos es una página de cómic (véase [Cómics](https://postext.dev/es/docs/comics.md)) y conserva ese sentido. Un `:::page` de tres dos puntos que lleva `template=` se sigue leyendo como página de cómic, y el aviso `pageFenceLength` indica que una página compuesta necesita cuatro dos puntos.

Cuando falta una valla, el analizador se recupera sin perder texto:

- una página que se queda abierta llega hasta el final del capítulo y se señala como contenedor sin cerrar en su línea de apertura;
- una línea `::::page` que aparece con una página abierta cierra antes ahí la página abierta (y se señala de la misma manera);
- la valla de cierre cierra cualquier marco o caja que siga abierto dentro de la página, y señala cada uno;
- una valla de cierre con otro número de dos puntos no es la de la página: se queda en el texto, a la vista.

## Marcos y slots

```md
:::frame{slot="dinero" label="El dinero"}
Es el instrumento utilizado como medio de pago…

Hasta hace pocos años, lo más normal era pagar con dinero en **efectivo**…
:::
```

Un **slot** es el nombre que pide un trozo de contenido, y coincide con el nombre de un marco de la plantilla. `:::frame{slot="…"}` envía a ese marco todo lo que contiene. Su cuerpo es Markdown completo: párrafos, listas, encabezados, chips, inserciones `::resource`, referencias `:ref`, notas al pie, `:::columns` y recuadros anidados. `label` y `title` ponen las palabras de la pestaña o del título del marco, como en un `:::callout`; el aspecto del marco sale del estilo de recuadro que le asigna la plantilla.

Un bloque suelto también puede nombrar su propio slot:

| Bloque | Cómo se escribe |
| --- | --- |
| Un encabezado | `## Retos {slot="retos-titulo"}` |
| Una inserción de recurso | `::resource{id="dinero-pago" slot="dinero-fotos"}` (los dos atributos, en cualquier orden) |
| Un listado de código | `````js {slot="codigo"}`` |
| Un recuadro o cualquier otro contenedor | `:::callout{type="nota" slot="aside"}`: el recuadro entero va al slot |
| Una figura | `:::figure{resource="mapa" slot="mapa"}` (véase más abajo) |

Cómo se reparte el contenido:

- Varios trozos con el mismo slot se juntan en el orden del Markdown.
- El contenido sin slot va al **marco de flujo** de la plantilla (el que lleva `flow`).
- Un encabezado sin slot va primero al marco que toma su nivel (`takes: "h1"`), si la plantilla tiene alguno, así que el título de un tema de un libro de texto es un simple `# Título` al principio de la página.
- El contenido que nombra un slot que la plantilla no tiene va al marco de flujo y se señala (`slotUnknown`).

Un `slot` fuera de una página compuesta se lee y no cambia nada.

## Figuras con rótulos

```md
:::figure{resource="mapa-europa" slot="mapa"}
::label[OCÉANO ATLÁNTICO]{at="atlantico" style="mar"}
::label[**Madrid**]{at="madrid"}
::label[Mar Negro]{at="62% 48%"}
:::
```

`:::figure{resource="…"}` coloca un recurso, con su pie y su número, como hace `::resource`, y enumera los rótulos que van sobre la imagen: una línea `::label[TEXTO]{…}` por rótulo, con líneas en blanco permitidas entre ellas.

- `at` nombra un ancla del recurso (un punto que se define una sola vez sobre la imagen y que comparten todos los idiomas), o da un punto en porcentajes de la imagen, `at="x% y%"`, para un rótulo sin ancla.
- `style` nombra un estilo de rótulo y se impone al del ancla.
- El texto entre corchetes es Markdown en línea: negrita, cursiva, chips.

Una figura funciona tanto en el texto corrido como en un marco. Los rótulos se leen y se guardan junto con su posición en el Markdown; dibujarlos sobre la imagen llegará en una versión posterior, y hasta entonces la figura se compone igual que su recurso. Una línea dentro de una figura que no es un rótulo ni está en blanco termina una figura a la que le falta el `:::` de cierre, y se señala como contenedor sin cerrar.

## Ejemplo

La página de un libro de texto escolar, con una banda de título, un texto con su lista y tres cajas de colores, cada una con su foto al lado:

```md
::::page{template="tema-cajas" fondo="banda-retos"}
# La actividad económica y el dinero

## La actividad económica

Las personas utilizan :chip[recursos]{style="clave"} que se obtienen de la naturaleza…

- Las :chip[empresas.]{style="clave"} Ofrecen bienes y servicios a cambio de un beneficio.
- Las :chip[personas y familias.]{style="clave"} Adquieren lo que ofrecen las empresas…

:::frame{slot="dinero" label="El dinero"}
Es el instrumento utilizado como medio de pago, ya que su valor es conocido…
:::

::resource{id="dinero-pago" slot="dinero-fotos"}

:::frame{slot="gasto" label="El gasto"}
- **Fijo:** es el que se produce todos los meses…
- **Variable:** puede cambiar según las necesidades…
:::

::resource{id="gasto-compras" slot="gasto-fotos"}

:::frame{slot="ahorro" label="Préstamos y ahorro"}
Si necesitas o quieres comprar algo muy caro…
:::

::resource{id="ahorro-balanza" slot="ahorro-fotos"}
::::
```

El título va al marco que toma `h1`; el subtítulo, el párrafo y la lista, al marco de flujo; cada caja y cada foto, al marco de su slot. La edición en catalán son las mismas líneas con otras palabras: la plantilla, los slots y las fotos no cambian.

## Cómo se compone la página

Una página compuesta ocupa una página propia (con el papel `composed` para el filtro `pages` de los elementos de diseño). La plantilla se elige según el lado en que cae la página, así que se aplica su `variants.recto` o su `variants.verso`. Después se coloca y se llena cada marco, uno tras otro, con la tipografía del resto del libro: los estilos de párrafo y de encabezado, las listas, los chips, las llamadas de nota y el estilo de recuadro del marco.

### Colocar los marcos

Un marco se sitúa en su celda de la rejilla, o donde lo pone su `placement` (un anclaje, un borde y un desplazamiento, como un elemento de diseño), o, si no tiene ninguno de los dos, en la esquina superior izquierda del área de la plantilla y con todo el ancho del área. Un marco anclado a otro (`"to": "#dinero"`) se coloca después de él y se mueve con él: `below` lo cuelga debajo del otro marco, `right-of` lo pone a su lado y una esquina como `top-right` lo sujeta a esa esquina de la caja del otro marco.

Su altura es fija (una longitud o un porcentaje), `fill` (hasta el borde del área) o `auto`, el valor por defecto: la altura del contenido que recibe, entre `minHeight` y `maxHeight`, sin pasar nunca del pie del área. En un marco más alto que su contenido, `verticalAlign` dice dónde queda ese contenido (`top`, `middle`, `bottom`, o `justify` para repartir el espacio entre sus bloques). El estilo de recuadro del marco dibuja el fondo, el borde, las esquinas redondeadas, la banda y el icono al tamaño completo del marco; el `label` de la valla `:::frame` se imprime en la pestaña del estilo.

### El marco de flujo y los encabezados

El contenido que no nombra ningún slot va al marco marcado con `flow`; el primer encabezado de un nivel va al marco que lo toma con `takes`. Los marcos de una página se leen en el orden en que su contenido aparece por primera vez en el Markdown, cada uno entero: un lector de pantalla, el PDF etiquetado y las salidas de maquetación fluida leen una caja después del texto que abre la página, no en medio de él.

### Encadenar marcos

`next` envía lo que un marco no puede contener a otro marco de la misma página, y ese a otro, como los marcos de texto enlazados de la maquetación tradicional:

```json
{ "name": "columna-1", "flow": true, "next": "columna-2", "height": { "value": 120, "unit": "mm" } },
{ "name": "columna-2", "placement": { "anchor": { "to": "#columna-1", "edge": "right-of" } } }
```

El texto se corta entre dos líneas y sigue en el marco siguiente, partido de nuevo al ancho de ese marco, sin perder ni repetir nada. `overflow.continueIn: "#frame"` significa lo mismo. Seguir en la página siguiente está previsto (`"next-page"`).

### Marcos que apartan el texto

Un marco con `exclude: "push"` deja fuera de su rectángulo el texto de los demás marcos: la línea de otro marco que choca con él se compone más corta por el lado que tapa el rectángulo, igual que el texto rodea una imagen en el texto corrido. Una foto anclada a la esquina de una caja y marcada con `push` hace que el texto de la caja corra a su lado. Una caja que deba dejar libre una columna fija para sus imágenes puede usar `padding` en su lugar.

### Decoración, cabeceras y capas

La `decoration` de la plantilla (imágenes, filetes, cajas y textos breves) se coloca cuando los marcos ya tienen su caja definitiva, así que un elemento puede anclarse a un marco (`"to": "#gasto"`) y seguirlo. Sus textos leen los atributos de la propia página: `fondo="banda-retos"` en la valla de página da `{attr.fondo}`, por ejemplo para elegir página a página una imagen de fondo con `"resourceId": "{attr.fondo}"`. Las cabeceras y los pies de página también los leen. El `header` o el `footer` de una plantilla sustituye al de la sección, `false` lo oculta y, si la plantilla no fija ninguno, se imprimen los de la sección.

Los marcos se pintan en el orden de su `layer`: la decoración en la capa 0, los marcos en la 1 por defecto, una capa más alta encima de una más baja y una capa negativa debajo de la decoración. `rotate` gira un marco junto con su contenido, en cualquier ángulo y alrededor del centro del marco, en el lienzo, en el PDF, en el visor HTML y en el EPUB de maquetación fija.

### Contenido que no cabe

Un marco cuyo contenido es más alto que él, sin un marco siguiente donde continuar, conserva todo ese contenido en el documento pero solo muestra lo que cabe: el resto se corta al pie del marco en la salida paginada, y el aviso [`frameOverflow`](https://postext.dev/es/docs/composed-pages.md#frameoverflow) dice cuánto. El contenido que no recoge ningún marco (no hay marco de flujo, o en ese lado falta un marco) se compone en el texto corrido, en la página que sigue a la página compuesta.

## Avisos

Las comprobaciones leen el Markdown y la configuración antes de maquetar nada, así que aparecen en el panel Revisión mientras escribes, en `postext check` y en los `contentWarnings` del documento, cada una con el rango de origen de lo que señala (véase [Configuración › Avisos del documento](https://postext.dev/es/docs/configuration-programmatic-usage.md#avisos-del-documento)).

| Tipo | Causa | Solución |
| --- | --- | --- |
| `templateUnknown` | La valla de la página nombra una plantilla que no está en `pageTemplates`, o no nombra ninguna. Sus slots no se comprueban. | Corrige el id o añade la plantilla a la configuración. |
| `slotUnknown` | Un `slot="…"` no nombra ningún marco de la plantilla de la página (cuentan los marcos heredados con `extends` y los de una página de `variants`). El contenido va al marco de flujo. | Corrige el nombre del slot o añade a la plantilla un marco con ese nombre. |
| `slotEmpty` | Un marco de la plantilla no recibe nada en esta página: ningún slot lo nombra, no toma ningún encabezado y, si es el marco de flujo, no queda contenido sin slot. No se señala un marco que solo recibe lo que otro marco continúa en él (`next`). `postext check` lo lista como nota. | Escribe contenido para ese marco con su slot, o márcalo como `optional: true` en la plantilla. |
| `pageFenceLength` | Un `:::page` de tres dos puntos lleva `template=`. Se lee como página de cómic, igual que antes. | Abre la página con cuatro dos puntos, `::::page{template="…"}`, y ciérrala con `::::`. |
| `unclosedContainer` | Una página compuesta, un marco o una figura no se cierra nunca (véanse las reglas de recuperación de más arriba). El panel Revisión llama `::::page` a la página compuesta. | Añade la valla de cierre: tantos dos puntos como la línea de apertura de la página, o `:::` para un marco o una figura. |

### frameOverflow

Este aviso lo da la maquetación, no las comprobaciones del código fuente de arriba: aparece cuando la página ya está compuesta, en el panel Revisión, en `postext check` y en los `contentWarnings` del documento.

**Causa.** El contenido que entra en un marco (por su slot, o el que recibe el marco de flujo) es más alto que el marco, y el marco no tiene ningún `next` donde seguir. Casi siempre hay más texto del que cabe: una traducción más larga que el original, o un marco sujeto a un `height` o un `maxHeight` fijos. Lo que no cabe se corta al pie del marco en la salida paginada; sigue en el documento, en el mapa de origen y en el EPUB de maquetación fluida. El aviso indica la plantilla, el marco, la página, la altura cortada (`overflowMm`) y `configPath`, el puntero JSON del marco en la configuración (`/pageTemplates/0/frames/2`).

**Solución.** Da más sitio al marco (sube o quita su `height` o su `maxHeight`), encadénalo a otro marco con `next`, acorta el texto o compón la página con otra plantilla. Por ahora la maquetación no reduce el texto para que quepa.

## Desde el código

`composedPages(blocks)` lee los bloques analizados de un capítulo (`parseMarkdownWithIssues(markdown).blocks`) y devuelve un nodo por cada valla de página, en orden: su `template`, `spread`, `attrs` con el rango de origen de cada valor (`attrSources`), su `fenceIndex` (el ordinal de la valla en el capítulo, el mismo en todos los idiomas), `slots` (los índices de los bloques de cada slot, en el orden del Markdown), `unassigned` (los bloques sin slot) y el rango de origen de la página entera. Un bloque lleva el slot que nombra en `ContentBlock.slot`, con el rango de origen del valor; una figura es un `resourceBlock` cuyo `figure` guarda sus atributos y sus rótulos, cada rótulo con su texto, sus tramos en línea y un mapa de origen que remite al Markdown. `checkComposedSlots(pages, config)` devuelve los tres avisos de slot de esas páginas.

Tras la maquetación, el `VDTPage.composed` de una página compuesta registra su plantilla, la variante aplicada, los atributos de la valla de página y, para cada marco, su id (`cp0:dinero`: el ordinal de la valla de página y el nombre del marco, el mismo en todos los idiomas), su caja, su capa, su giro, la altura del contenido que no cupo (`overflowPx`), el marco del que continúa o en el que continúa, su ruta en la configuración (`/pageTemplates/0/frames/2`) y los ids de sus bloques. Esos bloques son bloques de recuadro corrientes en `page.floats` y `doc.blocks`, con sus rangos de origen, así que un clic en un marco sin girar de la vista previa del Sandbox lleva a su Markdown. `composedPaintOrder(page)` da el orden en que un renderizador pinta los marcos y la decoración, y `frameMatrix(frame)`, el giro de un marco.
