#Écrire une section
Une section est un fichier de sections/ : du balisage, et à la fin un
{% schema %} qui déclare ce qui est modifiable. C’est l’unité dont on
compose une page — un bandeau d’accueil, une grille de prestations, un
formulaire de contact — et c’est là que se passe l’essentiel du travail d’un
thème.
Cette page montre comment on en écrit une, ce qu’elle reçoit au rendu, et les deux manières dont elle peut être appelée.
#Un exemple complet
Celui-ci est réel : c’est themes/origo/sections/prestations.liquid, réduit
de moitié pour tenir sur un écran, mais rien n’y a été inventé.
<section class="section section-{{ section.settings.fond | default: 'papier' }} prestations" id="prestations">
<div class="enveloppe">
<div class="tete">
{%- if section.settings.surtitre != blank -%}
<span class="surtitre">{{ section.settings.surtitre }}</span>
{%- endif -%}
{%- if section.settings.titre != blank -%}
<h2 class="titre titre-section">{{ section.settings.titre }}</h2>
{%- endif -%}
</div>
<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>
</div>
</section>
{% schema %}
{
"name": "Prestations",
"settings": [
{ "type": "text", "id": "surtitre", "label": "Surtitre", "default": "Ce qu'on fait" },
{ "type": "text", "id": "titre", "label": "Titre", "default": "Nos prestations" },
{
"type": "select",
"id": "colonnes",
"label": "Colonnes",
"options": [
{ "value": "2", "label": "Deux" },
{ "value": "3", "label": "Trois" },
{ "value": "4", "label": "Quatre" }
],
"default": "3"
}
],
"blocks": [
{
"type": "prestation",
"name": "Prestation",
"settings": [
{ "type": "text", "id": "nom", "label": "Nom", "default": "Une prestation" },
{ "type": "textarea", "id": "description", "label": "Description" },
{ "type": "text", "id": "prix", "label": "Prix", "default": "sur devis" }
]
}
],
"max_blocks": 12
}
{% endschema %}#L’anatomie du fichier
Le nom du fichier est l’identifiant de la section
sections/prestations.liquidrépond au"type": "prestations"d’un template, et à{% section 'prestations' %}dans la coquille. Il n’y a pas d’autre déclaration à faire : le fichier suffit.Le balisage, en premier
Du HTML, avec des expressions Liquid dedans. Tout ce qui précède le
{% schema %}est rendu ; le blocschemalui-même est retiré avant le rendu.Le schéma, à la fin
Du JSON pur, entre
{% schema %}et{% endschema %}. Par convention il est posé en dernier, et c’est ainsi que Shopify le lit aussi. Voir Le bloc schema.
#Ce que la section reçoit
Au rendu, une section voit le contexte complet du site — site, page,
settings, menu, articles, annee — plus un objet section qui n’existe
que pour elle.
section.idchaîneL’identifiant de l'instance. Pour une section de template, c’est la clé
sous laquelle elle est déclarée ("tete", "corps"). Pour une section de
coquille, c’est son nom. Utile pour fabriquer un id HTML unique quand la
même section est posée deux fois.
section.typechaîneLe nom du fichier, sans sections/ ni .liquid.
section.settingsobjetLes réglages de cette instance : les default du schéma, recouverts par les
valeurs enregistrées. Voir Les types de réglage.
section.blockstableauLes blocs, dans l’ordre. Chaque entrée porte id, type et settings. Voir
Les blocs.
Une section ne voit pas les autres sections de la page, ni le HTML déjà
rendu. Elles sont rendues en parallèle et concaténées ensuite : rien ne
circule de l’une à l’autre, pas même par {% assign %}.
#Les deux façons d’appeler une section
#Depuis un template — une instance par entrée
C’est le cas courant. Le template déclare l’instance, ses réglages et ses blocs :
{
"sections": {
"nos-services": {
"type": "prestations",
"settings": { "titre": "Nos services", "colonnes": "2" },
"blocks": {
"b1": { "type": "prestation", "settings": { "nom": "Dépannage" } },
"b2": { "type": "prestation", "settings": { "nom": "Installation" } }
},
"block_order": ["b1", "b2"]
}
},
"order": ["nos-services"]
}La même section peut être posée plusieurs fois sur une page, sous deux clés différentes, avec des réglages différents. Voir Les templates.
#Depuis la coquille — le groupe header et footer
L’en-tête, le pied et la barre d’annonce ne sont dans aucun template : ils
doivent apparaître sur toutes les pages. C’est la coquille qui les pose,
avec {% section 'nom' %}, et leurs valeurs vivent dans
config/settings_data.json sous current.sections.
{% section 'annonce' %}
{% section 'entete' %}
<main id="contenu">{{ content_for_layout }}</main>
{% section 'pied' %}Ces sections portent par convention "group": "header" ou "footer" dans
leur schéma :
{% schema %}
{
"name": "En-tête",
"group": "header",
"settings": [
{ "type": "text", "id": "marque", "label": "Nom affiché", "info": "Vide : le nom du site est utilisé." },
{ "type": "text", "id": "horaires", "label": "Horaires du jour", "default": "Ouvert 7 h – 19 h 30" }
]
}
{% endschema %}group est aujourd’hui purement documentaire : le moteur ne le lit pas.
Ce qui décide qu’une section est posée par la coquille, c’est le
{% section %} du layout, et rien d’autre. Écris-le quand même — il dit à qui
relira le thème où cette section apparaît, et il sera lu le jour où un
panneau de réglages existera.
#Les motifs qu’on retrouve dans les thèmes d’origine
#Masquer un élément vide plutôt que d’afficher un trou
{%- if section.settings.texte != blank -%}
<p class="chapeau">{{ section.settings.texte }}</p>
{%- endif -%}blank couvre à la fois la chaîne vide et la variable absente. C’est ce qui
permet à un client de vider un champ pour faire disparaître un élément, sans
laisser une balise vide qui décale la mise en page.
#Une classe pilotée par un select
<section class="section section-{{ section.settings.fond | default: 'papier' }}">Le réglage vaut papier, voile, encre ou accent ; la feuille de style
définit les quatre classes. Le default: protège du cas où le réglage n’a
jamais été renseigné et n’a pas de default dans le schéma.
#Les tirets qui mangent les espaces
{%- if … -%} supprime les blancs autour du tag. Sans eux, une boucle sur
douze blocs sème douze lignes vides dans le HTML rendu. Ce n’est pas
cosmétique dans un <span> : un blanc de trop dedans se voit à l’écran.
#Réagir à l’envoi d’un formulaire
{%- if page.demande == 'merci' -%}
<p class="message message-ok" role="status">
Merci, votre message est bien arrivé.
</p>
{%- elsif page.demande == 'erreur' -%}
<p class="message message-ko" role="alert">
Le message n'a pas pu être envoyé.
</p>
{%- endif -%}page.demande est renseigné après un envoi de formulaire. C’est ce qui permet
de remercier sans une ligne de JavaScript : le formulaire poste vers
/api/contacts, la route redirige, la page se rend à nouveau.
#Boucler sur les articles du CMS
{%- if articles.size > 0 -%}
{%- for article in articles limit: section.settings.nombre -%}
<h3><a href="{{ article.adresse }}">{{ article.titre }}</a></h3>
<span class="date">{{ article.date | date_fr }}</span>
{%- endfor -%}
{%- endif -%}articles vient du CMS, pas du thème. Un range dans le schéma décide
combien on en affiche. Voir Objets.
#Ce qui se passe quand une section échoue
Rien ne tombe en page blanche. La section fautive est remplacée par un commentaire HTML, et le reste de la page rend normalement.
<!-- section inconnue : bandeau-promo -->
<!-- section prestations : tag "for" not closed, line:14, col:1 -->
<!-- section absente : entete -->| Commentaire | Cause |
|---|---|
section inconnue : <type> | Le template demande un type dont le fichier n’existe pas. |
section absente : <nom> | {% section 'nom' %} sans fichier correspondant. |
section <type> : <message> | La section existe mais son rendu a levé une erreur. |
Ces commentaires sont le premier endroit à regarder quand un bloc de page a
disparu sans explication. Ouvre le code source de la page — pas
l’inspecteur, qui ne montre pas toujours les commentaires — et cherche
<!-- section.
#Les erreurs fréquentes
Un id de réglage qui ne correspond pas{{ section.settings.titre }} avec un schéma qui déclare "id": "title"
rend du vide, sans erreur. C’est de loin la cause numéro un d’un « réglage qui
ne marche pas ».
Un JSON de schéma invalideL’API refuse désormais de l’enregistrer (voir plus bas). Un thème importé
avant le 30 septembre 2026 peut encore en contenir un : la section reste
rendable, mais sans aucun réglage — plus de valeurs par défaut,
section.settings ne contient que ce que le template a enregistré. Voir
Le bloc schema.
Deux blocs schema dans le même fichierSeul le premier est lu, et lui seul est retiré du rendu. Le second sera rendu
tel quel et fera échouer la section avec tag "schema" not found.
Un section.settings lu dans un snippet{% render 'ma-carte' %} n’hérite de rien : le snippet ne voit ni
section, ni settings, ni site. Passe ce dont il a besoin en arguments.
Voir Snippets.
#La vérification à l’enregistrement
L’API refuse d’écrire un .liquid dont la syntaxe ne s’analyse pas, en disant
où et pourquoi — fichier, ligne, colonne. Le contrôle porte sur la syntaxe,
pas sur le sens : {{ nawak }} passe, comme chez Shopify, et rend du vide.
node scripts/theme.mjs pousser✗ sections/hero.liquid — Liquid invalide — ligne 14, colonne 1 : la balise « for » n'est jamais refermée — il manque {% endfor %}.Le {% schema %} est analysé à part : son JSON doit être valide, sans quoi
l’enregistrement est refusé — avec la ligne du fichier, pas celle du bloc.
Le reste du fichier est analysé comme au rendu, le schéma mis de côté.
#Les sections écrites par Cosa
Depuis l’éditeur de thème, « Générer » fait écrire par Cosa une section que le thème n’a pas — un carrousel, une frise, un compteur. Elle arrive dans le thème ouvert comme un fichier ordinaire, que tu peux lire et modifier comme les autres :
- son nom commence par
genere-:sections/genere-carrousel-des-creations.liquid; - il s’ouvre sur un commentaire de provenance — la date, la demande, les retouches. Il ne rend rien chez le visiteur ; c’est lui que l’éditeur lit pour afficher la carte « Créée par Cosa ». Le retirer en fait une section comme une autre ;
- sa racine porte
id="cosa-{{ section.id }}", et toutes ses règles CSS commencent par ce sélecteur : elle ne touche rien hors d’elle-même ; - l’élément racine de chaque bloc porte
{{ bloc.attributs }}— l’équivalent deblock.shopify_attributes, vide sur le site publié, qui rend le bloc sélectionnable dans l’éditeur ; - elle n’embarque aucun JavaScript : carrousels, défilements et compteurs
sont écrits en CSS (
scroll-snap,@keyframes,animation-timeline,@property), sousprefers-reduced-motion.
Avant d’être ajoutée, elle passe un contrôle plus strict que l’enregistrement
ordinaire — schéma complet, réglages tous lus, aucune adresse externe, rendu
contre le thème — et une section qui échoue n’est pas écrite. Elle n’est posée
sur aucune page tant que la personne n’a pas enregistré : un fichier
genere-* qu’aucun template ne nomme ne se rend nulle part.

