#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
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.jsonExactement 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
| Dossier | Extensions | Contenu | Requis |
|---|---|---|---|
layout/ | .liquid | La coquille HTML | theme.liquid |
sections/ | .liquid | Les blocs de page, avec leur schéma | — |
snippets/ | .liquid | Les fragments appelés par {% render %} | — |
assets/ | .css, .svg | Feuilles de style et images vectorielles | — |
config/ | .json | Réglages globaux, métadonnées | settings_schema.json |
templates/ | .json | La composition de chaque page | index.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.
<!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.
{%- 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.
{%- if label != blank -%}
<a class="bouton{% if style == 'secondaire' %} bouton-secondaire{% endif %}"
href="{{ href | default: '/contact' }}">{{ label }}</a>
{%- endif -%}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.
{{ 'theme.css' | asset_url | stylesheet_tag }}{{ '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.jsontableaurequisLes 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.jsonobjetLes 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.jsonobjetNom, 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.
{
"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.
{
"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 :
| Refus | Déclenché par | Message |
|---|---|---|
traversee | .., / initial, \ | « Ce chemin sort du thème. » |
hors_dossier | plus ou moins de deux segments, dossier inconnu | « Ce fichier n’est dans aucun dossier connu. » |
nom | caractère hors [a-z0-9._-], ou premier caractère non alphanumérique | « Ce nom de fichier contient des caractères qui ne passent pas. » |
extension | extension non admise dans ce dossier | « Ce type de fichier n’est pas accepté dans ce dossier. » |
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 : 120Nombre 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 : 524288Longueur 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.

