// template-pack-7-design-patterns.jsx // PACK 7 · DESIGN PATTERNS & PAGE COMPOSITION RULES — Allo Education Books v1.2 // // ❗️ Ce fichier n'est PAS un template de page. C'est une PLANCHE DESIGN. // Sortie : une fiche de référence pour rédacteur / dev / auditeur PDF. // Aucune ligne pédagogique réelle — uniquement des blocs montrés à l'état pur, // avec leurs règles (items max, lignes max, combinaisons OK / KO, anti-overflow). /* ─────────────────────────────────────────────────────────────────────── Mini-helpers internes : aucune dépendance aux PageShell. C'est une planche. ─────────────────────────────────────────────────────────────────────── */ const P7_TOKEN = { cardBg: "white", border: "var(--ae-border)", rail: "var(--ae-primary)", ok: "var(--ae-success)", ko: "var(--ae-error)", warn: "var(--ae-yellow-dark)", ink: "var(--ae-text)", inkSoft: "var(--ae-text-soft)", muted: "var(--ae-muted)", }; function P7Pill({ tone = "primary", children }) { const map = { primary: ["var(--ae-primary-soft)", "var(--ae-primary)"], yellow: ["var(--ae-yellow-soft)", "var(--ae-yellow-dark)"], orange: ["var(--ae-orange-soft)", "var(--ae-orange-dark)"], green: ["var(--ae-success-soft)", "var(--ae-success)"], red: ["var(--ae-error-soft)", "var(--ae-error)"], cool: ["var(--ae-bg-cool)", "var(--ae-text-soft)"], cream: ["#FBF8F1", "#7a5a1c"], }; const [bg, fg] = map[tone] || map.cool; return ( {children} ); } function P7Card({ num, name, usage, max, combine, avoid, overflow, std, compact }) { return (
{/* Left: previews */}
P7-{String(num).padStart(2, "0")}

{name}

{usage}

{std}
{compact}
{/* Right: rules */}
); } function P7VariantHead({ label }) { return (
{label}
); } function P7RulesRow({ label, value, rows, tone }) { if (rows) { return (
{label}
{rows.map((r, i) => {r})}
); } return (
{label} {value}
); } /* ─────────────────────────────────────────────────────────────────────── Mini-blocs visuels (jamais utilisés en page — uniquement ici, en planche) ─────────────────────────────────────────────────────────────────────── */ function MiniBox({ tone = "primary", label, dashed, children, pad = "10px 12px" }) { const map = { primary: ["var(--ae-primary-soft)", "var(--ae-primary)"], yellow: ["var(--ae-yellow-soft)", "var(--ae-yellow-dark)"], orange: ["var(--ae-orange-soft)", "var(--ae-orange-dark)"], green: ["var(--ae-success-soft)", "var(--ae-success)"], red: ["var(--ae-error-soft)", "var(--ae-error)"], cool: ["var(--ae-bg-cool)", "var(--ae-text-soft)"], cream: ["#FBF8F1", "#7a5a1c"], neutral: ["white", "var(--ae-border)"], }; const [bg, bd] = map[tone] || map.neutral; return (
{label && (
{label}
)} {children}
); } function MiniHRule({ w = "100%" }) { return
; } function MiniWriteLine({ w = "100%" }) { return
; } function MiniWriteBox({ h = 28 }) { return
; } function MiniDot({ c = "var(--ae-primary)" }) { return ; } function MiniQR({ size = 44 }) { return (
); } /* ─────────────────────────────────────────────────────────────────────── PLANCHE 1 — Hero ─────────────────────────────────────────────────────────────────────── */ function P7Hero() { return (
Pack 7 · Patterns

Design Patterns & Composition Rules

La planche visuelle des 18 blocs autorisés dans Allo Education Books. Pour le rédacteur : combien d'items écrire. Pour le dev : quels blocs existent. Pour l'auditeur PDF : ce qui est valide, ce qui ne l'est pas, et comment composer une page qui respire.

v1.2 · verrouillé 18 blocs documentés 5 compositions OK 5 compositions KO
); } /* ─────────────────────────────────────────────────────────────────────── PLANCHE 2 — Les 18 blocs ─────────────────────────────────────────────────────────────────────── */ const BLOCKS = [ /* 01 — Objectif */ { num: 1, name: "Objectif", usage: "Énonce ce que l'élève doit savoir faire à la fin de la page. Toujours en haut, jamais ailleurs.", max: { items: "1 objectif", lines: "1 à 2 lignes" }, combine: ["À retenir", "Méthode", "Exemple guidé", "Exercice"], avoid: ["Autre Objectif", "Bilan final"], overflow: "Si l'objectif dépasse 2 lignes, réécris plus court — n'ajoute pas une 3ᵉ ligne.", std: ( Comparer deux fractions de même dénominateur. ), compact: ( Comparer 2 fractions. ), }, /* 02 — À retenir */ { num: 2, name: "À retenir", usage: "Règle, définition ou notion-clé que l'élève doit mémoriser. Style fond jaune, marqueur visuel fort.", max: { items: "1 règle + 0–4 bullets", lines: "5 lignes max" }, combine: ["Objectif", "Exemple guidé", "Méthode", "Exercice"], avoid: ["Mini-sujet", "Correction compacte"], overflow: "Découper en deux \"À retenir\" plutôt qu'un seul trop long. Pas de paragraphe fleuve.", std: ( Une fraction représente une partie d'un tout.
Numérateur en haut.
Dénominateur en bas.
), compact: ( Numérateur ÷ Dénominateur. ), }, /* 03 — Méthode / Étapes */ { num: 3, name: "Méthode / Étapes", usage: "Procédure pas-à-pas, numérotée. Verts. Disponible en vertical (standard) et horizontal (compact).", max: { items: "3 à 6 étapes", lines: "1 ligne / étape" }, combine: ["Objectif", "Exemple guidé", "Problème"], avoid: ["Autre Méthode", "Mini-sujet"], overflow: "Au-delà de 6 étapes : c'est deux méthodes. Ou ce n'est pas une méthode.", std: (
{["Je lis l'énoncé.", "Je repère les données.", "Je pose le calcul.", "Je rédige la phrase."].map((s, i) => (
{i + 1} {s}
))}
), compact: (
{["Lire", "Repérer", "Calculer", "Rédiger"].map((s, i) => ( {i + 1} {s} ))}
), }, /* 04 — Exemple guidé */ { num: 4, name: "Exemple guidé", usage: "Un exemple résolu sous les yeux de l'élève. Énoncé court + démarche courte + résultat. Disponible en variante maths, français, sciences.", max: { items: "1 exemple", lines: "3 à 5 lignes de calcul" }, combine: ["À retenir", "Méthode", "Exercice"], avoid: ["Autre exemple", "Correction compacte"], overflow: "Si la démarche dépasse 5 lignes, soit la méthode est mal expliquée, soit l'exemple est trop dur.", std: (
Compare 3/5 et 4/5.
3/5 < 4/5
même dénominateur → on compare les numérateurs.
), compact: ( 3/5 < 4/5 ), }, /* 05 — Exercice simple */ { num: 5, name: "Exercice simple", usage: "Grille d'items courts, même format. 2 colonnes. Chaque item = énoncé minimal + zone réponse.", max: { items: "4 à 6 items", lines: "1 ligne d'énoncé / item" }, combine: ["Objectif", "À retenir", "Méthode"], avoid: ["Exercice progressif (sur la même page)", "Mini-sujet"], overflow: "Plus de 6 items → c'est un exercice progressif, pas un exo simple. Utilise le bloc 06.", std: (
{[1, 2, 3, 4].map(n => (
{n}
))}
), compact: (
{[1, 2, 3, 4].map(n => (
{n}. ___
))}
), }, /* 06 — Exercice progressif */ { num: 6, name: "Exercice progressif", usage: "Deux exercices structurés sur la même page, du plus facile au plus difficile. Zones de calcul compactes.", max: { items: "2 exercices · 4 items / exo", lines: "2 lignes de calcul / item" }, combine: ["Objectif", "Méthode", "Auto-évaluation"], avoid: ["Exemple guidé long", "Problème guidé", "Mini-sujet"], overflow: "Si un seul exo prend toute la page, c'est un Problème guidé, pas un exo progressif.", std: (
), compact: (
), }, /* 07 — QCM */ { num: 7, name: "QCM", usage: "Questions à choix multiples. Trois choix A/B/C systématiques. Option justification courte.", max: { items: "3 à 5 questions", lines: "1 ligne / option" }, combine: ["Objectif", "À retenir", "Auto-évaluation"], avoid: ["Problème guidé", "Mini-sujet (le QCM EST déjà un mini-sujet)"], overflow: "Au-delà de 5 questions → couper en deux QCM ou passer en mini-sujet (bloc 15).", std: (
1. La capitale de l'Italie est…
{["A Rome", "B Paris", "C Madrid"].map(o => ( {o} ))}
), compact: ( 1. … A · B · C ), }, /* 08 — Problème guidé */ { num: 8, name: "Problème guidé", usage: "Situation → données → étapes → phrase réponse. Format long. Maximum 1 ou 2 problèmes par page.", max: { items: "1 à 2 problèmes", lines: "8 à 10 lignes / problème" }, combine: ["Méthode", "Ketty (astuce)", "Auto-évaluation"], avoid: ["Mini-sujet", "QCM", "Exercice progressif"], overflow: "2 problèmes = limite stricte. Si tu veux 3 problèmes → 2 pages.", std: (
Léa achète 3 livres à 8 €. Combien paye-t-elle ?
Données Calcul
), compact: (
3 livres × 8 € = ___
), }, /* 09 — Figure / Schéma */ { num: 9, name: "Figure / Schéma", usage: "SVG géométrique, schéma scientifique, carte muette. Toujours légendé. Question associée immédiate.", max: { items: "1 figure principale", lines: "—" }, combine: ["Question", "Tableau (données)"], avoid: ["Autre figure", "Ketty long"], overflow: "Hauteur max recommandée 3 in. Si plus, c'est une planche → page dédiée.", std: (
ABCD
Quadrilatère ABCD · 4 cm × 3 cm
), compact: (
schéma
), }, /* 10 — Tableau */ { num: 10, name: "Tableau", usage: "Donnée structurée. Variantes : proportionnalité, tableur (gris), statistiques.", max: { items: "2 à 5 colonnes · 2 à 5 lignes", lines: "—" }, combine: ["Question", "Figure (légende)"], avoid: ["Autre tableau plein", "Problème guidé long"], overflow: "Une cellule = un mot ou un nombre. Pas de phrase dans une cellule.", std: (
Élève Note
Léa16
Sam14
), compact: (
×
2
3
P
4
6
), }, /* 11 — Graphique / Repère */ { num: 11, name: "Graphique / Repère", usage: "Repère orthonormé, axes gradués, points placés. Toujours suivi de 2-3 questions de lecture.", max: { items: "1 repère · 5 points max", lines: "—" }, combine: ["Question", "Tableau"], avoid: ["Figure 3D", "Ketty long"], overflow: "Taille max 2.5 in × 2.5 in. Au-delà = page dédiée graphique.", std: ( {[20, 30, 40, 50, 60, 70, 80].map(x => )} {[15, 25, 35, 45].map(y => )} ), compact: ( ), }, /* 12 — Auto-évaluation */ { num: 12, name: "Auto-évaluation", usage: "Petite ligne de fin de page. Oui / Presque / À revoir. Jamais énorme. Toujours en bas, jamais en haut.", max: { items: "3 lignes", lines: "1 ligne / item" }, combine: ["Exercice", "Problème", "QCM"], avoid: ["Ketty long", "Bilan final", "Mini-sujet"], overflow: "Si l'élève doit lire plus de 3 phrases pour s'auto-évaluer → c'est devenu un bilan.", std: ( {["J'ai compris la leçon.", "Je sais l'appliquer seul·e.", "Je peux expliquer à un·e camarade."].map((s, i) => (
{s} {[0,1,2].map(j => )}
))}
), compact: (
OuiPresqueÀ revoir
), }, /* 13 — Ketty */ { num: 13, name: "Ketty", usage: "Bulle de coaching de la mascotte. Court, encourageant. Types : astuce, méthode, erreur, bravo.", max: { items: "1 bulle / page (2 max)", lines: "2 à 3 lignes" }, combine: ["Problème", "Exercice", "Exemple guidé"], avoid: ["Bilan final", "Mini-sujet", "Correction compacte"], overflow: "Si Ketty dit + de 3 lignes, c'est plus une bulle, c'est un paragraphe. Coupe.", std: (
K
Repère d'abord le dénominateur — c'est lui qui décide.
), compact: ( Ketty — repère le dénominateur. ), }, /* 14 — QR */ { num: 14, name: "QR", usage: "Bloc d'accès aux ressources : corrections, audio, quiz. Position recommandée : bas-droit ou bas-page.", max: { items: "1 QR principal / page", lines: "—" }, combine: ["Footer", "Auto-évaluation", "Bilan"], avoid: ["Ketty long juste à côté", "Tableau dense juste à côté"], overflow: "Taille min imprimable : 0.6 in. Sous-label = 1 ligne max.", std: (
Corriger avec Ketty
alloeducation.fr/qr/p28
), compact: (
p28
), }, /* 15 — Mini-sujet */ { num: 15, name: "Mini-sujet", usage: "Sujet d'examen blanc : conditions, exercices, barème. Mode TRÈS compact, attention overflow.", max: { items: "3 à 5 exercices", lines: "3 lignes max / consigne" }, combine: ["Auto-évaluation très compacte", "QR mini"], avoid: ["Méthode", "Ketty", "Exemple guidé", "Problème guidé"], overflow: "Le mini-sujet mange déjà la page. AUCUN autre bloc large. Zones de réponse minimales.", std: (
Calculatrice interdite.
{["Ex 1 · /4 ……………", "Ex 2 · /6 ……………", "Ex 3 · /5 ……………", "Ex 4 · /5 ……………"].map(s =>
{s}
)}
), compact: ( Sujet · 4 ex · /20 ), }, /* 16 — Correction compacte */ { num: 16, name: "Correction compacte", usage: "Page de fin de cahier : réponses finales uniquement. AUCUN développement, aucune méthode.", max: { items: "6 à 10 groupes", lines: "1 ligne / réponse" }, combine: ["QR mini", "Renvoi page"], avoid: ["Méthode", "Ketty", "Exemple guidé", "Tableau plein"], overflow: "Si tu veux expliquer une correction → c'est dans le QR, pas sur la page.", std: (
{["p28 · 1) 12", "p28 · 2) 5,4", "p29 · 1) Vrai", "p29 · 2) 3/4"].map(s =>
{s}
)}
), compact: (
p28→p32 · 12 réponses
), }, /* 17 — Formulaire */ { num: 17, name: "Formulaire", usage: "Cartes de formules / propriétés. 2 colonnes. Aucune phrase — uniquement les formules avec nom.", max: { items: "6 à 10 cartes", lines: "Nom + formule" }, combine: ["Mini-sujet (à côté, page miroir)"], avoid: ["Exemple guidé", "Méthode", "Problème"], overflow: "Si une carte fait 3 lignes, ce n'est pas une formule, c'est un mini-cours. Sors-la d'ici.", std: (
{[["Aire carré", "c × c"], ["Aire rect.", "L × l"], ["P. carré", "4 × c"], ["P. rect.", "2(L+l)"]].map(([n, f]) => (
{n}
{f}
))}
), compact: ( A=L×l · P=2(L+l) ), }, /* 18 — Fin de livre */ { num: 18, name: "Fin de livre", usage: "Famille de blocs premium : diplôme, bilan, hub QR, mentions. Style sobre, encadrement, dégradé léger autorisé.", max: { items: "1 bloc / page", lines: "—" }, combine: ["Couverture intérieure", "Collection showcase"], avoid: ["Exercice", "Mini-sujet", "Méthode", "Problème"], overflow: "Ne JAMAIS mélanger fin de livre + exercice sur la même page. Page dédiée toujours.", std: (
Diplôme
Léa Martinez
Cahier terminé · 12 chapitres
), compact: ( Diplôme · Bilan · QR ), }, ]; /* ─────────────────────────────────────────────────────────────────────── PLANCHE 3 — Compositions OK / KO ─────────────────────────────────────────────────────────────────────── */ function PageMock({ verdict = "ok", title, sub, blocks }) { const railColor = verdict === "ok" ? "var(--ae-success)" : "var(--ae-error)"; return (
{/* Header strip */}
{verdict === "ok" ? "Combinaison recommandée" : "Combinaison à éviter"} {verdict === "ok" ? "✓" : "✕"}
{/* Body */}
{title}
{sub}
{/* Page silhouette 8.5×11 reduced */}
{/* faux header */}
{/* content */}
{blocks.map((b, i) => (
{b.label}
))}
{/* faux footer */}
{/* KO cross overlay */} {verdict === "ko" && (
)}
{/* Reasoning */}
{sub === undefined ? null : null}
); } const blkOK = (label, opts = {}) => ({ label, bg: "var(--ae-primary-soft)", border: "var(--ae-primary)", fg: "var(--ae-primary-deep)", ...opts, }); const blkYel = (label, opts = {}) => ({ label, bg: "var(--ae-yellow-soft)", border: "var(--ae-yellow-dark)", fg: "#7a5a1c", ...opts, }); const blkGr = (label, opts = {}) => ({ label, bg: "var(--ae-success-soft)", border: "var(--ae-success)", fg: "var(--ae-success)", ...opts, }); const blkOr = (label, opts = {}) => ({ label, bg: "var(--ae-orange-soft)", border: "var(--ae-orange-dark)", fg: "var(--ae-orange-dark)", ...opts, }); const blkCr = (label, opts = {}) => ({ label, bg: "#FBF8F1", border: "#d8c89a", fg: "#7a5a1c", ...opts, }); const blkN = (label, opts = {}) => ({ label, bg: "white", border: "var(--ae-border)", fg: "var(--ae-text-soft)", ...opts, }); const blkRed = (label, opts = {}) => ({ label, bg: "var(--ae-error-soft)", border: "var(--ae-error)", fg: "var(--ae-error)", ...opts, }); const COMPOSITIONS_OK = [ { title: "Leçon courte", sub: "Objectif → À retenir → Exemple → À toi → QR", blocks: [ blkOK("Objectif", { flex: 0.6 }), blkYel("À retenir", { flex: 1.1 }), blkCr("Exemple guidé", { flex: 1 }), blkOK("Exercice simple · 4 items", { flex: 1.4 }), blkN("QR + Auto-éval", { flex: 0.7 }), ]}, { title: "Exercice progressif", sub: "Objectif → Ex 1 → Ex 2 → Auto-évaluation → QR", blocks: [ blkOK("Objectif", { flex: 0.5 }), blkOK("Exercice 1 · facile", { flex: 1.4 }), blkOr("Exercice 2 · expert", { flex: 1.6 }), blkGr("Auto-évaluation", { flex: 0.6 }), blkN("QR", { flex: 0.5 }), ]}, { title: "Problème guidé", sub: "Situation → Données → Étapes → Réponse → Ketty → QR", blocks: [ blkOK("Situation + Données", { flex: 1 }), blkGr("Méthode 4 étapes", { flex: 0.9 }), blkOK("Calcul + Phrase réponse", { flex: 1.6 }), blkOK("Ketty · 2 lignes", { flex: 0.6 }), blkN("QR", { flex: 0.5 }), ]}, { title: "Mini-sujet (brevet blanc)", sub: "Conditions → 4 exercices → Score → QR", blocks: [ blkOr("Conditions · 30 min · /20", { flex: 0.5 }), blkOr("Exercice 1 · /4", { flex: 1 }), blkOr("Exercice 2 · /6", { flex: 1.1 }), blkOr("Exercice 3 · /5", { flex: 1 }), blkOr("Exercice 4 · /5", { flex: 1 }), blkN("Score · QR mini", { flex: 0.4 }), ]}, { title: "Correction", sub: "Réponses compactes · 2 colonnes · QR explication", blocks: [ blkN("Titre · p28 → p32", { flex: 0.4 }), blkN("Réponses ex 1-4 (2 col)", { flex: 1.6 }), blkN("Réponses ex 5-8 (2 col)", { flex: 1.6 }), blkN("QR explications détaillées", { flex: 0.5 }), ]}, ]; const COMPOSITIONS_KO = [ { title: "Grosse figure + gros tableau + Ketty long", sub: "Tout en concurrence. Aucun élément ne respire.", blocks: [ blkRed("Figure 3.5 in"), blkRed("Tableau plein 5×5"), blkRed("Ketty · 6 lignes"), blkRed("Exercice · 6 items"), ]}, { title: "Mini-sujet + grandes zones de réponse", sub: "Le mini-sujet est déjà dense. Les zones débordent.", blocks: [ blkRed("Sujet · conditions"), blkRed("Ex 1 + zone réponse 2 in"), blkRed("Ex 2 + zone réponse 2 in"), blkRed("Ex 3 + zone réponse 2 in"), ]}, { title: "Correction compacte + explications longues", sub: "La correction n'explique jamais. C'est le rôle du QR.", blocks: [ blkRed("Correction p28"), blkRed("Explication détaillée"), blkRed("Méthode pas à pas"), blkRed("Exemple guidé"), ]}, { title: "QR large + footer chargé", sub: "Double QR au pied de page. Choix : ou page, ou footer.", blocks: [ blkN("Contenu page"), blkRed("QR large 1.5 in"), blkRed("Footer · score · QR factory · page"), ]}, { title: "Plus de 2 problèmes guidés sur 1 page", sub: "Limite stricte : 2 problèmes max. Au-delà → 2 pages.", blocks: [ blkRed("Problème 1"), blkRed("Problème 2"), blkRed("Problème 3"), blkRed("Problème 4"), ]}, ]; /* ─────────────────────────────────────────────────────────────────────── PLANCHE 4 — Règles d'espacement ─────────────────────────────────────────────────────────────────────── */ function SpacingVisual({ gap, label, verdict }) { const tone = verdict === "ok" ? "var(--ae-success)" : verdict === "ko-tight" ? "var(--ae-error)" : "var(--ae-yellow-dark)"; return (
{label}
gap : {gap}
{[1,2,3].map(i => (
Bloc {i}
))}
); } function P7Spacing() { return (

Règles d'espacement entre blocs

Un cahier d'exercices DOIT respirer. La V1.2 verrouille trois densités. Si une page est trop serrée OU trop vide, c'est qu'on a choisi la mauvaise densité — pas qu'on doit improviser des marges.

Tokens officiels
{[ { name: "Air (CP / CE1)", gap: "0.26 in", lh: 1.6, ah: "0.84 in" }, { name: "Standard (par défaut)", gap: "0.20 in", lh: 1.5, ah: "0.60 in" }, { name: "Dense (lycée)", gap: "0.14 in", lh: 1.45, ah: "0.50 in" }, ].map(d => (
{d.name}
blockGap · {d.gap}
lineHeight · {d.lh}
answerBox · {d.ah}
))}
Anti-trous blancs — checklist auditeur
  • Si une zone vide dépasse 1.5 in de haut → ajouter un bloc, agrandir une zone réponse, ou réduire la densité.
  • Si deux blocs se touchent (gap < 0.14 in) → c'est trop serré, on passe en densité standard ou air.
  • Une page d'exercices DOIT avoir au moins 3 blocs distincts. Sinon = trop vide.
  • Une page DOIT avoir au plus 6 blocs principaux. Au-delà = trop chargé.
  • Le footer ne compte pas comme bloc. Le header non plus.
); } /* ─────────────────────────────────────────────────────────────────────── PLANCHE 5 — Le composant principal ─────────────────────────────────────────────────────────────────────── */ function Pack7DesignPatterns() { return (
{/* Bloc-by-bloc */}

Les 18 blocs autorisés

Bloc → règles → standard + compact
{BLOCKS.map(b => )}
{/* OK */}

Combinaisons recommandées par page

Cinq compositions canoniques. Reprends-les telles quelles plutôt que d'inventer.

{COMPOSITIONS_OK.map((c, i) => )}
{/* KO */}

Combinaisons à éviter

Cinq combinaisons que l'auditeur PDF doit rejeter automatiquement.

{COMPOSITIONS_KO.map((c, i) => )}
{/* Spacing */} {/* Closer */}
Pourquoi cette planche existe

Cette planche n'est pas une page de livre. C'est l'outil de référence partagé entre rédacteur, dev et auditeur PDF. Si un nouveau template doit naître, il commence ici : choisis 3 à 5 blocs autorisés, vérifie les compositions OK / KO, applique la densité, puis seulement écris le contenu.

); } Object.assign(window, { Pack7DesignPatterns, P7_BLOCKS: BLOCKS });