Aller au contenu

#É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é.

sections/prestations.liquidliquid
<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

  1. Le nom du fichier est l’identifiant de la section

    sections/prestations.liquid ré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.

  2. Le balisage, en premier

    Du HTML, avec des expressions Liquid dedans. Tout ce qui précède le {% schema %} est rendu ; le bloc schema lui-même est retiré avant le rendu.

  3. 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îne

L’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îne

Le nom du fichier, sans sections/ ni .liquid.

section.settingsobjet

Les réglages de cette instance : les default du schéma, recouverts par les valeurs enregistrées. Voir Les types de réglage.

section.blockstableau

Les blocs, dans l’ordre. Chaque entrée porte id, type et settings. Voir Les blocs.

Attention

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 :

templates/index.jsonjson
{
  "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.

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.

layout/theme.liquidliquid
{% 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 :

themes/piazza/sections/entete.liquidliquid
{% 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 %}
Note

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

liquid
{%- 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

liquid
<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

liquid
{%- 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

liquid
{%- 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.

Code source de la page renduehtml
<!-- section inconnue : bandeau-promo -->
<!-- section prestations : tag "for" not closed, line:14, col:1 -->
<!-- section absente : entete -->
CommentaireCause
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.
Astuce

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 invalide

L’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 fichier

Seul 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 de block.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), sous prefers-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.

#Pages voisines