Aller au contenu

#Objets

Voici tout ce qu’un thème peut lire. La liste est complète : ce qui n’y figure pas n’existe pas, et rendra une chaîne vide sans prévenir. Chaque champ est donné avec son type, un usage tiré d’un thème livré, et la valeur que le serveur rend réellement.

La source qui fait foi est src/lib/liquid/contexte.ts, complétée par le code qui la remplit — src/lib/liquid/rendu-site.ts et le rendu public du site. Les valeurs ci-dessous en sortent, pas d’une intention.

#La table des matières du contexte

ObjetFormeDisponible
siteobjetpartout
pageobjetpartout
settingsobjet librepartout
menutableaupartout
articlestableaupartout
anneeentierpartout
theme_basechaînepartout
sectionobjetdans une section seulement
content_for_layoutchaîne de HTMLdans layout/theme.liquid seulement
Attention

Un snippet appelé par render ne voit rien de tout cela. Le tag isole complètement la portée : seuls les paramètres passés explicitement entrent. Voir les tags — c’est la surprise la plus fréquente, et de loin.

#site

L’entreprise à qui appartient le site. Deux champs, et le second n’est pas ce qu’on croit.

site.nomchaîne

Le nom du site tel qu’il est saisi dans le CMS. C’est ce qu’on met dans la balise title, dans le logo textuel de l’en-tête et dans la mention de copyright.

site.adressechaîne

Le préfixe des liens internes, pas une adresse absolue. En production il est vide : le site est servi sous son propre nom de domaine, et un lien interne n’a besoin d’aucun préfixe. Il ne vaut /s/mon-site que sur l’hôte de l’administration, c’est-à-dire en développement, où les sous-domaines ne résolvent pas.

C’est pour cela que les thèmes livrés écrivent {{ site.adresse }}/ et non {{ site.adresse }} : sans la barre oblique finale, le lien vers l’accueil serait une chaîne vide en production.

sections/entete.liquid — Origoliquid
<a class="marque" href="{{ site.adresse }}/">{{ site.nom }}</a>
Danger

N’écris jamais <a href="{{ site.adresse }}"> seul, ni {{ site.adresse }}/contact en croyant fabriquer une URL absolue. Le premier produit href="" en production — le navigateur recharge la page courante ; le second produit /contact, ce qui se trouve être juste, mais par accident. Pour un lien interne, écris simplement /contact.

#page

La page en cours de rendu.

page.titrechaîne

Le titre saisi dans le CMS. Il sert dans la balise title de la coquille, et les thèmes livrés le reprennent aussi comme champ caché provenance du formulaire de contact, pour savoir depuis quelle page la demande a été envoyée.

page.adressechaîne

/ pour l’accueil, /le-slug pour les autres. Sans préfixe : contrairement à site.adresse et aux entrées de menu, ce champ n’est pas préfixé sur l’hôte d’administration. Il sert à savoir où l’on est, pas à fabriquer un lien.

page.accueilbooléen

Vrai sur la page d’accueil. Utile pour une coquille qui met un h1 sur l’accueil et un h2 ailleurs, ou pour n’afficher une barre d’annonce que sur la première page.

page.demandechaîne

"merci", "erreur", ou vide. C’est l’état du dernier envoi de formulaire, lu depuis ?demande= dans l’adresse.

Ce champ mérite qu’on s’y arrête, parce qu’il porte à lui seul tout le retour utilisateur d’un formulaire de contact. Le formulaire poste vers /api/contacts, la route enregistre la demande puis redirige vers la page avec ?demande=merci (ou ?demande=erreur), et la page se rend à nouveau. La section Contact peut donc remercier sans une ligne de JavaScript — ce qui tombe bien, puisque assets/ n’accepte pas de .js.

sections/contact.liquid — Origoliquid
{%- if page.demande == 'merci' -%}
  <p class="message message-ok" role="status">
    Merci, votre message est bien arrivé. Nous vous répondons vite.
  </p>
{%- elsif page.demande == 'erreur' -%}
  <p class="message message-ko" role="alert">
    Le message n'a pas pu être envoyé. Vérifiez votre e-mail ou votre
    téléphone, puis réessayez.
  </p>
{%- endif -%}

<form class="formulaire" method="post" action="/api/contacts">

  <input type="hidden" name="provenance" value="{{ page.titre }}">
</form>
Astuce

role="status" et role="alert" ne sont pas décoratifs : ils font annoncer le message par un lecteur d’écran au moment où il apparaît. Un message de confirmation qu’on ne peut pas entendre n’est pas un message de confirmation.

#settings

Les réglages globaux du thème — couleurs, largeur, typographie. Ils sont lus dans config/settings_data.json, sous la clé current, et l’accès se fait par identifiant : {{ settings.accent }} pour le réglage dont l’id est accent.

config/settings_data.json — Origojson
{
  "current": {
    "encre": "#15171c",
    "papier": "#ffffff",
    "accent": "#4364df",
    "largeur": 1160,
    "boutons": "pilule",
    "sections": {
      "entete": { "settings": { "cta_label": "Nous contacter" } }
    }
  }
}

La clé sections est retirée de settings avant le rendu : elle ne contient pas des réglages globaux mais ceux des sections posées par la coquille. {{ settings.sections }} rend donc du vide, et c’est voulu.

Danger

Les valeurs par défaut déclarées dans config/settings_schema.json ne sont pas fusionnées dans settings. Un réglage global absent de settings_data.json rend du vide, même s’il a un "default" dans le schéma — contrairement aux réglages de section, où la fusion a bien lieu.

Concrètement : ajouter un réglage global à un thème demande de l’ajouter aux deux fichiers. Sinon --accent: ; part dans la feuille de style, la déclaration est invalide, et toute la page perd sa couleur d’accent sans qu’une seule erreur soit levée. Voir les réglages globaux.

#section

L’objet de la section en cours. Il n’existe que pendant le rendu d’un fichier de sections/ — ni dans la coquille, ni dans un snippet appelé par render.

section.idchaîne

L’identifiant de l’instance. Pour une section posée par un template, c’est la clé qu’elle porte dans templates/*.json ; pour une section posée par la coquille avec section, c’est son nom de fichier. Deux instances de la même section dans une page ont le même type et des id différents — c’est ce qui permet de fabriquer une ancre unique.

section.typechaîne

Le nom du fichier, sans son extension : sections/prestations.liquid donne prestations.

section.settingsobjet

Les réglages de cette instance, accessibles par leur id. Ils sont fusionnés avec les valeurs "default" du {% schema %} : ce qui n’a pas été renseigné prend le défaut. Sans cette fusion, ajouter un réglage à un thème déjà installé afficherait du vide partout, et l’auteur croirait son défaut ignoré.

section.blockstableau

Les blocs répétables de la section, dans l’ordre défini par block_order. Vide si la section n’en déclare pas. Chaque bloc porte id, type et settings.

sections/etapes.liquid — Forgeliquid
<ol class="etapes-liste">
  {%- 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>

forloop.index vient de Liquid, pas de Webcosa : dans une boucle for, l’objet forloop donne index (à partir de 1), index0, first, last, length et rindex. Numéroter les étapes en CSS aurait été possible ; le faire ici permet au numéro d’être stylé comme un élément à part entière.

Quand une section porte plusieurs types de blocs, on teste bloc.type :

Plusieurs types de blocsliquid
{%- for bloc in section.blocks -%}
  {%- if bloc.type == 'image' -%}
    <img src="{{ bloc.settings.fichier }}" alt="{{ bloc.settings.legende | escape }}">
  {%- else -%}
    <p>{{ bloc.settings.texte }}</p>
  {%- endif -%}
{%- endfor -%}

Le détail de la déclaration est sur la page des blocs.

Le menu principal du site, tel qu’il est composé dans le CMS. C’est un tableau d’objets à deux champs, et rien d’autre : ni sous-menus, ni état actif, ni identifiant.

menu[].labelchaîne

Le texte du lien.

menu[].adressechaîne

L’adresse. Les liens internes sont déjà préfixés — contrairement à page.adresse — donc on les écrit tels quels. Les liens externes sont laissés intacts.

sections/entete.liquid — Origoliquid
<nav class="menu" aria-label="Menu principal">
  {%- for lien in menu -%}
    <a href="{{ lien.adresse }}">{{ lien.label }}</a>
  {%- endfor -%}
</nav>
Note

Pour marquer l’entrée courante, compare avec page.adresse — mais souviens-toi que menu[].adresse est préfixé et page.adresse non. En production les deux préfixes sont vides et la comparaison directe fonctionne ; sur l’hôte d’administration, elle échoue silencieusement. Un thème qui en dépend paraîtra cassé en développement seulement.

#articles

Les derniers articles publiés du site, tous blogs confondus, du plus récent au plus ancien. Le tableau est plafonné à 12 entrées, quelle que soit la valeur demandée par la section.

articles[].titrechaîne

Le titre de l’article.

articles[].extraitchaîne

Le chapeau saisi dans le CMS. Vide s’il n’y en a pas — d’où le test != blank avant de rendre le paragraphe.

articles[].adressechaîne

L’adresse de l’article, de la forme /blog/<blog>/<slug>, déjà préfixée.

articles[].imagechaîne

L’adresse de l’image de couverture, vide s’il n’y en a pas. C’est une URL complète servie par la plateforme, pas un fichier d’assets/ : ne lui applique pas asset_url.

articles[].datechaîne

La date de publication, destinée au filtre date_fr.

Ce champ est actuellement toujours vide. Le rendu public le remplit avec une chaîne vide au lieu de la date de publication lue en base. La conséquence est discrète : {{ article.date | date_fr }} rend du vide, donc la ligne de date d’une carte d’article est simplement absente. Le champ existe, son contrat tient, sa valeur ne suit pas encore. Écris ton thème comme s’il était rempli — le jour où il le sera, ton thème l’affichera sans modification.

sections/journal.liquid — Origoliquid
{%- if articles.size > 0 -%}
  <div class="grille grille-3">
    {%- for article in articles limit: section.settings.nombre -%}
      <article class="carte">
        {%- if article.image != blank -%}
          <a href="{{ article.adresse }}"><img src="{{ article.image }}" alt="" loading="lazy"></a>
        {%- endif -%}
        <h3><a href="{{ article.adresse }}">{{ article.titre }}</a></h3>
        {%- if article.extrait != blank -%}<p>{{ article.extrait }}</p>{%- endif -%}
        <span class="date">{{ article.date | date_fr }}</span>
      </article>
    {%- endfor -%}
  </div>
{%- endif -%}

Trois habitudes à reprendre de cet extrait. Le if articles.size > 0 évite un titre de section suivi du vide sur un site sans blog. Le limit: de la boucle for prend sa valeur d’un réglage range, ce qui rend le nombre de cartes réglable sans toucher au code. Et loading="lazy" sur les images n’est pas un détail : une page de douze photos qui les charge toutes d’un coup perd deux secondes sur la note de vitesse.

#annee

L’année en cours, en entier. Elle est calculée au moment du rendu, pas à la compilation du thème.

sections/pied.liquid — Origoliquid
<span>© {{ annee }} {{ site.nom }}</span>

Pourquoi l’exposer alors qu’un thème pourrait s’en passer ? Parce que sans elle, chaque thème écrirait l’année en dur dans son pied de page, et tous les sites afficheraient 2025 en février 2026. Un pied de page périmé est le genre de détail qui fait douter un visiteur de la fraîcheur de tout le reste.

Note

Le calcul a lieu à chaque rendu de page, jamais à la génération d’un cache. Une page qui serait mise en cache pour la nuit du 31 décembre garderait sinon l’année de sa génération. Le rendu public étant dynamique, la valeur est toujours juste.

#theme_base

La base des adresses des fichiers d’assets/. Sa valeur est /theme-assets/<id-du-thème> — un identifiant de thème, pas son nom.

Attention

Ne l’utilise pas directement. Un thème peut techniquement écrire {{ theme_base }}/theme.css et court-circuiter asset_url, mais ce n’est pas une promesse qu’on tient : le jour où la route des assets changera, seul asset_url suivra. Ce champ est documenté parce qu’il est visible, pas parce qu’il est fait pour être écrit.

Écris {{ 'theme.css' | asset_url }}. Voir les filtres.

#content_for_layout

Le HTML des sections de la page, déjà rendu. Il n’existe que dans layout/theme.liquid, et c’est le point d’insertion du contenu : la coquille décide où il tombe et pose autour ce qu’elle veut.

layout/theme.liquid — Piazzaliquid
{% section 'entete' %}

<main id="contenu">{{ content_for_layout }}</main>

{% section 'pied' %}

C’est du HTML déjà assemblé : ne lui applique ni escape, ni lignes, ni aucun filtre de texte — tu afficherais le balisage au visiteur. La coquille est détaillée sur sa propre page.

#Ce qui n’est pas là

Ni product, ni cart, ni collection, ni customer, ni checkout. Ce n’est pas un oubli et ce n’est pas provisoire : Webcosa fait des sites vitrines, et exposer des objets systématiquement vides ferait écrire des thèmes contre une API qui ne rend jamais rien. La page des absents explique le raisonnement en détail et dit quoi faire à la place.