Skip to main content
Recipe number 174

Cookbook · Chapter 12 · Textbooks

A puzzle page with the answers upside down

A children's magazine puzzle page: crossword, word search, riddle, story to order, matching and a mixed-up fact on cards, each with its answers upside down.

On this page
Output
Canvas · PDF
Postext
Tested with Postext 1.59.0
Needs ≥ 1.59.0 · postext-pdf ≥ 1.59.0
Licence
Updated 24 Oct 2026
Code MIT · Text CC BY 4.0
  • Trim 210 × 280 mm
  • 2 columns, 7 mm gutter
  • Nunito 10.5/15
  • Baloo 2
  • Fredoka
  • 2 pages
  • Level
  • Postext 1.59.0
  • Laid out in 71 ms
  • 129 lines of code

In short

The puzzle page of a children's magazine about the sea. Each puzzle sits on its own card, and its answers are printed upside down underneath, so readers turn the magazine round to check.

What you'll build

The puzzle page of Splash!, a children's magazine about the sea, on two A4-sized pages in two columns. A reef painting fills the top of the first page with the title over its open water. Below it, each puzzle sits on a cream card with a coral number: a crossword built from six sea creatures and their clues, a word search that hides eight words across, down, diagonally and backwards, a riddle with three options to ring, a turtle's life to put in order, a mixed-up fact to unscramble and baby animals to join with their names. Under every puzzle, inside its card, the answers are printed upside down in small grey type, the way puzzle magazines have always kept them out of sight.

This recipe answers

  • How do I make a word search or a crossword from a list of words?
  • How do I print the answers upside down at the foot of an exercise?

The short answer

script.js · lines 33–59in full code
// placement 'inverted' sets each puzzle's key after it in a box turned 180°, inside the
// puzzle's card, so a page break never parts a puzzle from its answers. The key is made
// from the puzzle: the crossword's words by number, the ringed option, the right order.
const puzzle = {
  id: 'puzzle',
  box: 'card', // the card each puzzle sits on (a calloutStyles id)
  number: { shape: 'disc', fontFamily: DISPLAY, background: col('coral') },
  instruction: { fontFamily: TEXT, bold: false },
  options: { marker: 'circle', labels: 'none' }, // a ring to colour in, no letters
  grid: { fontFamily: LABEL, rule: col('sea'), cell: em(1.45) },
  match: { color: col('sea') },
  area: { kind: 'lines', color: col('sea') },
  answers: { color: col('coral'), fontFamily: LABEL },
  solutions: { placement: 'inverted', box: 'turned' },
  marginTop: mm(0), marginBottom: mm(0),
};
const card = { id: 'card', title: '', background: col('card'), borderRadius: mm(4),
  padding: { top: mm(3.5), right: mm(4), bottom: mm(3), left: mm(4) },
  titleStyle: { fontFamily: DISPLAY, fontSize: pt(15), fontWeight: 700, color: col('ink') },
  body: { fontFamily: TEXT, fontSize: pt(10.5), lineHeight: pt(15), color: col('ink'),
    firstLineIndent: pt(0) },
  marginTop: mm(0), marginBottom: mm(5), keepTogether: true };
const turned = { id: 'turned', title: '', backgroundEnabled: false, border: { enabled: false },
  padding: { top: pt(0), right: pt(0), bottom: pt(0), left: pt(0) },
  body: { fontFamily: LABEL, fontSize: pt(8.5), lineHeight: pt(11), color: col('muted'),
    firstLineIndent: pt(0) },
  marginTop: mm(2), marginBottom: mm(0) };

Every puzzle on a card, its answers turned upside down under it

Ingredients

Type
Baloo 2, Nunito, Fredoka (SIL OFL 1.1)
Assets
  • hatchling-600.jpg
  • reef-1600x792.jpg
  • A coral reef with a smiling sea turtle, an octopus, a clownfish, a pufferfish and a seahorse under sunbeams. (Generated With Diffusion Models, original)
  • A baby sea turtle hatching from its egg on the sand. (Generated With Diffusion Models, original)

Method

#1 · Every puzzle on a card, its answers turned

The code is the short answer above. The exercise style's box names a box style, so every puzzle is set on a card, its number in a disc and its title as the first line of its instruction. placement: 'inverted' sets the key after each puzzle in a box turned 180°, inside the card, so the two never fall on different pages; the grids and the writing line stay inside the card's padding.

#2 · Let the engine build the grids

The Markdown holds only the words: - OCTOPUS: Eight arms and three hearts for the crossword, - SHELL for the word search. The engine lays the crossword with every word crossing another and numbers the starts in reading order, and hides the search words in the directions the fence names (directions="across down diagonal backwards"), filling the rest with the words' own letters. Both are seeded, so the grid is the same on every run, in every output, and the Spanish edition builds its own grid from its own words.

#3 · A reef across both columns

script.js · lines 63–82in full code
const BAND = 104; // mm from the top edge: the painting's height at the page's width
const opener = { enabled: true, minHeight: mm(BAND - 16 + 6), slot: { elements: [
  { kind: 'image', id: 'reef', resourceId: 'reef', decorative: true, reserve: false,
    placement: { anchor: { to: 'page', edge: 'top-left' },
      size: { width: mm(210), height: mm(BAND) } } },
  { kind: 'text', id: 'kicker', content: '{attr.kicker}', fontFamily: LABEL, fontSize: pt(11),
    fontWeight: 600, color: col('paper'), textTransform: 'uppercase', letterSpacing: pt(2),
    box: { backgroundColor: col('coral'), borderRadius: mm(2),
      padding: { top: mm(0.8), bottom: mm(0.8), left: mm(2.4), right: mm(2.2) } },
    placement: { anchor: { to: 'page', edge: 'top-left' }, offset: { x: mm(16), y: mm(14) } } },
  { kind: 'text', id: 'title', content: '{titleText}', fontFamily: DISPLAY, fontSize: pt(56),
    fontWeight: 800, lineHeight: 1, color: col('paper'),
    placement: { anchor: { to: '#kicker', edge: 'below' }, offset: { x: mm(0), y: mm(3) } } },
  { kind: 'text', id: 'sub', content: '{attr.sub}', fontFamily: TEXT, fontSize: pt(12),
    fontWeight: 700, color: col('paper'), overflow: 'wrap', align: 'left',
    box: { backgroundColor: col('ink'), borderRadius: mm(2),
      padding: { top: mm(1.5), bottom: mm(1.5), left: mm(3), right: mm(3) } },
    placement: { anchor: { to: '#title', edge: 'below' }, offset: { x: mm(0), y: mm(2) },
      size: { width: mm(110) } } },
] } };

The opener spans both columns (span: 'page'), so the puzzles start under the painting in each column. The picture is cut to the band's proportions and anchored to the page, so it bleeds at the top and the sides; the standfirst sits in a dark box so it reads over the water.

The whole recipe

Sandbox
// ═══ Postext Cookbook · Nº 174 · A puzzle page with the answers upside down ═══════
// https://postext.dev/en/cookbook/puzzle-page-upside-down-answers
// Code: MIT · Text: original (CC BY 4.0) · Pictures: generated with diffusion models
// Fonts: Baloo 2, Nunito, Fredoka (SIL OFL 1.1) · Needs postext ≥ 1.59.0
import {
  buildDocumentWithFonts, renderPageToCanvas, registerResourceImage,
} from 'https://esm.sh/postext';
import { renderToPdf, decompressWoff2 } from 'https://esm.sh/postext-pdf';

const LANG = 'en'; // @lang: the language of the sample document ('en' | 'es')
const RECIPE = 'puzzle-page-upside-down-answers';

// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: reef colours: deep blue ink, coral, sea green, a sandy paper
const palette = {
  ink: '#16324a', // text: a deep sea blue
  coral: '#e8573f', // the accent: puzzle numbers, the title kicker
  sea: '#0f8b8d', // the second: crossword and word-search rules, the riddle's rings
  sun: '#ffc93c', // the turtle card's badge
  card: '#fff4e0', // the puzzle cards
  muted: '#5d7180', // answers, colophon, folio
  paper: '#ffffff',
};
const col = (id) => ({ hex: palette[id], model: 'hex', paletteId: id });
const colorPalette = [
  ...Object.entries(palette).map(([id, hex]) => ({ id, name: id, value: { hex, model: 'hex' } })),
  { id: 'main-color', name: 'accent (defaults)', value: { hex: palette.coral, model: 'hex' } },
];
// #endregion
const [DISPLAY, TEXT, LABEL] = ['Baloo 2', 'Nunito', 'Fredoka'];

// #region answer: every puzzle on a card, its answers turned upside down under it
// placement 'inverted' sets each puzzle's key after it in a box turned 180°, inside the
// puzzle's card, so a page break never parts a puzzle from its answers. The key is made
// from the puzzle: the crossword's words by number, the ringed option, the right order.
const puzzle = {
  id: 'puzzle',
  box: 'card', // the card each puzzle sits on (a calloutStyles id)
  number: { shape: 'disc', fontFamily: DISPLAY, background: col('coral') },
  instruction: { fontFamily: TEXT, bold: false },
  options: { marker: 'circle', labels: 'none' }, // a ring to colour in, no letters
  grid: { fontFamily: LABEL, rule: col('sea'), cell: em(1.45) },
  match: { color: col('sea') },
  area: { kind: 'lines', color: col('sea') },
  answers: { color: col('coral'), fontFamily: LABEL },
  solutions: { placement: 'inverted', box: 'turned' },
  marginTop: mm(0), marginBottom: mm(0),
};

The composed script.js runs as it is: paste it into any page’s module script, or open the recipe on CodePen. Recipe folder on GitHub ↗ (opens in a new tab)

Variations

#Hide the word list

A harder word search prints only the grid: add hidden to the fence.

-:::exercise{type=wordsearch size=9 directions="across down diagonal backwards"}
+:::exercise{type=wordsearch size=9 directions="across down diagonal backwards" hidden}

chapterEnd collects every key at the end instead of turning each one.

-  solutions: { placement: 'inverted', box: 'turned' },
+  solutions: { placement: 'chapterEnd' },

Pitfalls

  • A crossword word that crosses no other is left out and reported as exerciseWordUnplaced. Choose words that share letters, or add one that links two groups.
  • A puzzle card is kept together, so a tall crossword moves to the next column when it does not fit: check the Spanish edition, whose words are longer.

Credits

Text
Original prose, CC BY 4.0
Images
  • A coral reef with a smiling sea turtle, an octopus, a clownfish, a pufferfish and a seahorse under sunbeams. · Generated With Diffusion Models · original
  • A baby sea turtle hatching from its egg on the sand. · Generated With Diffusion Models · original
Fonts
Baloo 2 (SIL OFL 1.1) · Nunito (SIL OFL 1.1) · Fredoka (SIL OFL 1.1)
Sandbox