En bref
Une page de calcul pour des enfants de neuf ans, en espagnol, en anglais, en français et en allemand. Chaque pays pose la division à sa façon et la fiche suit ; un interrupteur affiche la version de l’enseignant avec toutes les réponses.
Ce que vous allez composer
L’unité 3 d’un cahier de calcul de CM1, Les quatre opérations, en quatre éditions. Une rangée de calcul mental sur trois colonnes, puis des additions, des soustractions et des multiplications posées, un chiffre par case, avec une rangée de cases pour le résultat, enfin des divisions et un problème sur papier quadrillé. C’est à la division que les éditions se séparent : la fiche anglaise met le diviseur à l’extérieur du crochet ⟌ et le quotient au-dessus ; l’espagnole et la française placent le diviseur dans une potence à droite du dividende et le quotient dessous ; l’allemande écrit 864 : 4 = sur une ligne, avec de la place dessous pour les calculs intermédiaires, et note ses produits avec un point. La virgule ou le point décimal du calcul mental suit aussi la langue. Un interrupteur au-dessus des pages change la fiche de l’élève en version de l’enseignant, chaque réponse imprimée en orange dans sa case.
Cette recette répond à
- Comment composer la division posée comme l'écrivent les écoles de chaque pays ?
- Comment tirer le livre du professeur, réponses imprimées à leur place, du livre de l'élève ?
La réponse courte
// `- 864 : 4` in the Markdown is one operation; the style sets it digit by digit in squares
// of 7.2 mm, a cell for each figure of the answer. Long division follows the country: the
// divisor in an L right of the dividend in Spain and France (potence), `864 : 4 =` on one
// line in Germany, the bracket ⟌ in English-speaking schools.
const calculo = {
id: 'calculo',
number: { shape: 'disc', background: col('accent'), fontFamily: LABEL, fontWeight: 700 },
instruction: { bold: true, color: col('ink') },
grid: { cell: mm(9), fontFamily: TEXT, fontSize: pt(15), rule: col('rule'),
color: col('ink') },
arithmetic: { columns: 4,
division: t({ es: 'potence', fr: 'potence', de: 'inline', en: 'bracket' }) },
blank: { look: 'box', width: em(3.2), color: col('rule'), weight: pt(0.8) },
area: { kind: 'grid', spacing: mm(5), color: col('rule'), weight: pt(0.4) },
answers: { color: col('accent'), fontFamily: LABEL }, // the teacher's copy, in the accent
marginTop: mm(7), marginBottom: mm(2), itemSpacing: em(0.5),
};
Ingrédients
- Fonctionnalités
- Opérations poséesLivre du professeurStyles d'exerciceExercicesTextes à trous et banques de motsQuestions ouvertes et zones d'écritureOuvertures composéesAttributs de titrePalette de couleurs sémantiqueCésure et langue du documentPages sur un canvasExport PDFStyles de paragraphe
- Typographie
- Fira Sans, Lexend (SIL OFL 1.1)
- Ressources
- Aucune : chaque image est dessinée en code
Préparation
#1 · Écrire les opérations comme des lignes du Markdown
// `- 864 : 4` in the Markdown is one operation; the style sets it digit by digit in squares
// of 7.2 mm, a cell for each figure of the answer. Long division follows the country: the
// divisor in an L right of the dividend in Spain and France (potence), `864 : 4 =` on one
// line in Germany, the bracket ⟌ in English-speaking schools.
const calculo = {
id: 'calculo',
number: { shape: 'disc', background: col('accent'), fontFamily: LABEL, fontWeight: 700 },
instruction: { bold: true, color: col('ink') },
grid: { cell: mm(9), fontFamily: TEXT, fontSize: pt(15), rule: col('rule'),
color: col('ink') },
arithmetic: { columns: 4,
division: t({ es: 'potence', fr: 'potence', de: 'inline', en: 'bracket' }) },
blank: { look: 'box', width: em(3.2), color: col('rule'), weight: pt(0.8) },
area: { kind: 'grid', spacing: mm(5), color: col('rule'), weight: pt(0.4) },
answers: { color: col('accent'), fontFamily: LABEL }, // the teacher's copy, in the accent
marginTop: mm(7), marginBottom: mm(2), itemSpacing: em(0.5),
};
Un :::exercise{type="arithmetic"} prend une opération par élément de liste, - 864 ÷ 4, avec les signes qu’écrit son pays (÷, :, ×, ·). Le moteur calcule le résultat, le quotient et le reste, et le style décide du dessin de l’opération : des cases de 7,2 mm, et une arithmetic.division choisie pour chaque édition avec t(). Une rangée de calcul mental est un exercice à trous ordinaire dont la liste se trouve dans un groupe :::columns{count=3}, chaque [[70]] donnant une case de la même largeur que les autres.
#2 · Composer l’en-tête de la fiche
const at = (to, edge, x = 0, y = 0, size) => ({ anchor: { to, edge },
offset: { x: mm(x), y: mm(y) }, ...(size && { size }) });
const field = (id, attr, x, width) => [
{ kind: 'text', id, content: `{attr.${attr}}`, fontFamily: LABEL, fontSize: pt(9.5),
fontWeight: 600, color: col('muted'), align: 'left',
placement: at('container', 'top-left', x, 26) },
{ kind: 'rule', id: `${id}-line`, direction: 'horizontal', thickness: pt(0.75),
color: col('rule'), placement: at('container', 'top-left', x + 17, 31, { width: mm(width) }) },
];
const head = { enabled: true, minHeight: mm(36), slot: { elements: [
{ kind: 'box', id: 'band', style: { backgroundColor: col('accent'), borderRadius: mm(3) },
placement: at('container', 'top-left', 0, 0, { width: 'fill', height: mm(19) }) },
{ kind: 'text', id: 'unit', content: '{attr.unidad}', fontFamily: LABEL, fontSize: pt(9),
fontWeight: 700, color: col('accent'), align: 'center', verticalAlign: 'middle',
textTransform: 'uppercase', letterSpacing: pt(1.2), lineHeight: 1.1,
box: { backgroundColor: col('paper'), borderRadius: mm(3.5) },
placement: at('container', 'top-left', 5, 5, { width: mm(26), height: mm(9) }) },
{ kind: 'text', id: 'title', content: '{titleText}', fontFamily: LABEL, fontSize: pt(21),
fontWeight: 700, color: col('paper'), align: 'left', lineHeight: 1.1,
placement: at('container', 'top-left', 37, 5.2) },
...field('name', 'nombre', 0, 100), ...field('date', 'fecha', 124, 38),
] } };
Le titre de niveau 1 de l’unité dessine le bandeau orange, la pastille et le titre, ainsi que les champs du prénom et de la date à partir de ses attributs (nombre="Name" fecha="Date") : chaque édition les intitule donc dans sa langue.
#3 · Passer de la fiche de l’élève à la version de l’enseignant
const form = document.getElementById('controls');
const labels = t({ es: ['Alumno', 'Profesor'], en: ['Pupil', 'Teacher'],
fr: ['Élève', 'Enseignant'], de: ['Schüler', 'Lehrkraft'] });
form.querySelectorAll('label span').forEach((span, k) => { span.textContent = labels[k]; });
function render() {
// The same config, edited in place: the next build sees the edition the form asks for.
config.exercises.edition = form.elements.edition.value;
const doc = buildDocument({ markdown }, config); // the faces are loaded by now
showPages(doc, { title });
document.querySelectorAll('#pt-actions a, [data-postext-pdf]').forEach((old) => old.remove());
offerPdf(() => renderToPdf(doc, { fontProvider: fontsourceProvider }),
`${RECIPE}-${LANG}-${config.exercises.edition}.pdf`);
}
form.addEventListener('change', render);
render();
exercises.edition: 'teacher' imprime chaque réponse à sa place, dans la couleur que donne answers.color, ici celle d’accent : les cases du calcul mental, les chiffres de chaque résultat, le quotient et le reste, la réponse modèle du problème. Le formulaire au-dessus des pages modifie la configuration sur place et relance la composition ; les polices sont déjà chargées, si bien que buildDocument suffit. La capture compose d’abord les deux fiches, et les pages publiées montrent la fiche de l’élève, puis celle de l’enseignant.
La recette complète
// ═══ Postext Cookbook · Nº 170 · Column arithmetic as each country writes it ═══════
// https://postext.dev/en/cookbook/column-arithmetic-by-country
// Code: MIT · Text: original (CC BY 4.0) · Pictures: none
// Fonts: Fira Sans, Lexend (SIL OFL 1.1) · Needs postext ≥ 1.59.0
import { buildDocumentWithFonts, buildDocument, renderPageToCanvas } from 'https://esm.sh/postext';
import { renderToPdf, decompressWoff2 } from 'https://esm.sh/postext-pdf';
const LANG = 'fr'; // @lang: the language of the worksheet ('es' | 'en' | 'fr' | 'de')
const RECIPE = 'column-arithmetic-by-country';
// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: ink, one orange accent, a pale tint, the grey of squared paper
const palette = {
ink: '#1f2328', accent: '#c4511c', tint: '#fbeee6', rule: '#b9c0c8', muted: '#5f6670',
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.accent, model: 'hex' } },
];
// #endregion
const [TEXT, LABEL] = ['Fira Sans', 'Lexend'];
// #region answer: operations in squares, long division as the edition's schools set it
// `- 864 : 4` in the Markdown is one operation; the style sets it digit by digit in squares
// of 7.2 mm, a cell for each figure of the answer. Long division follows the country: the
// divisor in an L right of the dividend in Spain and France (potence), `864 : 4 =` on one
// line in Germany, the bracket ⟌ in English-speaking schools.
const calculo = {
id: 'calculo',
number: { shape: 'disc', background: col('accent'), fontFamily: LABEL, fontWeight: 700 },
instruction: { bold: true, color: col('ink') },
grid: { cell: mm(9), fontFamily: TEXT, fontSize: pt(15), rule: col('rule'),
color: col('ink') },
arithmetic: { columns: 4,
division: t({ es: 'potence', fr: 'potence', de: 'inline', en: 'bracket' }) },
blank: { look: 'box', width: em(3.2), color: col('rule'), weight: pt(0.8) },
area: { kind: 'grid', spacing: mm(5), color: col('rule'), weight: pt(0.4) },
answers: { color: col('accent'), fontFamily: LABEL }, // the teacher's copy, in the accent
marginTop: mm(7), marginBottom: mm(2), itemSpacing: em(0.5),
};
// #endregion
// #region head: the unit pill, the title and the lines for a name and a date
const at = (to, edge, x = 0, y = 0, size) => ({ anchor: { to, edge },
offset: { x: mm(x), y: mm(y) }, ...(size && { size }) });
const field = (id, attr, x, width) => [
<form id="controls" aria-label="Edition">
<label><input type="radio" name="edition" value="student" checked> <span></span></label>
<label><input type="radio" name="edition" value="teacher"> <span></span></label>
</form>
<main id="pages"></main>
/* The edition switch above the desk: two pills in the sheet's orange. */
#controls {
display: flex; justify-content: center; gap: 8px; padding: 14px 16px; background: #16181d;
font: 600 14px/1.2 Lexend, system-ui, sans-serif;
}
#controls label {
display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 18px;
border-radius: 22px; color: #f4f1ea; border: 1px solid #5f6670; cursor: pointer;
}
#controls label:has(input:checked) { background: #c4511c; border-color: #c4511c; }
#controls input { accent-color: #fff; }
Le script.js composé s’exécute tel quel : collez-le dans le script de module de n’importe quelle page, ou ouvrez la recette sur CodePen. Dossier de la recette sur GitHub ↗ (s’ouvre dans un nouvel onglet)
Variantes
#Mettre les solutions à l’envers en pied de page
Une habitude des cahiers de jeux : le corrigé de chaque exercice, retourné à 180° sous l’exercice.
- exercises: { edition: 'student' }, // the switch above the pages sets 'teacher'
+ exercises: { edition: 'student', solutions: { placement: 'inverted', layout: 'key' } },#Poser la division sous le crochet dans toutes les éditions
Les écoles japonaises et chinoises utilisent aussi le crochet ; une langue que vous ne nommez pas garde sa propre disposition.
- division: t({ es: 'potence', fr: 'potence', de: 'inline', en: 'bracket' }) },
+ division: 'bracket' },Erreurs fréquentes
- Les opérations portent sur des nombres entiers : une opération avec des décimales comme
12,5 + 3,75n’est pas lue et s’imprime telle qu’elle est écrite. Gardez les nombres décimaux dans les trous du calcul mental, où la réponse est du texte.
Crédits
- Recette
- Ignacio Ferro
- Texte
- Texte original, CC BY 4.0
- Polices
- Fira Sans (SIL OFL 1.1) · Lexend (SIL OFL 1.1)
Modifier ce texte ↗ (s’ouvre dans un nouvel onglet)Dossier de la recette sur GitHub ↗ (s’ouvre dans un nouvel onglet)


