Aller au contenu

#Arborescence

Un thème a exactement six dossiers, et un fichier est toujours à <dossier>/<fichier> — jamais plus profond, jamais à la racine. Cette page dit ce qu’on met dans chacun, quelles extensions y sont admises, et pourquoi cette liste est fermée.

#Le plan complet

themes/origo/
layout/
  theme.liquid              la coquille HTML de toutes les pages
sections/
  entete.liquid             une section, avec son {% schema %}
  hero.liquid
  prestations.liquid
  pied.liquid
snippets/
  bouton.liquid             un fragment réutilisable
  image.liquid
  section-tete.liquid
assets/
  theme.css                 la feuille de style
config/
  theme.json                nom, version, métiers visés — catalogue seulement
  settings_schema.json      les réglages globaux, déclarés
  settings_data.json        leurs valeurs pour CE site
templates/
  index.json                la composition de la page d'accueil
  page.json                 le gabarit de repli des pages libres
  contact.json
  tarifs.json
Attention

Exactement deux segments. sections/hero.liquid est valide, sections/accueil/hero.liquid ne l’est pas, theme.liquid à la racine non plus. Un chemin qui n’a pas deux segments est refusé à l’entrée avec hors_dossier : « Ce fichier n’est dans aucun dossier connu. »

#Le tableau récapitulatif

DossierExtensionsContenuRequis
layout/.liquidLa coquille HTMLtheme.liquid
sections/.liquidLes blocs de page, avec leur schéma
snippets/.liquidLes fragments appelés par {% render %}
assets/.css, .svgFeuilles de style et images vectorielles
config/.jsonRéglages globaux, métadonnéessettings_schema.json
templates/.jsonLa composition de chaque pageindex.json

#layout/ — la coquille

Un seul fichier compte : layout/theme.liquid. C’est le HTML qui entoure toutes les pages du site, et il doit contenir {{ content_for_layout }}, sinon le contenu des sections n’est écrit nulle part.

themes/origo/layout/theme.liquidliquid
<!doctype html>
<html lang="fr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ page.titre }} — {{ site.nom }}</title>
    {{ 'theme.css' | asset_url | stylesheet_tag }}
  </head>
  <body>
    {% section 'entete' %}
    <main id="contenu">{{ content_for_layout }}</main>
    {% section 'pied' %}
  </body>
</html>

Rien n’interdit d’y déposer d’autres .liquid, mais rien ne les lira : le moteur ne connaît que layout/theme.liquid. Tout est disséqué dans La coquille.

#sections/ — les blocs de page

Un fichier par type de section. Son nom est son identifiant : un template qui écrit "type": "prestations" fait chercher sections/prestations.liquid. Le fichier contient du balisage, et à la fin un {% schema %} qui déclare ce qui est modifiable.

themes/forge/sections/annonce.liquidliquid
{%- if section.settings.texte != blank -%}
  <div class="annonce">{{ section.settings.texte }}</div>
{%- endif -%}

{% schema %}
{
  "name": "Barre d'annonce",
  "group": "header",
  "settings": [
    {
      "type": "text",
      "id": "texte",
      "label": "Message",
      "default": "Devis gratuit sous 48 h — déplacement compris",
      "info": "Laisse vide pour masquer la barre."
    }
  ]
}
{% endschema %}

Le thème origo en compte trente-quatre, du bandeau d’accueil aux horaires d’ouverture. Voir Écrire une section.

#snippets/ — les fragments

Des morceaux de balisage appelés par {% render 'nom' %}. Le dossier et l’extension sont facultatifs à l’appel : {% render 'bouton' %} résout snippets/bouton.liquid.

themes/origo/snippets/bouton.liquidliquid
{%- if label != blank -%}
  <a class="bouton{% if style == 'secondaire' %} bouton-secondaire{% endif %}"
     href="{{ href | default: '/contact' }}">{{ label }}</a>
{%- endif -%}
Attention

Un snippet appelé par {% render %} ne voit que ce qu’on lui passe — ni site, ni settings, ni section, ni même theme_base. C’est le piège numéro un du dossier, et il est expliqué en détail dans Snippets.

#assets/ — CSS et SVG, rien d’autre

Deux extensions : .css et .svg. L’adresse d’un fichier s’obtient uniquement par le filtre asset_url, qui prend le nom seul, jamais le chemin.

Dans la coquilleliquid
{{ 'theme.css' | asset_url | stylesheet_tag }}
Danger

{{ 'assets/theme.css' | asset_url }} fabrique une adresse à deux segments qui ne correspond à aucune route : la feuille de style répond 404, et la page rend quand même — en HTML nu. C’est le bug qui a tenu sur tous les sites à la fois sans jamais lever d’erreur. Le nom seul, toujours.

Les quatre thèmes d’origine n’ont chacun qu’un seul fichier ici, theme.css, entre 11 et 23 Ko. Voir Assets.

#config/ — les réglages et les métadonnées

Trois fichiers, dont un seul est requis.

config/settings_schema.jsontableaurequis

Les réglages globaux déclarés, groupés par section de panneau. C’est la liste de ce qui est réglable, pas les valeurs.

config/settings_data.jsonobjet

Les valeurs de ces réglages pour ce site, sous la clé current. C’est ce fichier qui alimente settings dans les templates, et current.sections donne les valeurs des sections posées par la coquille.

config/theme.jsonobjet

Nom, version, métiers visés, points forts. Lu uniquement par scripts/compiler-themes.mjs au moment de compiler le catalogue : sur un thème installé, il ne sert plus à rien. Il voyage quand même avec le thème, puisque config/*.json est admis.

themes/piazza/config/settings_data.jsonjson
{
  "current": {
    "encre": "#2b1b12",
    "papier": "#ffffff",
    "accent": "#b45309",
    "largeur": 1160,
    "sections": {
      "entete": { "settings": { "horaires": "Ouvert 7 h – 19 h 30" } }
    }
  }
}

Rien n’interdit d’autres .json dans config/ : ils seront stockés, servis au CLI, et ignorés par le rendu. Voir Réglages globaux.

#templates/ — la composition des pages

Un fichier par gabarit. index.json est requis ; page.json est fortement conseillé, parce qu’il est le repli de toute page sans gabarit propre. Le nom du fichier correspond au gabarit demandé par la page du CMS.

themes/origo/templates/page.jsonjson
{
  "sections": {
    "tete": {
      "type": "hero-sobre",
      "settings": { "surtitre": "", "titre": "", "texte": "" }
    },
    "corps": {
      "type": "texte",
      "blocks": {
        "b1": { "type": "paragraphe", "settings": { "texte": "…" } }
      },
      "block_order": ["b1"]
    }
  },
  "order": ["tete", "corps"]
}

L’ordre de résolution — trois niveaux — est le sujet de Les templates.

#La liste des extensions est fermée, et c’est de la sécurité

Ce n’est pas une convention de rangement. Un thème importé est du contenu qui vient de l’extérieur, et il s’exécutera sous le domaine du client, avec ses cookies. Sans cette liste, une archive pourrait déposer un .js dans assets/ : du script servi à tous les visiteurs du site, sous son origine.

Le contrôle vit dans une seule fonction, refusDuChemin, appelée à l’installation, à l’import et à chaque écriture de fichier par l’API. Quatre refus possibles :

RefusDéclenché parMessage
traversee.., / initial, \« Ce chemin sort du thème. »
hors_dossierplus ou moins de deux segments, dossier inconnu« Ce fichier n’est dans aucun dossier connu. »
nomcaractère hors [a-z0-9._-], ou premier caractère non alphanumérique« Ce nom de fichier contient des caractères qui ne passent pas. »
extensionextension non admise dans ce dossier« Ce type de fichier n’est pas accepté dans ce dossier. »
Danger

La traversée de répertoire est vérifiée à l’entrée, pas au moment d’écrire. C’est la faille classique des archives — celle qui permet d’écrire ailleurs que là où on croit — et il ne doit y avoir qu’un seul endroit à relire pour s’en assurer.

Un nom de fichier doit donc commencer par une lettre ou un chiffre, et ne contenir ensuite que des lettres, des chiffres, des points, des tirets et des tirets bas. Mon Fichier.liquid est refusé ; mon-fichier.liquid passe.

#Les deux plafonds

FICHIERS_MAXentierdéfaut : 120

Nombre de fichiers d’un thème. Vérifié à l’installation, à l’import, et à la création d’un fichier par l’API — jamais à la modification, pour qu’un thème déjà au plafond reste corrigeable.

TAILLE_MAXcaractèresdéfaut : 524288

Longueur du contenu d’un fichier. Comparée à la longueur de la chaîne, donc en unités de code UTF-16 et non en octets : un fichier plein d’accents passe la barre un peu plus tard qu’un ls -l ne le laisse croire.

Pour l’ensemble des bornes — temps de rendu, mémoire, imbrication — voir Le bac à sable.

#Pages voisines