#Les blocs
Un bloc est l’élément répétable d’une section : une prestation dans une grille, un forfait dans un tableau de tarifs, un lien de réseau social dans un pied de page. La section déclare les types de bloc qu’elle accepte ; le template décide combien il y en a et dans quel ordre.
C’est ce qui sépare une section réglable d’une section figée : sans blocs, il
faudrait déclarer prestation_1_nom, prestation_2_nom, et fixer d’avance le
nombre de colonnes.
#Déclarer un type de bloc
Dans le {% schema %}, sous la clé blocks.
{
"name": "Pied de page",
"settings": [
{ "type": "textarea", "id": "adresse", "label": "Adresse" },
{ "type": "checkbox", "id": "mentions", "label": "Afficher les mentions légales", "default": true }
],
"blocks": [
{
"type": "reseau",
"name": "Réseau social",
"settings": [
{ "type": "text", "id": "reseau", "label": "Nom", "default": "Instagram" },
{ "type": "text", "id": "href", "label": "Adresse", "default": "https://instagram.com/" }
]
}
],
"max_blocks": 5
}typechaînerequisL’identifiant du type de bloc. C’est lui qu’un template écrit, et lui qu’on
teste en Liquid avec bloc.type. Un mot court, en minuscules.
namechaînerequisLe nom lisible du type. Purement documentaire aujourd’hui.
settingstableauLes réglages du bloc, mêmes treize types que ceux d’une section. Voir Les types de réglage.
limitentierNombre maximal de blocs de ce type, quand une section en accepte plusieurs. Aucun des quatre thèmes d’origine ne s’en sert.
Une section peut déclarer plusieurs types. La section « Texte libre »
d’origo en a trois, et les trois portent le même id de réglage — c’est le
type qui décide de ce qu’on en fait :
{
"name": "Texte libre",
"blocks": [
{
"type": "paragraphe",
"name": "Paragraphe",
"settings": [
{ "type": "textarea", "id": "texte", "label": "Texte", "default": "Écrivez ici ce que vous voulez raconter. Un bloc par paragraphe." }
]
},
{
"type": "titre",
"name": "Sous-titre",
"settings": [
{ "type": "text", "id": "texte", "label": "Sous-titre", "default": "Un sous-titre" }
]
},
{
"type": "liste",
"name": "Liste à puces",
"settings": [
{ "type": "textarea", "id": "texte", "label": "Éléments", "info": "Une ligne par élément.", "default": "Premier point\nDeuxième point" }
]
}
],
"max_blocks": 30
}max_blocksentierSe déclare à la racine du schéma, pas dans blocks : c’est un plafond pour
la section entière, tous types confondus. origo va de 4 pour les
forfaits et les arguments d’un bandeau à 12 pour les prestations.
max_blocks et limit ne sont aujourd’hui vérifiés par rien. Le rendu
boucle sur tout ce que le template contient. Ce sont des contrats à
destination de qui compose une page, et ils seront lus par le panneau de
réglages le jour où il existera. Écris-les justes : une grille prévue pour
trois colonnes ne supporte pas quinze blocs, et c’est le seul endroit où tu
peux le dire.
#Poser des blocs dans un template
Deux clés jumelles : blocks, un objet indexé par identifiant, et
block_order, un tableau qui donne l’ordre.
{
"sections": {
"hero": {
"type": "hero",
"settings": {},
"blocks": {
"b1": { "type": "argument", "settings": { "texte": "Devis gratuit" } },
"b2": { "type": "argument", "settings": { "texte": "Intervention sous 48 h" } },
"b3": { "type": "argument", "settings": { "texte": "Travail garanti" } }
},
"block_order": ["b1", "b2", "b3"]
}
},
"order": ["hero"]
}Pourquoi un objet plus un tableau, et pas simplement un tableau ? Parce
qu’un identifiant stable permet de déplacer un bloc sans réécrire son
contenu : réordonner, c’est permuter deux chaînes dans block_order. C’est
la forme de Shopify, pour la même raison.
block_order fait foi quand il existe. Sans lui, l’ordre des clés de
l’objet JSON décide de l’ordre d’affichage — il se trouve qu’il est stable en
pratique, mais rien ne le garantit, et un thème ne doit pas en dépendre.
Un identifiant listé dans block_order mais absent de blocks est ignoré
en silence. C’est ce qui permet de retirer un bloc sans nettoyer l’ordre —
mais aussi ce qui fait qu’une faute de frappe dans un identifiant fait
disparaître un bloc sans un mot.
#Lire les blocs en Liquid
section.blocks est un tableau. Chaque entrée porte trois champs.
block.idchaîneLa clé sous laquelle le bloc est déclaré dans le template : "b1", "b2".
Utile pour fabriquer un id HTML unique ou une ancre.
block.typechaîneLe type déclaré, celui du schéma. C’est lui qu’on teste quand une section accepte plusieurs types.
block.settingsobjetLes réglages du bloc, tels que le template les a écrits.
#La boucle simple
<div class="grille grille-{{ section.settings.colonnes | default: '3' }}">
{%- for bloc in section.blocks -%}
<article class="carte">
<h3>{{ bloc.settings.nom }}</h3>
{%- if bloc.settings.description != blank -%}
<p>{{ bloc.settings.description }}</p>
{%- endif -%}
{%- if bloc.settings.prix != blank -%}
<span class="prix">{{ bloc.settings.prix }}</span>
{%- endif -%}
</article>
{%- endfor -%}
</div>#Ne rien afficher quand il n’y a aucun bloc
{%- if section.blocks.size > 0 -%}
<ul class="hero-arguments">
{%- for bloc in section.blocks -%}
<li>{{ bloc.settings.texte }}</li>
{%- endfor -%}
</ul>
{%- endif -%}Sans ce test, une section sans bloc laisse un ul vide — donc, selon la
feuille de style, une marge inexpliquée au milieu de la page.
#Aiguiller sur le type
La section texte des quatre thèmes accepte plusieurs types de bloc et
aiguille dessus. Voici celle d’origo, qui en a trois :
<div class="texte-corps">
{%- for bloc in section.blocks -%}
{%- if bloc.type == 'titre' -%}
<h3>{{ bloc.settings.texte }}</h3>
{%- elsif bloc.type == 'liste' -%}
<ul class="liste-coches">
{%- assign lignes = bloc.settings.texte | split: '
' -%}
{%- for ligne in lignes -%}<li>{{ ligne }}</li>{%- endfor -%}
</ul>
{%- else -%}
{{ bloc.settings.texte | lignes }}
{%- endif -%}
{%- endfor -%}
</div>Le {%- else -%} final n’est pas décoratif : c’est lui qui rend le
paragraphe, le type le plus courant, et qui rattrape un type disparu du
schéma que des pages utilisent encore. Un {% case %} sans {% else %}
laisserait ces blocs sans aucune sortie.
Noter aussi le split sur un saut de ligne littéral : Liquid n’interprète
pas \n dans une chaîne, il faut donc écrire la coupure de ligne dans le
code.
#Mettre un bloc en avant
{%- for bloc in section.blocks -%}
<article class="forfait{% if bloc.settings.mis_en_avant %} forfait-phare{% endif %}">
{%- if bloc.settings.mis_en_avant -%}
<span class="forfait-etiquette">{{ bloc.settings.etiquette }}</span>
{%- endif -%}
<h3>{{ bloc.settings.nom }}</h3>
<p class="forfait-prix">{{ bloc.settings.prix }}</p>
</article>
{%- endfor -%}Un checkbox par bloc, et c’est le client qui décide quel forfait est mis en
avant — pas l’ordre, pas une position codée en dur.
#Les variables de boucle de Liquid
forloop est disponible et sert plus souvent qu’on ne croit. Les quatre
thèmes numérotent leurs étapes avec, plutôt que de demander le numéro dans un
réglage :
<ol class="etapes-liste etapes-{{ section.settings.sens | default: 'vertical' }}">
{%- for bloc in section.blocks -%}
<li>
<span class="etape-rang">{{ forloop.index }}</span>
<div>
<h3>{{ bloc.settings.titre }}</h3>
{%- if bloc.settings.texte != blank -%}<p>{{ bloc.settings.texte }}</p>{%- endif -%}
</div>
</li>
{%- endfor -%}
</ol>Insérer une étape au milieu ne demande alors aucune renumérotation. Même principe pour ouvrir la première question d’une FAQ :
<details{% if forloop.first and section.settings.premier_ouvert %} open{% endif %}>forloop.index compte à partir de 1, forloop.index0 à partir de 0,
forloop.first et forloop.last bornent, forloop.length donne le total.
Voir Tags.
#Le piège : les blocs n’héritent pas de leurs valeurs par défaut
Les default déclarés dans le schéma d’un bloc ne sont jamais appliqués.
La fusion des défauts ne porte que sur les réglages de la section. Un bloc
dont le template ne renseigne pas un champ rend du vide, même si le schéma
annonce une valeur par défaut.
Concrètement, ce template :
"blocks": {
"b1": { "type": "prestation", "settings": { "nom": "Dépannage" } }
}avec ce schéma :
{ "type": "text", "id": "prix", "label": "Prix", "default": "sur devis" }rend un prix vide, pas « sur devis ». Deux conséquences pratiques :
- écris toujours tous les réglages d’un bloc dans le template, y compris ceux qui reprennent le défaut. Les templates des quatre thèmes le font ;
- ne compte pas sur un
defaultde bloc pour rattraper une page ancienne. Ajouter un réglage à un type de bloc laisse toutes les instances existantes à vide.
Le filtre default de Liquid, lui, fonctionne, et c’est le rattrapage à
connaître :
<span class="prix">{{ bloc.settings.prix | default: 'sur devis' }}</span>#Les blocs d’une section de coquille
L’en-tête et le pied sont posés par {% section %}, pas par un template.
Leurs blocs vivent donc dans config/settings_data.json — et la forme n’est
pas la même.
Pour une section de coquille, blocks est passé au rendu tel quel. Ce
doit donc être un tableau d’objets, et non l’objet indexé par identifiant
des templates. Il n’y a pas de block_order : l’ordre du tableau fait foi.
{
"current": {
"sections": {
"pied": {
"settings": { "mentions": true },
"blocks": [
{ "type": "reseau", "settings": { "reseau": "Instagram", "href": "https://instagram.com/atelier" } },
{ "type": "reseau", "settings": { "reseau": "Facebook", "href": "https://facebook.com/atelier" } }
]
}
}
}
}La lecture, elle, ne change pas :
<div class="enveloppe pied-bas">
<span>© {{ annee }} {{ site.nom }}</span>
{%- for bloc in section.blocks -%}
<a href="{{ bloc.settings.href }}" rel="noreferrer">{{ bloc.settings.reseau }}</a>
{%- endfor -%}
</div>Écrire un objet indexé à cet endroit ne lève aucune erreur : la boucle parcourt alors les valeurs de l’objet dans un ordre non garanti, ou ne rend rien du tout. C’est un cas qu’aucun des quatre thèmes n’exerce — traite-le avec méfiance.
#Une section désactivée
"disabled": true sur une section d’un template la retire du rendu sans
la supprimer. Pratique pour masquer une bande le temps d’une saison, en
gardant son contenu.
{
"sections": {
"promo": { "type": "annonce", "disabled": true, "settings": { "texte": "Soldes" } }
},
"order": ["promo"]
}Il n’y a pas d’équivalent au niveau du bloc : pour retirer un bloc, on
l’ôte de block_order, ce qui le laisse en place dans blocks et le rend
récupérable.

