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.
Header (0.95 in) — bandeau bleu fixe, matière + niveau + n° de page.
Page title — eyebrow + titre + sous-titre, séparé du header par page-header-gap.
Zone contenu — uniquement des blocs autorisés, espacés par les tokens.
Zone tampon (0.30 in) — obligatoire avant le footer. Aucun bloc n'a le droit de la traverser.
Footer (0.85 in) — score, QR Ketty, pagination. Inchangé d'une page à l'autre.
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 (
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.
block-padding-y · 0.18 in (haut/bas) — fixe.
block-padding-x · 0.20 in (gauche/droite) — fixe.
block-tab-clear · 0.12 in — clearance sous l'onglet, indispensable.
intra-block-gap · 0.10 in — entre 2 lignes/items d'un même bloc.
min-height · 0.46 in — pas de bloc plus court (sinon → ligne, pas bloc).
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
Relation entre les deux blocs
Espacement
Exemple
{[
["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]) => (
{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.
{["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.
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.
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.