// template-composition-rules.jsx // CHARTE D'ESPACEMENT — Grammaire officielle de la page Allo Education Books v1.3 // CE N'EST PAS UN PACK DE TEMPLATES. C'est la règle qui gouverne TOUS les templates. const CR_C = { ink: "var(--ae-text)", inkSoft:"var(--ae-text-soft)", muted: "var(--ae-muted)", border: "var(--ae-border)", primary:"var(--ae-primary)", primaryDeep:"var(--ae-primary-deep)", ok: "var(--ae-success)", ko: "var(--ae-error)", warn: "var(--ae-yellow-dark)", }; /* ─── Petits primitives visuels (planche, pas page réelle) ─────────── */ function CRChip({ tone = "primary", children }) { const map = { primary:["var(--ae-primary-soft)","var(--ae-primary)"], yellow: ["var(--ae-yellow-soft)","#7a5a1c"], 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)"], }; const [bg,fg] = map[tone] || map.cool; return {children}; } function CRToken({ name, value, role }) { return (
{name} {value} {role}
); } function CRMeasure({ label, value, vertical, length = 60 }) { if (vertical) { return (
{label}
{value}
); } return null; } /* ─── 1. Anatomie d'une page ──────────────────────────────────────── */ function CRPageAnatomy() { // SVG annoté : page 8.5×11 réduite à 320×420 (ratio respecté) const W = 340, H = 440; const margin = 18; const headerH = 38; const titleH = 30; const footerH = 30; const contentTop = margin + headerH + 10; const contentBottom = H - margin - footerH - 10; return (
{/* Page sheet */} {/* Header */} HEADER · matière · niveau · page {/* Title zone */} PAGE TITLE (eyebrow + titre + sous-titre) {/* First major block */} BLOC MAJEUR · Objectif {/* Second block */} BLOC MAJEUR · À retenir {/* Third block */} BLOC MAJEUR · Exercice {/* Footer safe area */} ⛔ ZONE TAMPON · footer-safe-gap {/* Footer */} FOOTER · score · QR · pagination {/* MEASURE GUIDES (right side) */} {/* page-header-gap */} page-header-gap 0.40 in {/* page-title-gap */} page-title-gap · 0.22 in {/* block-gap-md */} block-gap-md · 0.24 in {/* block-gap-lg */} block-gap-lg · 0.34 in {/* footer-safe-gap */} footer-safe-gap · 0.30 in

1. Anatomie d'une page

Une page Allo Education Books est toujours structurée selon cette anatomie. Aucun template n'a le droit d'inventer ses propres marges, son propre header, ni de toucher au footer.

Règle d'or : si un bloc empiète sur la zone tampon, la page est invalide. On split en deux pages ou on passe la densité d'air → standard, ou standard → dense.
); } /* ─── 2. Anatomie d'un bloc ──────────────────────────────────────── */ function CRBlockAnatomy() { const W = 360, H = 240; return (
{/* Outer block */} {/* Tab */} EXERCICE 1 {/* Padding regions */} {/* Tab clearance */} {/* Body content (mock text) */} Consigne · 1 ligne d'explication. {/* Annotations */} {/* padding-y top */} block-padding-y · 0.18 in {/* tab clearance */} block-tab-clear · 0.12 in {/* intra-block-gap */} intra-block-gap · 0.10 in {/* padding-y bottom */} block-padding-y · 0.18 in {/* padding-x */} block-padding-x · 0.20 in

2. Anatomie d'un bloc

Un bloc est toujours construit avec : un onglet (label), un clearance, un corps. Le texte du corps ne doit jamais lécher un bord.

Court ou long, c'est le bloc qui s'adapte — pas ses paddings. Si le contenu est très long, il rallonge le bloc. Si très court, le bloc garde sa min-height, le texte est centré.
); } /* ─── 3. Hiérarchie d'espacement entre blocs ─────────────────────── */ function GapDemo({ label, value, color }) { return (
{label} · {value}
Bloc A
{value}
Bloc B
); } function CRGapHierarchy() { return (

3. Hiérarchie d'espacement entre blocs

Cinq niveaux d'espacement, à choisir selon la relation entre deux blocs. Jamais d'espacement improvisé.

Quand utiliser quoi
{[ ["Lignes d'un même bloc","intra-block-gap","Items d'une liste Méthode"], ["2 blocs mineurs liés","block-gap-sm","Ketty mini + QR mini"], ["2 blocs majeurs liés","block-gap-md","Objectif → À retenir"], ["Changement de pédagogie","block-gap-lg","À retenir → Exercice 1"], ["Nouvelle section","section-gap","Cours → Exercices · Exercice → Bilan"], ["Avant footer","footer-safe-gap","Dernier bloc → footer (obligatoire)"], ].map(([a,b,c]) => ( ))}
Relation entre les deux blocs Espacement Exemple
{a} {b} {c}
); } /* ─── 4. Blocs majeurs vs mineurs ───────────────────────────────── */ function BlockChip({ name, kind }) { const ok = kind === "major"; return (
{name}
); } function CRMajorMinor() { return (

4. Blocs majeurs vs blocs mineurs

Tous les blocs n'ont pas le même poids visuel. Cette distinction conditionne l'espacement.

Blocs majeurs
{["Objectif","À retenir","Exemple guidé","Exercice principal","Problème guidé","Bilan","Correction","Mini-sujet"] .map(n => )}
Blocs mineurs
{["Mini-rappel","Astuce courte","Check rapide","Ligne de réponse","Micro-consigne","QR compact","Ketty mini","Note de bas"] .map(n => )}
Règle non négociable · deux blocs majeurs ne doivent jamais être visuellement collés. Entre eux : block-gap-md minimum, block-gap-lg si changement de pédagogie.
); } /* ─── 5. Densités ───────────────────────────────────────────────── */ function DensityCard({ name, gap, padding, gapMd, useCase, color }) { return (
{useCase}
{name}
--ae-block-gap : {gap}
--block-gap-md : {gapMd}
--block-padding-y : {padding}
{/* visual */}
{[1,2,3].map(i => (
))}
); } function CRDensity() { return (

5. Trois densités, jamais plus

Même en densité dense, la séparation visuelle entre blocs reste obligatoire. La densité ajuste les marges, pas la hiérarchie.

); } /* ─── 6. Exemples Bon / Mauvais ─────────────────────────────────── */ function PageMockup({ verdict, label, sub, gap = 8, pad = 8, blocks, footerGap = 12 }) { const tone = verdict === "ok" ? CR_C.ok : CR_C.ko; return (
{verdict === "ok" ? "✓ Bon" : "✕ Mauvais"} {label}
{sub}
{/* header */}
{/* body */}
{blocks.map((b,i) => (
{b.label}
))}
{/* footer */}
); } 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 blkRed = (label, opts={}) => ({ label, bg:"var(--ae-error-soft)", border:"var(--ae-error)", fg:"var(--ae-error)", ...opts }); const blkOr = (label, opts={}) => ({ label, bg:"var(--ae-orange-soft)", border:"var(--ae-orange-dark)", fg:"var(--ae-orange-dark)", ...opts }); const COMPARISONS = [ // pair 1 : gap { bad: { label:"Blocs collés", sub:"Tous les blocs se touchent. Aucune respiration. Le lecteur ne voit plus la hiérarchie.", gap:2, pad:6, footerGap:6, blocks:[blkRed("Objectif"),blkRed("À retenir"),blkRed("Exemple"),blkRed("Exercice 1"),blkRed("Exercice 2"),blkRed("Bilan")] }, good: { label:"Espace équilibré", sub:"Gap = block-gap-md entre blocs majeurs. Le rythme visuel respire et hiérarchise.", gap:10, pad:8, footerGap:14, blocks:[blkOK("Objectif",{flex:0.6}),blkYel("À retenir",{flex:1.1}),blkOr("Exemple",{flex:1}),blkOK("Exercice 1",{flex:1.3})] }, }, // pair 2 : padding interne { bad: { label:"Texte qui lèche le bord", sub:"Padding interne nul. Le texte touche le cadre. Impression d'arrachement, illisible.", gap:8, pad:6, footerGap:10, blocks:[blkRed("Objectif · Texte AU BORD DU CADRE",{pad:"0 0"}),blkRed("À retenir · texte qui touche",{pad:"0 0"})] }, good: { label:"Padding confortable", sub:"block-padding-x = 0.20 in, block-padding-y = 0.18 in. Le texte respire dans son cadre.", gap:8, pad:8, footerGap:10, blocks:[blkOK("· Objectif (0.20 in inset) ·"),blkYel("· À retenir (0.20 in inset) ·")] }, }, // pair 3 : footer { bad: { label:"Footer écrasé", sub:"Le dernier bloc touche le footer. Le QR du footer paraît être dans le bloc.", gap:8, pad:6, footerGap:0, blocks:[blkOK("Objectif",{flex:0.6}),blkYel("À retenir",{flex:1}),blkRed("Exercice (colle le footer)",{flex:1.6})] }, good: { label:"Zone tampon avant footer", sub:"footer-safe-gap = 0.30 in. Le footer redevient lisible et autonome.", gap:8, pad:6, footerGap:16, blocks:[blkOK("Objectif",{flex:0.6}),blkYel("À retenir",{flex:1}),blkOK("Exercice",{flex:1.4})] }, }, // pair 4 : page vide { bad: { label:"Page trop vide", sub:"Deux petits blocs en haut, énorme trou blanc. Donne l'impression d'une page bâclée.", gap:8, pad:6, footerGap:60, blocks:[blkOK("Objectif",{flex:0.5}),blkYel("À retenir",{flex:0.7})] }, good: { label:"Dernier bloc absorbe l'espace", sub:"Le bloc principal porte .ae-grow. Sa zone de réponse s'étire. Plus de trou.", gap:10, pad:6, footerGap:14, blocks:[blkOK("Objectif",{flex:0.5}),blkYel("À retenir",{flex:0.7}),blkOK("Exercice · zone réponse étirée",{flex:2.5})] }, }, ]; function CRGoodBad() { return (

6. Bons vs mauvais exemples

Quatre paires côte-à-côte. Mauvais à gauche, bon à droite. À mémoriser visuellement plutôt qu'en règle.

{COMPARISONS.map((pair,i) => ( ))}
); } /* ─── 7. Règles anti-overflow ───────────────────────────────────── */ function CRAntiOverflow() { const items = [ { rule:"Si la page dépasse → la page est invalide", explain:"Aucun contenu n'est silencieusement coupé. La page est rejetée à l'audit." }, { rule:"On ne colle pas les blocs pour faire rentrer", explain:"Ne jamais descendre sous block-gap-sm. Si ça ne rentre pas avec md, on change de densité." }, { rule:"D'abord : essayer la version compacte", explain:"Chaque bloc a une variante compacte documentée dans Pack 7. C'est le premier réflexe." }, { rule:"Ensuite : passer en densité dense", explain:"Cela touche tous les tokens d'espacement, en cohérence. Jamais d'override local." }, { rule:"Si encore overflow → split en 2 pages", explain:"Deux pages aérées valent mieux qu'une page écrasée. C'est la règle finale, sans exception." }, { rule:"Zone tampon footer non négociable", explain:"footer-safe-gap = 0.30 in. Aucun bloc n'entre dedans. L'auditeur PDF rejette automatiquement." }, ]; return (

7. Règles anti-overflow

L'ordre exact à suivre quand un contenu déborde. Pas de bricolage local : on respecte cette chaîne, dans cet ordre.

    {items.map((it,i) => (
  1. {i+1}
    {it.rule}
    {it.explain}
  2. ))}
); } /* ─── 8. Tokens officiels ───────────────────────────────────────── */ function CRTokens() { return (

8. Tokens de spacing officiels

Onze variables CSS, livrées en V1.3, à utiliser dans tous les templates. Plus jamais de pixels ou d'inches en dur dans un template.

TokenValeurRôle
Pour le dev · ces tokens sont scalés automatiquement par la classe de densité (.density-air, .density-std, .density-dense). Ne jamais override en local.
); } /* ─── Composant principal ──────────────────────────────────────── */ function CompositionRules() { return (
{/* Hero */}
Charte v1.3 · Composition

Comment une page Allo Education Books doit respirer

La grammaire d'espacement officielle : anatomie d'une page, anatomie d'un bloc, hiérarchie des gaps, règles anti-overflow, tokens. Si cette base est claire, tous les templates respirent.

11 tokens d'espacement 3 densités 8 chapitres overflow = page invalide
{/* Closer */}
Pour conclure

Cette charte ne donne pas de nouveaux templates. Elle dit comment les blocs s'assemblent. Si chaque template applique ces tokens via density-std et utilise .ae-grow sur son bloc principal, la page respire automatiquement — sans bricolage local.

); } Object.assign(window, { CompositionRules });